Вызов bash из sh (dash) с командами, считанными из аргументов, и «Незавершенная строка в кавычках»/«неожиданный EOF»

Вызов bash из sh (dash) с командами, считанными из аргументов, и «Незавершенная строка в кавычках»/«неожиданный EOF»

Просто подумал, что стоит это задокументировать: я пробую что-то очень простое — устанавливаю переменную env в bash, и вывожу ее на печать:

$ bash -c "a=1; echo a$a;"
a
$ bash -c "a=1; echo a\$a;"
a1

Теперь я хотел бы сделать то же самое, но вызвать его как аргумент sh(в моей системе ls -la $(which sh)это дает /bin/sh -> dash):

$ sh -c "bash -c "a=1; echo a\$a;""
a$a

# obviously I have to escape inner quotes:

$ sh -c "bash -c \"a=1; echo a\$a;\""
a

# escape the dollar once more?

$ sh -c "bash -c \"a=1; echo a\\$a\" "
sh: Syntax error: Unterminated quoted string

# nope... inner single quotes, then?

$ sh -c "bash -c 'a=1; echo a$a;'"
a

# nope... escape the single quotes?

$ sh -c "bash -c \'a=1; echo a$a;\'"
bash: -c: line 0: unexpected EOF while looking for matching `''
bash: -c: line 1: syntax error: unexpected end of file
a
sh: ': not found

# nope... escape the dollar too?

$ sh -c "bash -c \'a=1; echo a\$a;\'"
bash: -c: line 0: unexpected EOF while looking for matching `''
bash: -c: line 1: syntax error: unexpected end of file
a
sh: ': not found

Итак, мой вопрос - каков правильный синтаксис для экранирования, чтобы он sh -c [bash -c ...]давал тот же результат, что и просто bash -c ...?

решение1

Внутри одинарных кавычек нет символов, имеющих особое значение. Внутри двойных кавычек "\$`есть особое значение. Разберитесь снаружи внутрь: сначала выясните, какая строка создана внешней оболочкой, а затем что сделает из нее внутренняя оболочка.

Например, предположим, что переменная aне определена во внешней оболочке, с

sh -c "bash -c \"a=1; echo a\$a;\""

внешняя оболочка видит строку в двойных кавычках, которая расширяется до bash -c "a=1; echo a$a;", и именно эта строка передается промежуточной оболочке sh. shанализирует это как вызов bashс двумя аргументами -cи a=1; echo a;, причем последний получается в результате расширения строки в двойных кавычках, "a=1; echo a$a;"где переменная aне определена.

Если вы проведете этот анализ, то увидите, что один из способов получить то, что вам нужно, — это иметь "a=1; echo a\$a;"на этом этапе. Чтобы получить этот дополнительный обратный слеш, вам нужно поставить два обратных слеша в оригинале, поскольку уже был один этап расширения оболочки внутри двойных кавычек. Два плюс один равно трем.

sh -c "bash -c \"a=1; echo a\\\$a;\""

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

sh -c 'bash -c "a=1; echo a\$a;"'

Поскольку вы не хотите ничего расширять при вызове bashиз shлюбого из них, вы можете использовать одинарные кавычки и там. Вы не можете поместить одинарные кавычки внутрь строки в одинарных кавычках, но есть способ имитировать это: put '\''. Формально это завершает литерал в одинарных кавычках, добавляет литерал одинарной кавычки и начинает новый литерал в одинарных кавычках, но вы можете думать о последовательности из четырех символов '\''как о способе поместить одинарную кавычку внутрь строки в одинарных кавычках.

sh -c 'bash -c '\''a=1; echo a$a;'\'''

Вы можете опустить это ''в конце, это менее систематично, но приятнее для глаз.

sh -c 'bash -c '\''a=1; echo a$a;'\'

Одинарные кавычки не являются специальными внутри двойных кавычек, поэтому при написании "bash -c 'a=1; echo a\$a;'"вам необходимо ставить обратную косую черту перед знаком доллара, чтобы предотвратить расширение $aво внешней оболочке.

решение2

Ну, я нашел пару работающих (обратите внимание, это Ubuntu 11.04):

$ sh -c "bash -c 'a=1; echo a\$a \'"
a1
$ sh -c "bash -c 'a=1; echo a\$a;'"
a1

... и похоже, пока первая одинарная кавычка не экранирована, а доллар экранирован - это работает, независимо от того, экранирована последняя одинарная кавычка или нет! Что меня до сих пор немного озадачивает...

Связанный контент