Можно ли «защитить» символ IFS от разделения полей?

Можно ли «защитить» символ IFS от разделения полей?

В 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

На данный момент я не уверен, как включить это в расширение переменной (для этого понадобится подоболочка), но это должно стать отправной точкой для дальнейшего исследования.

Связанный контент