Большинство из этих ответов затрагивают конкретный случай, о котором вы спрашиваете. Существует общий подход друг , и я разработал , что позволяет произвольно процитировать в случае , если вам нужно процитировать Баш команд через несколько слоев расширения оболочки, например, через SSH, su -c
, bash -c
и т.д. Существует один основной примитив вам нужно, здесь в родном bash:
quote_args() {
local sq="'"
local dq='"'
local space=""
local arg
for arg; do
echo -n "$space'${arg//$sq/$sq$dq$sq$dq$sq}'"
space=" "
done
}
Это делает именно то, что говорит: он заключает в кавычки каждый аргумент индивидуально (конечно, после расширения bash):
$ quote_args foo bar
'foo' 'bar'
$ quote_args arg1 'arg2 arg2a' arg3
'arg1' 'arg2 arg2a' 'arg3'
$ quote_args dq'"'
'dq"'
$ quote_args dq'"' sq"'"
'dq"' 'sq'"'"''
$ quote_args "*"
'*'
$ quote_args /b*
'/bin' '/boot'
Это делает очевидную вещь для одного уровня расширения:
$ bash -c "$(quote_args echo a'"'b"'"c arg2)"
a"b'c arg2
(Обратите внимание, что двойные кавычки $(quote_args ...)
необходимы для того, чтобы превратить результат в один аргумент bash -c
.) И его можно использовать более широко для правильного цитирования через несколько уровней расширения:
$ bash -c "$(quote_args bash -c "$(quote_args echo a'"'b"'"c arg2)")"
a"b'c arg2
Приведенный выше пример:
- shell-кавычки каждый аргумент к внутреннему
quote_args
индивидуально и затем объединяет полученный результат в единый аргумент с внутренними двойными кавычками.
- кавычки
bash
,-c
и уже один раз процитированный результат с шага 1, а затем объединяет результат в один аргумент с внешними двойными кавычками.
- отправляет этот беспорядок в качестве аргумента к внешнему
bash -c
.
Это идея в двух словах. Вы можете сделать довольно сложные вещи с этим, но вы должны быть осторожны с порядком оценки и с тем, какие подстроки цитируются. Например, следующее делает неправильные вещи (для некоторого определения «неправильно»):
$ (cd /tmp; bash -c "$(quote_args cd /; pwd 1>&2)")
/tmp
$ (cd /tmp; bash -c "$(quote_args cd /; [ -e *sbin ] && echo success 1>&2 || echo failure 1>&2)")
failure
В первом примере, Баш немедленно расширяется quote_args cd /; pwd 1>&2
на две отдельные команды, quote_args cd /
и pwd 1>&2
, таким образом , по - прежнему УХО , /tmp
когда pwd
выполняется команда. Второй пример иллюстрирует аналогичную проблему для сглаживания. Действительно, та же самая основная проблема возникает со всеми расширениями bash. Проблема здесь в том, что подстановка команд не является вызовом функции: она буквально оценивает один скрипт bash и использует его вывод как часть другого скрипта bash.
Если вы попытаетесь просто избежать операторов оболочки, вы потерпите неудачу, потому что полученная в результате строка bash -c
является просто последовательностью строк в индивидуальном кавычке, которые затем не интерпретируются как операторы, что легко увидеть, если вы откроете строку, которая будет были переданы в bash:
$ (cd /tmp; echo "$(quote_args cd /\; pwd 1\>\&2)")
'cd' '/;' 'pwd' '1>&2'
$ (cd /tmp; echo "$(quote_args cd /\; \[ -e \*sbin \] \&\& echo success 1\>\&2 \|\| echo failure 1\>\&2)")
'cd' '/;' '[' '-e' '*sbin' ']' '&&' 'echo' 'success' '1>&2' '||' 'echo' 'failure' '1>&2'
Проблема в том, что вы преувеличиваете. Вам нужно, чтобы операторы были заключены в кавычки как входные данные для включения bash -c
, что означает, что они должны находиться за пределами $(quote_args ...)
подстановки команд.
Следовательно, что вам нужно сделать в самом общем смысле, это заключить в кавычки каждое слово команды, не предназначенной для расширения во время подстановки команд, отдельно и не применять никаких дополнительных кавычек к операторам оболочки:
$ (cd /tmp; echo "$(quote_args cd /); $(quote_args pwd) 1>&2")
'cd' '/'; 'pwd' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")
/
$ (cd /tmp; echo "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
'cd' '/'; [ -e *'sbin' ] && 'echo' 'success' 1>&2 || 'echo' 'failure' 1>&2
$ (cd /tmp; bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")
success
Как только вы это сделаете, вся строка станет честной игрой для дальнейшего цитирования на произвольных уровнях оценки:
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")"
/
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")"
/
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); $(quote_args pwd) 1>&2")")")"
/
$ bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")"
success
$ bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *sbin ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")"
success
$ bash -c "$(quote_args bash -c "$(quote_args bash -c "$(quote_args cd /tmp); $(quote_args bash -c "$(quote_args cd /); [ -e *$(quote_args sbin) ] && $(quote_args echo success) 1>&2 || $(quote_args echo failure) 1>&2")")")"
success
и т.п.
Эти примеры могут показаться перегруженными, если учесть, что такие слова, как success
, sbin
и pwd
не нужно заключать в кавычки, но ключевой момент, который следует помнить при написании сценария с произвольным вводом, заключается в том, что вы хотите процитировать все, в чем вы не уверены, что не ' не нужно цитировать, потому что вы никогда не знаете, когда пользователь добавит Robert'; rm -rf /
.
Чтобы лучше понять, что происходит под одеялом, вы можете поиграть с двумя небольшими вспомогательными функциями:
debug_args() {
for (( I=1; $I <= $#; I++ )); do
echo -n "$I:<${!I}> " 1>&2
done
echo 1>&2
}
debug_args_and_run() {
debug_args "$@"
"$@"
}
это перечислит каждый аргумент команды перед ее выполнением:
$ debug_args_and_run echo a'"'b"'"c arg2
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)"
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2
$ bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run bash -c "$(quote_args debug_args_and_run echo a'"'b"'"c arg2)")")")"
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'bash'"'"' '"'"'-c'"'"' '"'"''"'"'"'"'"'"'"'"'debug_args_and_run'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'echo'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'a"b'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'"'c'"'"'"'"'"'"'"'"' '"'"'"'"'"'"'"'"'arg2'"'"'"'"'"'"'"'"''"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'bash' '-c' ''"'"'debug_args_and_run'"'"' '"'"'echo'"'"' '"'"'a"b'"'"'"'"'"'"'"'"'c'"'"' '"'"'arg2'"'"''>
1:<bash> 2:<-c> 3:<'debug_args_and_run' 'echo' 'a"b'"'"'c' 'arg2'>
1:<echo> 2:<a"b'c> 3:<arg2>
a"b'c arg2