Expansión de la variable dentro de comillas simples en un comando en Bash

Quiero ejecutar un comando desde un script de shell bash que tiene comillas simples y algunos otros comandos dentro de las comillas simples y una variable.

por ejemplo, repo forall -c '....$variable'

En este formato, $ se escapó y la variable no se expandió.

Probé las siguientes variaciones, pero fueron rechazadas:

 repo forall -c '...."$variable" ' repo forall -c " '....$variable' " " repo forall -c '....$variable' " repo forall -c "'" ....$variable "'" 

Si sustituyo el valor en lugar de la variable, el comando se ejecuta muy bien.

Por favor, dime dónde me estoy equivocando.

Dentro de las comillas simples todo se conserva literalmente, sin excepción.

Eso significa que debe cerrar las comillas, insertar algo y luego volver a ingresar.

 'before'"$variable"'after' 'before'"'"'after' 'before'\''after' 

Como puede verificar, cada una de las líneas anteriores es una sola palabra para el shell. La concatenación de cadenas se hace simplemente por yuxtaposición. Las comillas (comillas simples o dobles, dependiendo de la situación) se utilizan para deshabilitar la interpretación de varios caracteres especiales, como espacios en blanco, $ ; … Para obtener un buen tutorial sobre las citas, ver la respuesta de Mark Reed. También relevante: ¿Qué personajes deben escaparse en bash?

No concatenar cadenas interpretadas por un shell

Debería evitar crear comandos de shell mediante la concatenación de variables. Esta es una mala idea similar a la concatenación de fragmentos de SQL (inyección de SQL).

Por lo general, es posible tener marcadores de posición en el comando y suministrar el comando junto con variables para que el destinatario pueda recibirlos de la lista de argumentos de invocación.

Por ejemplo, lo siguiente es muy inseguro. NO HAGAS ESTO

 script="echo \"Argument 1 is: $myvar\"" /bin/sh -c "$script" 

Si el contenido de $myvar no es de confianza, aquí hay un exploit:

 myvar='foo"; echo "you were hacked' 

En lugar de la invocación anterior, use argumentos posicionales. La siguiente invocación es mejor, no es explotable:

 script='echo "arg 1 is: $1"' /bin/sh -c "$script" -- "$myvar" 

Tenga en cuenta el uso de marcas individuales en la asignación al script , lo que significa que se toma literalmente, sin expansión de variables ni ninguna otra forma de interpretación.

Al comando repo no le importa qué tipo de citas obtiene. Si necesita expansión de parámetros, use comillas dobles. Si eso significa que terminas teniendo que hacer una barra diagonal inversa en una gran cantidad de cosas, utiliza comillas simples para la mayor parte, y luego sal de ellas y pasa al doble para la parte donde necesitas que la expansión ocurra.

 repo forall -c 'literal stuff goes here; '"stuff with $parameters here"' more literal stuff' 

Explicación a continuación, si estás interesado.

Cuando ejecuta un comando desde el shell, lo que ese comando recibe como argumentos es una matriz de cadenas terminadas en nulo. Esas cadenas pueden contener absolutamente cualquier carácter no nulo.

Pero cuando el shell construye ese conjunto de cadenas desde una línea de comandos, interpreta algunos caracteres especialmente; esto está diseñado para hacer comandos más fáciles (de hecho, posibles) para escribir. Por ejemplo, los espacios normalmente indican el límite entre cadenas en la matriz; por esa razón, los argumentos individuales a veces se llaman “palabras”. Pero un argumento puede, no obstante, tener espacios en él; solo necesitas una forma de decirle al caparazón que es lo que quieres.

Puede usar una barra inclinada invertida delante de cualquier carácter (incluido el espacio u otra barra inclinada invertida) para indicarle al intérprete que trate ese carácter literalmente. Pero mientras puedes hacer algo como esto:

echo \"Thank\ you.\ \ That\'ll\ be\ \$4.96,\ please,\"\ said\ the\ cashier

… puede ser tedioso. Entonces, el shell ofrece una alternativa: comillas. Estos vienen en dos variedades principales.

Las comillas dobles se llaman “citas de agrupamiento”. Impiden que se expandan los comodines y los alias, pero principalmente son para incluir espacios en una palabra. Otras cosas como la expansión de parámetros y comandos (el tipo de cosas señaladas por un $ ) aún ocurren. Y, por supuesto, si quiere una comilla doble literal dentro de comillas dobles, tiene que hacer una barra invertida:

echo "\"Thank you. That'll be \$4.96, please,\" said the cashier"

Las comillas simples son más draconianas. Todo entre ellos se toma de forma completamente literal, incluidas las barras diagonales inversas. No hay absolutamente ninguna forma de obtener una comilla simple literal dentro de comillas simples.

Afortunadamente, las comillas en el shell no son delimitadores de palabras ; por sí mismos, no terminan una palabra. Puede entrar y salir de las comillas (o entre diferentes tipos de citas) dentro de la misma palabra para obtener el resultado deseado:

echo '"Thank you. That'\''ll be $4.96, please," said the cashier'

Así que eso es más fácil, muchas menos barras invertidas, aunque la secuencia de comillas simples, comillas invertidas, literales simples, abiertas comillas simples requiere cierto tiempo para acostumbrarse.

Las shells modernas han agregado otro estilo de cotización no especificado por el estándar POSIX, en el que la comilla simple principal está precedida por un signo de dólar. Las cadenas así citadas siguen convenciones similares a los literales de cadena en el lenguaje de progtwigción ANSI C, y por lo tanto, a veces se denominan “cadenas ANSI” y el par $'' “Citas ANSI”. Dentro de tales cadenas, ya no se aplica el consejo anterior sobre las barras invertidas tomadas literalmente. En su lugar, se vuelven especiales de nuevo, no solo puede incluir una comilla simple literal o barra invertida al anteponer una barra inclinada invertida, sino que el shell también expande los caracteres de escape ANSI C (como \n para una nueva línea, \t para tabulación, y \xHH para el personaje con código hexadecimal HH ). De lo contrario, sin embargo, se comportan como cadenas de una sola cita: no se produce la sustitución de parámetros o comandos:

 echo $'"Thank you. That\'ll be $4.96, please," said the cashier' 

Lo importante a tener en cuenta es que la cadena única recibida como argumento para el comando echo es exactamente la misma en todos estos ejemplos. Después de que el shell finaliza el análisis de una línea de comando, no hay forma de que el comando se ejecute para indicar cómo se citó. Incluso si quisiera.

EDITAR: (según los comentarios en cuestión 🙂

He estado investigando esto desde entonces. Tuve la suerte de tener un repository por ahí. Todavía no está claro si necesita encerrar sus comandos entre comillas simples por la fuerza. Revisé la syntax del repository y no creo que sea necesario. Podría utilizar comillas dobles alrededor de su comando, y luego usar las comillas simples y dobles que necesite dentro, siempre que escape las dobles.

Debajo está lo que funcionó para mí:

QUOTE="'" hive -e "alter table TBL_NAME set location $QUOTE$TBL_HDFS_DIR_PATH$QUOTE"

¿Esto funciona para tí?

 eval repo forall -c '....$variable' 

Las variables pueden contener comillas simples.

 myvar=\'....$variable\' repo forall -c $myvar