Общее правило в сценариях оболочки заключается в том, что переменные всегда должны заключаться в кавычки, если нет веской причины не делать этого. Для получения более подробной информации, чем вы, вероятно, хотели бы узнать, взгляните на эти замечательные вопросы и ответы: последствия для безопасности того, что вы забыли заключить переменную в оболочки bash / POSIX .
Рассмотрим, однако, функцию, подобную следующей:
run_this(){
$@
}
Должны ли они $@
быть указаны там или нет? Я немного поиграл с ним и не смог найти ни одного случая, когда бы нехватка кавычек вызвала проблему. С другой стороны, использование кавычек приводит к разрыву при передаче команды, содержащей пробелы в качестве переменной в кавычках:
#!/usr/bin/sh
set -x
run_this(){
$@
}
run_that(){
"$@"
}
comm="ls -l"
run_this "$comm"
run_that "$comm"
Запуск приведенного выше сценария возвращает:
$ a.sh
+ comm='ls -l'
+ run_this 'ls -l'
+ ls -l
total 8
-rw-r--r-- 1 terdon users 0 Dec 22 12:58 da
-rw-r--r-- 1 terdon users 45 Dec 22 13:33 file
-rw-r--r-- 1 terdon users 43 Dec 22 12:38 file~
+ run_that 'ls -l'
+ 'ls -l'
/home/terdon/scripts/a.sh: line 7: ls -l: command not found
Я могу обойти это, если использую run_that $comm
вместо run_that "$comm"
, но, поскольку run_this
(без кавычек) функция работает с обоими, это кажется более безопасной ставкой.
Таким образом, в конкретном случае использования $@
в функции, работа которой заключается в выполнении $@
в качестве команды, следует $@
заключить в кавычки? Пожалуйста, объясните, почему он не должен / не должен быть в кавычках, и приведите пример данных, которые могут его сломать.
источник
run_that
поведение определенно то, что я ожидал (что если в пути к команде есть пробел?). Если вы хотите другое поведение, конечно , вы бы его конец цитаты на вызова -сайт , где вы знаете , что данные? Я ожидал бы вызвать эту функцию какrun_that ls -l
, которая работает одинаково в любой версии. Есть ли случай, который заставил вас ожидать по-другому?${mycmd[@]}
.Ответы:
Проблема заключается в том, как команда передается функции:
"$@"
следует использовать в общем случае, когда вашаrun_this
функция имеет префикс к обычно написанной команде.run_this
приводит к цитированию ада:Я не уверен, как я должен передать имя файла с пробелами
run_this
.источник
run_this
любым из них.$@
случайно оставили без кавычек. Я должен был оставить пример. : Drun_this
. По сути, это та же проблема, с которой вы сталкиваетесь при вставке сложных команд в строки, как обсуждалось в Bash FAQ 050 .Это либо:
Или:
или:
Но:
Не имеет особого смысла.
Если вы хотите выполнить
ls -l
команду (а неls
команду с аргументамиls
и в-l
качестве аргументов), вы должны сделать:Но если (более вероятно), это
ls
команда с аргументамиls
и в-l
качестве аргумента, вы должны выполнить:Теперь, если вы хотите выполнить не просто простую команду, а если вы хотите делать переменные, перенаправления, каналы ..., только
interpret_this_shell_code
сделайте:хотя, конечно, вы всегда можете сделать:
источник
Глядя на него из Баша / КШ / ЗШ точки зрения,
$*
и$@
являются частным случаем общего расширения массива. Расширения массива не похожи на обычные расширения переменных:С помощью расширений
$*
/${a[*]}
вы объединяете массив с первым значениемIFS
(по умолчанию это пробел) в одну гигантскую строку. Если вы не заключите его в кавычки, он будет разбит как обычная строка.С расширениями
$@
/${a[@]}
поведение зависит от того, цитируется ли расширение$@
/${a[@]}
или нет:"$@"
или"${a[@]}"
), вы получите эквивалент"$1" "$2" "$3" #...
или"${a[1]}" "${a[2]}" "${a[3]}" # ...
$@
или${a[@]}
), вы получите эквивалент$1 $2 $3 #...
или${a[1]} ${a[2]} ${a[3]} # ...
Для переноса команд вам определенно нужны заключенные в кавычки @ расширения (1.).
Более подробная информация о массивах bash (и bash-like): https://lukeshu.com/blog/bash-arrays.html
источник
Поскольку, когда вы не заключаете двойные кавычки
$@
, вы оставляете все проблемы со ссылками, которые вы дали своей функции.Как вы могли запустить команду с именем
*
? Вы не можете сделать это сrun_this
:И вы видите, даже когда произошла ошибка,
run_that
вы получили более значимое сообщение.Единственный способ расширить
$@
до отдельных слов это двойные кавычки. Если вы хотите запустить его как команду, вы должны передать команду и параметры в виде отдельных слов. То, что вы сделали на стороне вызывающего абонента, а не в вашей функции.это лучший выбор. Или если ваша оболочка поддерживает массивы:
Даже когда оболочка вообще не поддерживает массив, вы все равно можете поиграть с ним, используя
"$@"
.источник
Выполнение переменных в
bash
методе подвержено сбоям. Просто невозможно написатьrun_this
функцию, которая правильно обрабатывает все крайние случаи, например:ls | grep filename
)ls > /dev/null
)if
while
и т.д.Если все, что вы хотите сделать, это избежать повторения кода, вам лучше использовать функции. Например, вместо:
Ты должен написать
Если команды доступны только во время выполнения, вы должны использовать
eval
, который специально разработан для обработки всех причуд, которые могут привести кrun_this
сбою:Обратите внимание, что
eval
это известно проблемами безопасности, но если вы передадите переменные из ненадежных источниковrun_this
, вы также столкнетесь с выполнением произвольного кода.источник
Выбор ваш. Если вы не цитируете
$@
какое-либо из его значений, подвергните дополнительному расширению и интерпретации. Если вы заключите это в кавычки, все переданные аргументы будут воспроизведены в ее расширении дословно. Вы никогда не сможете надежно обрабатывать синтаксические токены оболочки, такие как&>|
и т. Д., Так или иначе, не разбирая аргументы самостоятельно, так что у вас останется более разумный выбор - передать свою функцию одному из:"$@"
....или...
$@
.Ни один из способов не является неправильным, если он является преднамеренным и если последствия того, что вы выбираете, хорошо поняты. Оба способа имеют преимущества друг перед другом, хотя преимущества второго редко бывают особенно полезными. Все еще...
... это не бесполезно , просто редко вероятно, будут иметь много использования . И в
bash
оболочке, потомуbash
что по умолчанию не привязывает определение переменной к своей среде, даже когда указанное определение добавляется перед командной строкой специальной встроенной функции или функции, глобальное значение для$IFS
не затрагивается, и его объявление является локальным только наrun_this()
звонок.Так же:
... Глобализация также настраивается. Цитаты служат цели - они не зря. Без них расширение оболочки подвергается дополнительной интерпретации - настраиваемой интерпретации. Раньше - с некоторыми очень старыми снарядами - что
$IFS
было во всем мире применяются для всех входных данных, а не только расширение. На самом деле, указанные оболочки вели себя очень похожеrun_this()
на то, что разбили все входные слова на значение$IFS
. Итак, если вам нужно очень старое поведение оболочки, то вам следует использоватьrun_this()
.Я не ищу его, и в данный момент мне трудно найти полезный пример для этого. Я обычно предпочитаю, чтобы команды, которые запускает моя оболочка, были теми, которые я набираю на ней. И так, учитывая выбор, я бы почти всегда
run_that()
. Кроме этого...Почти все может быть процитировано. Команды будут выполняться в кавычках. Это работает, потому что к тому моменту, когда команда фактически выполняется, все входные слова уже подверглись удалению кавычек, что является последним этапом процесса интерпретации ввода оболочки. Таким образом, разница между
'ls'
иls
может иметь значение только во время интерпретации оболочки - и поэтому цитированиеls
гарантирует, что любой названный псевдонимls
не заменит моеls
командное слово в кавычках . Кроме этого, единственное, на что влияют кавычки, - это разграничение слов (как и почему работает цитирование переменных / ввода-пробела) и интерпретация метасимволов и зарезервированных слов.Так:
Вы никогда не будете в состоянии сделать это с любым из
run_this()
илиrun_that()
.Но имена функций, или
$PATH
команды d, или встроенные команды будут выполняться просто в кавычках или без кавычек, и это именно то, какrun_this()
иrun_that()
работает в первую очередь. Вы не сможете сделать ничего полезного с$<>|&(){}
любым из них. Если не считатьeval
, есть.Но без этого вы ограничены простыми командами в силу используемых вами кавычек (даже если вы этого не делаете, потому что
$@
действует как кавычка в начале процесса, когда команда разбирается на метасимволы) . То же ограничение относится к назначениям и перенаправлениям командной строки, которые ограничены командной строкой функции. Но это не имеет большого значения:Я мог бы так же легко
<
перенаправить ввод или>
вывод туда, как открыл канал.В любом случае, здесь нет правильного или неправильного пути - каждый способ имеет свое применение. Просто вы должны написать это так, как вы собираетесь использовать, и вы должны знать, что вы собираетесь делать. Пропуск кавычек может иметь цель - иначе кавычек не было бы вообще - но если вы опускаете их по причинам, не имеющим отношения к вашей цели, вы просто пишете плохой код. Делай, что ты имеешь в виду; Я все равно пытаюсь.
источник