
В POSIX sh или в оболочке Bourne (как в 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"
По моему мнению, единственный выход здесь — использовать скрипт, оборачивающий echo oh_no
команду. Есть ли другой способ?
решение1
Не совсем.
Одним из решений является резервирование символа в качестве разделителя полей. Очевидно, что включить этот символ, каким бы он ни был, в опцию будет невозможно. Табуляция и новая строка являются очевидными кандидатами, если исходный язык позволяет легко вставлять их. Я бы избегал многобайтовых символов, если вам нужна переносимость (например, тире и 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 их нет, то наилучший внутренний подход, который я вижу, — это использовать специальный пробел. Неразрывный пробел (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
На данный момент я не уверен, как включить это в расширение переменной (для этого понадобится подоболочка), но это должно стать отправной точкой для дальнейшего исследования.