Більшість цих відповідей стосуються конкретного випадку, про який ви питаєте. Існує загальний підхід один , і я розробив , що дозволяє довільно процитувати в разі , якщо вам потрібно процитувати Баш команд через кілька шарів розширення оболонки, наприклад, через SSH, su -c
, bash -c
і т.д. Існує один основний примітив вам потрібно, тут у рідному базі:
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
Наведений вище приклад:
- оболонка цитує кожен аргумент до внутрішнього
quote_args
окремо, а потім поєднує отриманий результат у єдиний аргумент із внутрішніми подвійними лапками.
- shell-quotes
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
У першому прикладі bash негайно розгортається quote_args cd /; pwd 1>&2
на дві окремі команди, quote_args cd /
і pwd 1>&2
, таким чином, CWD залишається, /tmp
коли pwd
команда виконується. Другий приклад ілюструє аналогічну проблему глобалізації. Дійсно, однакова основна проблема виникає з усіма розширеннями bash. Проблема тут полягає в тому, що підміна команди не є викликом функції: вона буквально оцінює один скрипт bash та використовує його вихід у рамках іншого скрипту bash.
Якщо ви спробуєте просто уникнути операторів оболонки, ви не зможете, тому що передана в результаті рядок - bash -c
це лише послідовність рядків, котируваних окремо, які потім не інтерпретуються як оператори, що легко зрозуміти, чи повторюєте ви рядок, який би передані в баш:
$ (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