在 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),但这应该为进一步调查提供一个起点。