Следует ли избегать эквивалентов bash -c, sh -c и других оболочек в моих сценариях оболочки?

Рассмотрим следующий код:

#!/bin/bash -x
VAR='1 2 3'
bash -c "echo "\$$VAR""
eval "echo "\$$VAR""
bash -c "echo \"\$$VAR\""
eval "echo \"\$$VAR\""

Какие выходы:

+ VAR='1 2 3'
+ bash -c 'echo $1' 2 3
3
+ eval 'echo $1' 2 3
++ echo 2 3
2 3
+ bash -c 'echo "$1 2 3"'
 2 3
+ eval 'echo "$1 2 3"'
++ echo ' 2 3'
 2 3

Кажется, что оба eval и bash -c интерпретируют коды так же, как i.e "echo "\$$VAR"" до 'echo $1' 2 3 и "echo \"\$$VAR\"" до 'echo "$1 2 3"'.

Единственное отличие, которое я, по-видимому, замечаю, заключается в том, что bash -c открывает подоболочку и таким образом изменяет результаты от eval. Например, в

bash -c 'echo $1' 2 3

2 и 3 - позиционные параметры для подоболочки. С другой стороны, в

eval 'echo $1' 2 3

это просто еще один аргумент для echo.

Итак, мой вопрос в том, является ли опция -c (bash -c, sh -c или другие эквиваленты оболочек) безопасной для использования или она зла, как eval?

Ответ 1

Да, вам следует избегать использования sh -c и эквивалентов в ваших сценариях оболочки, так же, как вы избегаете eval.

eval "$foo"

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

sh -c "$foo"

делает то же самое - запускает расширения, перенаправления и т.п. - только в совершенно новой оболочке (не используя неэкспонированные переменные или другое состояние).

Оба из них означают, что контент, такой как $(rm -rf /), подвержен расширению, если не будет предпринята большая осторожность, вместо того, чтобы гарантировать, что, как это обычно бывает, данные будут обрабатываться только как данные, основополагающий элемент написания защищенного кода.


Теперь, счастливая вещь заключается в том, что, когда вы используете bash (или zsh или ksh93), а не sh, вы можете избегать использования eval почти во всех случаях.

Например:

value=$(eval "echo \$$varname")

... можно заменить на:

value=${!varname}

... и в bash,

eval "$varname="'$value'

... можно заменить на...

printf -v varname %s "$value"

... и т.д. (с ksh93 и zsh, имеющими прямые эквиваленты последним); более продвинутые формулировки с участием ассоциативных карт и т.


Примечательно, что bash -c не эффективно заменяет eval в большинстве приведенных выше примеров, потому что он не работает в том же контексте: Изменения, внесенные в состояние оболочки, выбрасываются, когда дочерний процесс завершается; таким образом, не только bash -c не покупает безопасность, но не работает как замена eval для начала.

Ответ 2

Насколько мне известно, нет причин использовать bash -c из оболочки bash. С его помощью начнется новый процесс, который будет дорогостоящим.

Вы можете использовать eval, который не запускает новый процесс. Если вы хотите под-оболочку (например, для сохранения среды), вы можете использовать круглые скобки.

Обычно bash -c (или другие оболочки с -c) используются для выполнения команды из другой среды без оболочки (или, возможно, DSL, интерпретируемой оболочкой), где требуется расширение аргументов оболочки. Например, в старые времена вы можете использовать его с execvp в программе на C. В наши дни обычно есть способы запуска команды с использованием оболочки в большинстве сред.