Llamar a bash desde sh (dash) con comandos leídos de args y "cadena entre comillas no terminada"/"EOF inesperado"

Llamar a bash desde sh (dash) con comandos leídos de args y "cadena entre comillas no terminada"/"EOF inesperado"

Solo pensé en documentar esto: estoy intentando algo muy simple: configurar la variable env bashe imprimirla:

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

Ahora me gustaría lo mismo, pero llamado como argumento de sh(en mi sistema, ls -la $(which sh)da /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

Entonces, mi pregunta sería: ¿cuál es la sintaxis adecuada para escapar, de modo que sh -c [bash -c ...]dé el mismo resultado que just bash -c ...?

Respuesta1

Entre comillas simples, ningún carácter tiene un significado especial. Entre comillas dobles, "\$`tienen un significado especial. Trabaje desde afuera hacia adentro: primero averigüe qué cuerda está construida por la capa exterior, luego qué hará la capa interior con ella.

Por ejemplo, suponiendo que la variable ano está definida en el caparazón exterior, con

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

el caparazón exterior ve una cadena entre comillas dobles que se expande hasta bash -c "a=1; echo a$a;", y es esa cadena la que se pasa al intermedio sh. shanaliza esto como una llamada a bashcon los dos argumentos -cy a=1; echo a;, este último resultante de expandir la cadena entre comillas dobles "a=1; echo a$a;"donde la variable ano está definida.

Si haces este análisis podrás ver que una forma de conseguir lo que quieres sería tenerlo "a=1; echo a\$a;"en esta etapa. Para obtener esta barra invertida adicional, debe colocar dos barras invertidas en el original, ya que ya ha habido una etapa de expansión del shell entre comillas dobles. Dos más uno son tres.

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

Sería más sencillo usar comillas simples para la cadena exterior, ya que no desea expandir nada allí.

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

Como no desea expandir nada cuando llama bashdesde shninguno de los dos, también puede usar comillas simples allí. No puedes poner comillas simples dentro de una cadena entre comillas simples, pero hay una manera de simular esto: put '\''. Formalmente, esto termina el literal entre comillas simples, agrega una comilla simple literal e inicia un nuevo literal entre comillas simples, pero puede pensar en la secuencia de cuatro caracteres '\''como una forma de poner una comilla simple dentro de una cadena entre comillas simples.

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

Puedes omitir eso ''al final, es menos sistemático pero más agradable a la vista.

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

Las comillas simples no son especiales dentro de las comillas dobles, por lo que cuando escribes "bash -c 'a=1; echo a\$a;'"necesitas la barra invertida antes del dólar para evitar la expansión de $aen la capa exterior.

Respuesta2

Bueno, encontré un par que funcionan (NB, este es Ubuntu 11.04):

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

... y parece que, siempre que no se escape la primera comilla simple y se escape el dólar, ¡funciona, independientemente de si se escapa la comilla simple final o no! Lo cual todavía me desconcierta bastante...

información relacionada