문제

I have a simple debug_print shell function that I use to help write larger shell scripts. In general, I try to write only Bourne-compatible scripts and avoid Bash-isms. In this case, the Bash version of my debug_print function works fine, but I cannot for the life of me figure out why my Bourne-compatible version fails. Specifically, my problems are with using the %s format modifier with printf.

I've included my test script below. The debug_print function displays a header bar with a label, the contents of its string or file argument, and finally a footer bar with the same width as the header. The problems are with displaying the footer bar and begin with the comment "Display a footer line...". The two uncommented lines following that are what I believe should work, but do not.

Following that are several commented lines under the "None of these work" comment where you can see some of the various tests/variances I used while trying to figure out the problem. After those lines are a working Bourne version, and, finally, a working Bash version.

I've read the manpages in painful detail and tried every variety of format string, quoting, and variable use I can think of. So far, none of these attempts is working right. Under the "None of these work" comment, the none working commands all print out the entire string instead of, as I believe they should, only the number of characters specified by the field width modifier.

To determine "Bourne compatibility" I use dash as /bin/sh on my system, which is typical of Debian systems. Dash is meant for efficient script execution and not interactive use. According to the manpage, it strives for strict Bourne compatibility, so I have reasoned that if my script works with dash then it should be reasonably Bourne compatible and free of bash-isms. Also, I recognize that this function is not the be-all/end-all of debug printing and there are likely numerous corner cases and areas where it could be improved. I welcome all suggestions, but I am particularly interested in this printf problem. Given how simple this should be, it seems like I must be making some stupid mistake, but I cannot spot it.

Here is my debug_print test script:

#!/bin/sh
#
# Purpose: Debug print function.  Outputs delimiting lines and will display
#     either the contents of a file or the contents of a variable.
#
#   Usage: debug_print label input
#     Where label is the title to print in the header bar, usually the name of
#     the input variable.  Input is either a file on disk to be printed, or, 
#     if the argument is not a file then it is assumed to be a string and that
#     will be displayed instead.
#
# Returns: If two parameters are not provided, returns false, otherwise it
#     will always return true.
#
debug_print()
{
    local header_bar header_len dashes

    # Check for arguments
    if [ $# -ne 2 ]; then
        printf "Error: debug_print takes two parameters: 'label' and 'input'\n"
        return 1
    fi

    header_bar="-------------------------$1-------------------------"
    header_len=${#header_bar}
    printf "%s\n" "$header_bar"

    # Display either the contents of the input file or the input string
    if [ -r "$2" ]; then
        cat "$2"
    else
        printf "%s\n" "$2"
    fi

    # Display a footer line with the same length as the header line
    dashes="------------------------------------------------------------"
    printf "%*s\n" "$header_len" "$dashes"

    # None of these work
#    printf "%${header_len}s\n" "$dashes"
#    printf "%${header_len}s\n" "------------------------------------------------------------"
#    printf "%5s\n" xxxxxxxxxxxxxxxxxxxx
#    printf '%5s\n' "xxxxxxxxxxxxxxxxxxxx"
#    xxx="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
#    printf '%5s\n' $xxx

    # This works with dash
#    i=$header_len
#    while [ $i -gt 0 ]; do
#        printf "-"
#        i=$(( $i - 1 ))
#    done
#    printf "\n"

    # Working bash version
#    printf -v foot_line '%*s' $header_len
#    echo ${foot_line// /-}

    return 0
}


# Test debug printing
debug_print "Label" "The contents of the debug input..."

Thanks for any help.

도움이 되었습니까?

해결책

Are you writing for POSIX compliancy or for Bourne-shell compliancy, those are different things.. dash strives for strict POSIX compliancy, not Bourne compliancy..

Also, variable field widths:

printf "%*s\n" "$header_len" "$dashes"

are not supported in the POSIX shell standard.

To cut string length you could use a dot and a variable in the format field:

printf "%.${header_len}s\n" "$dashes"

--edit--

No provision is made in this volume of POSIX.1-2008 which allows field widths and precisions to be specified as * since the * can be replaced directly in the format operand using shell variable substitution. Implementations can also provide this feature as an extension if they so choose.

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html#tag_20_94_16

precision Gives the minimum number of digits to appear for the d, o, i, u, x, or X conversion specifiers (the field is padded with leading zeros), the number of digits to appear after the radix character for the e and f conversion specifiers, the maximum number of significant digits for the g conversion specifier; or the maximum number of bytes to be written from a string in the s conversion specifier. The precision shall take the form of a ( '.' ) followed by a decimal digit string; a null digit string is treated as zero.

http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap05.html#tag_05

I did notice one annoying thing and that is that the dot pertains to the number of bytes and not characters, so it is in fact only useable with the standard ASCII character set. So it should work with the dashes (I mean -). There apparently is a bug in the dash man page that mentions characters, but that should be bytes.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top