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

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

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

a='some var with spaces and a special space'
printf "%s\n" $a

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

some
var
with
spaces
and
a
special space

也就是说,通过引用或转义的某种组合来保护special和之间的空格?space

a事先不知道其中的字数,否则我会尝试以下操作:

a='some var with spaces and a special\ space'
printf "%s\n" "$a" | while read field1 field2 ...

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

export JVM_EXTRA_OPTS='-XX:OnOutOfMemoryError="echo oh_no"'

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

JVM_OPTS="$JVM_OPTS $JVM_EXTRA_OPTS"
#...
exec $NUMACTL "$JAVA" $JVM_OPTS $cassandra_parms -cp "$CLASSPATH" $props "$class"

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

答案1

并不真地。

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

如果您依赖 IFS 拆分,请不要忘记使用 关闭通配符扩展set -f

tab=$(printf '\t')
IFS=$tab
set -f
exec java $JVM_EXTRA_OPTS …

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

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

答案2

如果您使用的是 Bash 或类似的工具,数组就可以解决问题:

a=(some var with spaces and a 'special space')

但由于 POSIX shell 没有这些,我能看到的最好的内部方法是实际使用一个特殊的空间。不间断空格 (U+00A0) 非常适合此目的,但在 ASCII 之外需要就脚本的字符集达成一致。

a="some var with spaces and a special space"
# this is a non-breaking space ------^
echo "$a" \
| while read word; do printf '%s\n' ${word} | sed 's@ @ @g'; done
# this is a non-breaking space ----------------------^

这输出:

some
var
with
spaces
and
a
special space

目前,我不确定如何将其包含在变量扩展中(它将需要一个子 shell),但这应该为进一步调查提供一个起点。

相关内容