Bash неправильно выполняет расширение "$ @" внутри $ {var + ...}? - PullRequest
0 голосов
/ 28 августа 2018

В настоящее время я получаю 2 мнения по 3 оболочкам:

$ bash -c 'set bar; set foo${1+ "$@"}; echo "$# $*"'
1 foobar

$ ash -c 'set bar; set foo${1+ "$@"}; echo "$# $*"'
2 foo bar

$ dash -c 'set bar; set foo${1+ "$@"}; echo "$# $*"'
2 foo bar

Или я пропустил какое-то определение POSIX, которое отображает мой пример как поведение, определяемое реализацией?

Обратите внимание, что только "$ @" вызывает различия. Следующее работает одинаково для всех 3 оболочек:

$ bash -c 'set bar; set foo${1+ $*}; echo "$# $*"'
2 foo bar

$ ash -c 'set bar; set foo${1+ $*}; echo "$# $*"'
2 foo bar

$ dash -c 'set bar; set foo${1+ $*}; echo "$# $*"'
2 foo bar

К сожалению, $ * не совсем то же самое, что и "$ @", если аргументы должны содержать пробелы.

Кстати, я использую Bash версии 4.4.12 (1) -релиз.

Ответы [ 2 ]

0 голосов
/ 27 июня 2019

Хотя это кажется ошибкой, я сталкивался с некоторыми странными крайними случаями с "... $ @" и раньше, и это соответствует ожидаемому поведению, по крайней мере, исторически. " $@" (с висячими пробелами) - неопределенное поведение , потому что вы в основном запрашиваете, чтобы все члены массива $@ рассматривались как аргументы для синтаксического анализатора оболочки плюс один символ пробел. Может быть немного сложно обернуть голову, и некоторые из вещей, которые вы сказали, вводят в заблуждение, поэтому я решил предоставить больше информации и советы для достижения лучшего поведения.

То, как вы используете "$ @" и пытаетесь заменить его на "$ *", может вызвать проблемы. Существуют значительные различия между поведением $* и $@, которое соответствует их предполагаемому использованию. Если вы используете $@, это потому, что вы хотите сделать что-то вроде этого:

wrapper_fxn() {
  wrapped_cmd "$@"
}

Когда вы заключаете $ @ в кавычки (и любой другой массив в этом отношении), bash оценивает его , как если бы каждый аргумент / член массива сначала был заключен в двойные кавычки. Тем не менее, здесь задействовано немного больше магии, так как он фактически не заключает в двойные кавычки, а удаляет двойные кавычки, но указывает на изменение поведения при разборе на более позднем этапе.

Причина, по которой существует специальное поведение "$@", заключается в том, чтобы помочь справиться с неприятными помехами quoting & argv.

Итак, думайте о них так:

  • $ @: несколько аргументов / для распространения аргументов при сохранении цитирования пробела
  • $ *: один аргумент / для объединения всех строк в массиве в один аргумент

Мой совет: не делай "$@ "; это неопределенное поведение. Как только вы цитируете как "$@", вы пытаетесь выполнить расширение аргумента и добавить еще один аргумент или массив аргументов, просто сделайте это так:

# Making sure to have quotes around each new arg
somecmd "$newFirstArg" "$@" "${additionalArray[@]}" "$newLastArg"

... это лучше сообщит о намерениях и почти никогда не даст неожиданного поведения.

Подробнее

Учитывая все вышесказанное, странное поведение ребра "$ @" связано с тем, когда вы используете его, например: "... $@", как и "$ @". Bash объединит один из элементов массива в зависимости от позиции ... с дополнительными символами.

Когда вы добавляете что-либо справа или слева от $@ в цитируемом утверждении, например "extra stuff $@" или "$@ extra stuff", то, что сделает bash, это просто добавит extra stuff в первый или последний член массива соответственно. Исключением является случай, когда в операторе с двойными кавычками есть другой массив, возможно, $@, дублированный так: "$@ $@". Фактически он объединит последний элемент первого массива с первым элементом последнего массива. Таким образом, если в $@ есть три члена, в окончательном argv[] из "$@ $@" будет 5 аргументов, а не 6.

Вот полезная функция, помогающая разобраться в поведении:

# example function
print-args () 
{ 
    local i=0;
    for arg in "$@";
    do
        printf "[${i}]:%s\n" "\"$arg\"";
        let i++;
    done
}

# good example arg-set covering many of the important edge cases
set "one \"one" two three

# One case, modify it to see the various permutations of the edge cases
print-args " $@ $@ "
#>[0]:" one "one"
#>[1]:"two"
#>[2]:"three one "one"
#>[3]:"two"
#>[4]:"three "

0 голосов
/ 06 февраля 2019

Я склонен не доверять пробелам внутри расширения без кавычек. Я не могу объяснить, почему это не работает, но я могу дать вам решение, которое делает: переместить пространство за пределы расширения параметра. Команда set не возражает против пробела.

$ bash -c 'set bar; set foo ${1+"$@"}; echo "$# $*"'   # 5.0.2(1)
2 foo bar

$ dash -c 'set bar; set foo ${1+"$@"}; echo "$# $*"'   # 0.5.10.2
2 foo bar

$ ash  -c 'set bar; set foo ${1+"$@"}; echo "$# $*"'   # /bin/sh on FreeBSD 7.3
2 foo bar

$ ksh  -c 'set bar; set foo ${1+"$@"}; echo "$# $*"'   # 93u+ 2012-08-01
2 foo bar

$ zsh  -c 'set bar; set foo ${1+"$@"}; echo "$# $*"'   # 5.7
2 foo bar

(я запустил все на последнем тестировании Debian, кроме ash)

...