0

我有一个简单的 debug_print shell 函数,用于帮助编写更大的 shell 脚本。一般来说,我尽量只编写与 Bourne 兼容的脚本并避免使用 Bash 主义。在这种情况下,我的 debug_print 函数的 Bash 版本可以正常工作,但我一生都无法弄清楚为什么我的 Bourne 兼容版本会失败。具体来说,我的问题在于将 %s 格式修饰符与 printf 一起使用。

我在下面包含了我的测试脚本。debug_print 函数显示一个带有标签的标题栏、其字符串或文件参数的内容,最后是一个与标题宽度相同的页脚栏。问题在于显示页脚栏并以注释“显示页脚行...”开头。下面的两条未注释的行是我认为应该起作用的,但不是。

紧随其后的是“这些都不起作用”注释下的几行注释,您可以在其中看到我在尝试找出问题时使用的一些各种测试/方差。在这些行之后是一个工作的 Bourne 版本,最后是一个工作的 Bash 版本。

我已经详细阅读了手册页,并尝试了我能想到的各种格式字符串、引用和变量使用。到目前为止,这些尝试都没有奏效。在“这些都不起作用”注释下,没有工作的命令都打印出整个字符串,而不是我相信它们应该只打印由字段宽度修饰符指定的字符数。

为了确定“Bourne 兼容性”,我在我的系统上使用 dash 作为 /bin/sh,这是 Debian 系统的典型。Dash 用于高效的脚本执行而不是交互式使用。根据手册页,它力求严格兼容 Bourne,所以我推断如果我的脚本与 dash 一起使用,那么它应该是合理的 Bourne 兼容并且没有 bash 主义。此外,我认识到此功能不是调试打印的全部/全部,并且可能有许多极端情况和可以改进的区域。我欢迎所有建议,但我对这个 printf 问题特别感兴趣。鉴于这应该是多么简单,看来我一定犯了一些愚蠢的错误,但我无法发现它。

这是我的 debug_print 测试脚本:

#!/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..."

谢谢你的帮助。

4

1 回答 1

5

您是为 POSIX 合规性还是 Bourne-shell 合规性而写作,这些是不同的东西..dash争取严格的 POSIX 合规性,而不是 Bourne 合规性..

此外,可变字段宽度:

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

POSIX shell 标准不支持。

要减少字符串长度,您可以在格式字段中使用点和变量:

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

- 编辑 -

本卷 POSIX.1-2008 中没有规定允许指定字段宽度和精度,*因为*可以使用 shell 变量替换直接在格式操作数中替换。如果他们愿意,实现也可以将此功能作为扩展提供。

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

精度 给出 d、o、i、u、x 或 X 转换说明符(该字段用前导零填充)出现的最小位数,在 e 和 f 的基数字符之后出现的位数转换说明符,g 转换说明符的最大有效位数;或从 s 转换说明符中的字符串写入的最大字节数。精度应采用 ('.') 后跟十进制数字字符串的形式;空数字字符串被视为零。

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

我确实注意到一件烦人的事情,那就是点与字节数有关,而不是字符数,因此它实际上仅适用于标准 ASCII 字符集。所以它应该与破折号一起使用(我的意思是-)。手册页中显然有一个dash提到字符的错误,但这应该是字节。

于 2014-03-09T06:37:00.210 回答