Как правильно экспортировать переменные env, содержащие пробелы в ZSH

Как правильно экспортировать переменные env, содержащие пробелы в ZSH

Добавьте переменную env, содержащую пробелы, в~/.zshrc

export SKIP="-Dskip1 -Dskip2"
export SKIP_NO_SPACES="-Dskip3"

Попробуйте использовать это

set -x  
mvn $SKIP $SKIP_NO_SPACES

Команда, которая выполняется, на самом деле

+-zsh:108> mvn '-Dskip1 -Dskip2' -Dskip3

В кавычки помещается только та переменная, которая содержит пробелы.

Как избежать добавления одинарных кавычек?

Если вообще не использовать кавычки/экранирование, при открытии оболочки возникнет ошибка:

/.zshrc:export:11: недопустимо в данном контексте: -Dskip2

решение1

Я думаю, что вы (в некотором роде) злоупотребляете переменными окружения. Этот пробел является частью переменной, и когда вы выполняете расширение параметра с помощью $, он просто передаст этот пробел команде. mvnполучает один аргумент – содержимое вашей переменной.

Как правильно заметил Камил Мациоровски в комментарии, это относится только к Zsh. То, что вы предлагаете, будет работать в Bash, если вы опустите кавычки:

bash-5.0$ export test="foo bar"
bash-5.0$ ls $test
ls: bar: No such file or directory
ls: foo: No such file or directory
bash-5.0$ ls "$test"
ls: foo bar: No such file or directory

Одной из альтернатив сделать то, что вы хотите, было быглобальный псевдоним(также особенность Zsh, а не Bash), который можно заменить в любом месте строки. Он сохраняет свои пробелы:

$ alias -g SKIP="-Dskip1 -Dskip2"
$ alias -g SKIP2="-Dskip3"
$ mvn SKIP SKIP2

Это просто «добавит» строку туда, куда она была добавлена.

решение2

Проблема не имеет ничего общего с кавычками или экранированием, она связана с разделением слов (или его отсутствием). В большинстве оболочек, если вы используете переменную, содержащую пробелы, не заключая ее в двойные кавычки, значение будет разделено на «слова» на основе этих пробелов. Поэтому, если переменная SKIPустановлена ​​в -Dskip1 -Dskip2(обратите внимание, что в этом значении нет кавычек или экранирований), и вы используете ее как mvn $SKIP, она становится эквивалентной mvn "-Dskip1" "-Dskip2".

Но zsh не является большинством оболочек, и (по умолчанию) не делает разбиение слов. Если переменная установлена ​​в -Dskip1 -Dskip2, то это то, что передается команде,как единый аргумент. Кавычки, которые вы видите в set -xвыводе, на самом деле отсутствуют, они просто добавлены, чтобы явно указать, что вся строка передается как один аргумент.

Есть несколько способов сообщить zsh, что вы хотите, чтобы было выполнено разделение слов. Вы можете использовать модификатор =в расширении:

mvn ${=SKIP}

Или вы можете включить sh-подобное разделение для всех расширений с помощью опции оболочки:

setopt shwordsplit
mvn $SKIP

...однако, это может иметь неприятные побочные эффекты, такие как неожиданное разделение вещей, которые вы считали отдельными аргументами, и расширение всего, что выглядит как подстановочный знак имени файла, в список имен файлов. Разделение слов оболочки имеет тенденцию вызывать ошибки, поэтому оно отключено по умолчанию в zsh. Действительно правильный способ сохранить несколько строк в переменной — использовать массив вместо простой переменной:

SKIP=(-Dskip1 -Dskip2)
mvn "${SKIP[@]}"

Это будет работать в zsh, bash и некоторых других оболочках (но не в тех, где нет поддержки массивов). Однако вы не можете экспортировать массивы из zsh (ну, вы можете запустить export SKIP, но это ничего не сделает). Массивы — это функция оболочки, в то время как переменные окружения — это функция ОС, которая поддерживает только простые строки. Вам действительно нужно экспортировать эти параметры или вы просто используете их внутри оболочки?

решение3

Одинарные кавычки появляются в диагностической строке, напечатанной из-за set -x. Они не попадают в mvn. Содержимое переменной ( -Dskip1 -Dskip2) поступает mvnв как один аргумент, поскольку в zshвам не нужно жестко заключать переменные в кавычки (это необходимо в оболочках, совместимых с POSIX).

Проблема, кажется, в том, как zshрасширяет переменные. Я думаю, вы хотите, чтобы разделение слов происходило, когда $SKIPis не заключено в кавычки (как это происходит в других оболочках), но в zshнорме этого не происходит.

Использовать ${=SKIP}дляактивировать разделение слов после замены.

Пример:

% SKIP='-Dskip1 -Dskip2' 
% printf "%s\n" $SKIP   
-Dskip1 -Dskip2
% printf "%s\n" ${=SKIP}
-Dskip1
-Dskip2
% 

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