Cómo usar variables dentro de comillas simples

11

Tengo una aplicación que toma como entrada los atributos en comillas dobles incrustadas en comillas simples. Tome por ejemplo este comando correcto:

command -p 'cluster="cl1"'

Para automatizarlo, creé un archivo bash usando $CLUSTERcomo variable. ¿Cómo debe ser mi comando? En otras palabras, ¿qué debo poner en lugar de cl1?

Tenga en cuenta que, si modifiqué el comando anterior, no será aceptado. Por ejemplo: command -p "cluster=cl1"no se acepta

Mohamad-Jaafar NEHME
fuente
2
CLUSTER='"cl1"'; command -p "cluster=$CLUSTER"
mikeserv
Otra posibilidad (si prefiere almacenar clisin comillas dentro de la CLUSTERvariable):CLUSTER='cl1'; command -p 'cluster="'"$CLUSTER"'"'
jimmij
Finalmente, encontré la respuesta correcta. Gracias @ jimmij.
Mohamad-Jaafar NEHME

Respuestas:

8

Parece que su comando está configurando variables de entorno basadas en argumentos dados en la línea de comandos. Puede ser que puedas hacer:

CLUSTER=cl1; cluster=$CLUSTER command

... y establecer su entorno para ello en la invocación.

De lo contrario, las citas de shell suelen delimitar argumentos o escapar de otros caracteres especiales de shell de la interpretación de shell. Puede contener (y por lo tanto escapar) diferentes tipos de comillas de shell dentro de otros tipos basados ​​en varias reglas:

  • "''''" - una cadena entre comillas puede contener cualquier cantidad de comillas.
  • "\""- una \barra invertida puede escapar de una "comilla dentro de una "cadena entre comillas.
    • En este contexto, \\también se escapa una barra diagonal inversa, el \$token de expansión y las \nlíneas de contacto como se indica a continuación, pero por lo demás se trata literalmente.
  • "${expand} and then some"- una cadena entre comillas puede contener una $expansión de shell interpretada .
  • '"\'- una 'cadena entre comillas puede contener cualquier carácter que no sea una 'comilla.
  • \- una barra invertida sin comillas escapará a cualquier carácter siguiente para una interpretación literal, incluso otra barra invertida, excepto una línea \nelectrónica.
    • En un \\ncaso ewline, tanto la \barra diagonal inversa como la línea \new se eliminan por completo del comando interpretado resultante.
  • ${parameter+expand "$parameter"}- las comillas resultantes de una expansión de shell casi nunca sirven como marcadores delimitadores, excepto algunos casos especiales. No me aventuraré a describirlos más aquí.

Considero extraño que cualquier aplicación interprete comillas en sus argumentos de línea de comandos. Tal práctica no tiene mucho sentido en que, al menos para los shells, el propósito principal de una cita es generalmente delimitar un argumento. Sin embargo, en la invocación, los argumentos siempre están delimitados con \0NULcaracteres y, por lo tanto, una cita no puede servir de mucho.

Incluso un shell normalmente solo se molestará en interpretar citas en uno de sus argumentos de invocación cuando se lo llama con un -cinterruptor, lo que denota que su primer operando es en realidad un script de shell que debe ejecutarse al invocarlo. Este es un caso de entrada dos veces evaluada .

Dicho todo esto, puede hacer varias cosas para pasar comillas literales a través de argumentos en la línea de comandos. Por ejemplo:

CLUSTER='"cl1"'; command -p "cluster=$CLUSTER"

Como señalé en un comentario antes, puede contener las "comillas dentro de una expansión que se "cita.

CLUSTER=cl1; command -p "cluster=\"$CLUSTER\""

Puede escapar "con una \barra diagonal inversa dentro de la "cadena entre comillas.

CLUSTER=cl1; command -p cluster='"'"$CLUSTER"'"'

Puede alternar y concatenar estilos de citas para llegar al resultado final deseado como @jimmij observa arriba .

CLUSTER=cl1; ( set -f; IFS=; command -p cluster=\"$CLUSTER\" )

Puede deshabilitar tanto la generación como la $IFSdivisión de nombres de archivo , evitando así la necesidad de citar el $expansion, y así solo citar las citas. Esto es probablemente exagerado.

Por último, hay otro tipo de shell-quote que podría usarse. Como señalé antes, la sh -c "$scriptlet"forma de invocación de shell a menudo se usa para proporcionar un script de shell en la línea de comandos. Sin $scriptletembargo, cuando se complica, como cuando las comillas deben contener otras comillas, a menudo puede ser ventajoso usar un documento aquí y en su sh -slugar, donde el shell tiene instrucciones específicas de asignar todos los operandos siguientes a los parámetros posicionales como lo haría en un -ccaso y sin embargo también tomar su guión de stdin.

Si su comando debe interpretar las comillas de esta manera, lo consideraría mejor si pudiera hacerlo en una entrada de archivo. Por ejemplo:

CLUSTER=cl1
command --stdin <<-SCRIPT
    cluster="$CLUSTER"
SCRIPT

Si no cita el delimitador de a, <<here-documententonces todos sus contenidos se tratan casi exactamente como si fueran "comillas blandas, excepto que "las comillas dobles no se tratan especialmente. Y así, si ejecutamos lo anterior con en su catlugar:

CLUSTER=cl1
cat <<-SCRIPT
        cluster="$CLUSTER"
SCRIPT

... imprime ...

cluster="cl1"
mikeserv
fuente
1

Como escribió mikeserv :

 CLUSTER='"cl1"'; command -p "cluster=$CLUSTER" 

"Cita doble" cada literal que contiene espacios / metacaracteres y cada expansión: "$var", "$(command "$var")", "${array[@]}", "a & b". Utilizar 'single quotes'para el código o literal $'s: 'Costs $5 US', ssh host 'echo "$HOSTNAME"'. Ver
http://mywiki.wooledge.org/Quotes
http://mywiki.wooledge.org/Arguments
http://wiki.bash-hackers.org/syntax/words

Gilles Quenot
fuente
Tienes razón, gracias. Pero, ¿qué pasa con la automatización? ¿Y si quiero leer CLUSTER? Volveré a quedar atrapado con el mismo problema: variable dentro de comillas simples
Mohamad-Jaafar NEHME
@Moi: la automatización no es un gran problema, aunque tendrá que desinfectar la variable de "caracteres; solo el primero y el último deberían ser suficientes. El verdadero problema aquí es su aplicación interpretando citas en un argumento. Interpretar una cita en un argumento casi nunca es una buena idea porque una cita solo debe ser un medio para delimitar un argumento, y en la invocación esa delimitación es manejada por \0NULs en todos los casos. ¿Probablemente hay una mejor manera de pasar la información que desea a la aplicación? ¿Como a command --'script=/path/to/some/file'o algo?
mikeserv