printf 类似工作的包装器,用于添加前缀和重定向错误消息

printf 类似工作的包装器,用于添加前缀和重定向错误消息

我的 POSIX shell 代码error中有一个函数。sh它本质上看起来像

error () {
    printf 'utility: ERROR: ' >&2
    printf "$@" >&2
}

第二个printf使得我可以使用例如调用该函数

error 'Something relating to "%s" went wrong!\n' "$thing"
exit 1

该函数会在消息前面加上字符串前缀utility: ERROR:,并安排所有内容发送到标准错误,同时我仍然可以像使用printf.

明显地,外壳检查抱怨第二个printf,说

SC2059:不要在 printf 格式字符串中使用变量。使用 printf "..%s.." "$foo"

做我所做的事情并忽略这个警告是否“安全”(甚至可能禁用# shellcheck disable=SC2059在代码中带有注释)?注意,我总是使用我的error函数确切地否则我会使用printf,即使用静态单引号第一个参数。

“安全吗?”这里的意思是“我的函数是否printf以适当的方式充当类似工作的包装器(除了重定向之外) ?”

答案1

这是安全的,因为您确实打算在此处将格式作为参数传递。但在使用函数的时候一定要记住它是格式。所以也许你应该命名你的函数errorf作为提醒。

如果您将其用作:

error "invalid arg: $arg"

$arg你会导致类似的问题%99999999s

理想情况下,您希望告诉 shellcheck 忽略printf 它可以标记printf第一个参数包含变量的用法。

一些注意事项:

  • 由于您调用了printf两次,因此至少有 2 次write()系统调用。不太可能产生任何差异,请注意,像s 这样的某些printf实现在某些情况下也会执行多个系统调用。bash那种问题我想到的是错误消息的两部分与并行运行的另一个命令的输出交织在一起。
  • 前缀中error "%s\n" error1 error2只会输出第一个错误。如果您想这样做的话,那很好。你还可以这样做:

    errorf() { printf "utility: ERROR: $@" >&2; }
    

    将前缀添加到格式之前,因此每次重用格式时都会输出它(这也将解决多个问题)write() 问题)。

    utility然而,如果你想成为一个变量,那就行不通。

    PROG_NAME=${0##*/}
    errorf() { printf >&2 "$PROG_NAME: 错误: $@"; }
    
    不行,因为当$0contains%或 时它会失败\

    PROG_NAME=${0##*/}
    错误(){
    格式=$1;转移
    printf >&2“%s:错误:$format”“$PROG_NAME”“$@”
    }
    在重用格式的情况下不起作用(并且意味着用户需要将指令偏移一个%<n>$d(并不是我建议使用这些指令,因为它们不可移植))。

    除了手动转义\and之外,我想不出任何简单的解决方法,即使这也不容易,因为对于许多实现来说,您还需要转义在其他字符中找到的字节,而不是(对于像 BIG5 这样的字符集或 GB18030 有一些)。%$PROG_NAMEprintf0x5c\

相关内容