【问题标题】:Bourne shell "printf %s" usageBourne shell "printf %s" 用法
【发布时间】:2014-04-12 06:53:35
【问题描述】:

我有一个简单的 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..."

感谢您的帮助。

【问题讨论】:

  • @mockinterface 正如我在帖子的下一段中提到的,我的不工作行因打印整个输入字符串而不是其中的一部分而失败。另外,Fedora 18 使用哪个版本的 dash?在我的 Debian/测试系统上,我安装了 dash 包的 0.5.7-4 版本。
  • 在发表此评论时,0.5.7-4 也是 Fedora 18 上的当前版本。

标签: bash shell printf sh dash-shell


【解决方案1】:

您是为 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 转换说明符(该字段用前导填充 zeros),出现在基数字符之后的位数 e 和 f 转换说明符,最大有效数 g 转换说明符的数字;或最大字节数 从 s 转换说明符中的字符串写入。这 精度应采用 ('.') 后跟 十进制数字字符串;一个空数字字符串被视为零。

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

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

【讨论】:

  • 您说得对,它们根本不一样,并且重新阅读破折号手册页,清楚地表明 POSIX 合规性是目标。我越来越少地使用非 Linux 系统,但如果我的目标是编写更便携的脚本,那么我认为 POSIX 合规性是更好的目标。至于 printf 语句,您的建议与参数一样有效:“%.*s\n”“$header_len”“$dashes”。我应该尝试使用“。”,但手册页说它是可选的。根据破折号的手册页:“字段宽度或精度可能是'*'而不是数字字符串。”也许是手册页提到的伯克利扩展之一?
  • 嗨,是的,这是一个很好的目标,因为 /bin/sh 指向所有现代操作系统上的 POSIX shell,除了 Solaris dash,而在其他发行版中,bash --posix 被调用。点是 POSIX 规范的一部分,但星号不是。我已将相关内容添加到我的帖子中。我确实注意到一件烦人的事情(以及破折号手册页中的bug),那就是该点与字节数有关,而不是字符数,因此它实际上仅适用于标准 ASCII 字符集。跨度>
  • @JetpackJohn:见评论。
  • +1 感谢@Scrutinizer 的详细解释。用于关注您在 Unix 论坛上的帖子。很高兴你在这里。
  • 嗨@jaypal,谢谢你的客气话,我比以往任何时候都更活跃,但是,我有时也喜欢来这里。你的把手是什么?所以。没有 PM,是吗?
猜你喜欢
  • 2010-09-07
  • 2011-01-13
  • 2011-01-07
  • 1970-01-01
  • 2012-11-09
  • 2015-02-15
  • 1970-01-01
  • 2011-06-12
  • 1970-01-01
相关资源
最近更新 更多