是否可以“保护”IFS 字符免受字段拆分?

mur*_*uru 3 shell posix bourne-shell

在 POSIX sh 或 Bourne shell(如在 Solaris 10's 中/bin/sh)中,是否有可能具有以下内容:

a='some var with spaces and a special space'
printf "%s\n" $a
Run Code Online (Sandbox Code Playgroud)

并且,使用默认值IFS,得到:

some
var
with
spaces
and
a
special space
Run Code Online (Sandbox Code Playgroud)

也就是说,保护之间的空间special,并space通过引用或逃逸的某种组合?

a事先不知道字数,或者我会尝试类似的东西:

a='some var with spaces and a special\ space'
printf "%s\n" "$a" | while read field1 field2 ...
Run Code Online (Sandbox Code Playgroud)

上下文是Cassandra 中报告的这个错误,其中 OP 试图设置一个环境变量,指定 JVM 的选项:

export JVM_EXTRA_OPTS='-XX:OnOutOfMemoryError="echo oh_no"'
Run Code Online (Sandbox Code Playgroud)

在执行 Cassandra 的脚本中,它必须支持 POSIX sh 和 Solaris sh:

JVM_OPTS="$JVM_OPTS $JVM_EXTRA_OPTS"
#...
exec $NUMACTL "$JAVA" $JVM_OPTS $cassandra_parms -cp "$CLASSPATH" $props "$class"
Run Code Online (Sandbox Code Playgroud)

IMO 唯一的出路是使用包装echo oh_no命令的脚本。还有其他方法吗?

Gil*_*il' 5

并不真地。

一种解决方案是保留一个字符作为字段分隔符。显然,不可能在选项中包含该字符,无论它是什么。如果源语言易于插入,制表符和换行符是明显的候选者。如果您想要可移植性,我会避免使用多字节字符(例如,破折号和 BusyBox 不支持多字节字符)。

如果您依赖 IFS 拆分,请不要忘记使用set -f.

tab=$(printf '\t')
IFS=$tab
set -f
exec java $JVM_EXTRA_OPTS …
Run Code Online (Sandbox Code Playgroud)

另一种方法是引入引用语法。一个非常常见的引用语法是反斜杠保护下一个字符。使用反斜杠的缺点是,许多不同的工具将它用作引用字符,有时很难弄清楚您需要多少个反斜杠。

set java
eval 'set -- "$@"' $(printf '%s\n' "$JVM_EXTRA_OPTS" | sed -e 's/[^ ]/\\&/g' -e 's/\\\\/\\/g') …
exec "$@"
Run Code Online (Sandbox Code Playgroud)