我的 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: 错误: $@"; }$0
contains%
或 时它会失败\
。和
在重用格式的情况下不起作用(并且意味着用户需要将指令偏移一个PROG_NAME=${0##*/} 错误(){ 格式=$1;转移 printf >&2“%s:错误:$format”“$PROG_NAME”“$@” }%<n>$d
(并不是我建议使用这些指令,因为它们不可移植))。除了手动转义
\
and之外,我想不出任何简单的解决方法,即使这也不容易,因为对于许多实现来说,您还需要转义在其他字符中找到的字节,而不是(对于像 BIG5 这样的字符集或 GB18030 有一些)。%
$PROG_NAME
printf
0x5c
\