Cómo exportar correctamente variables env que contienen espacios en ZSH

Cómo exportar correctamente variables env que contienen espacios en ZSH

Agregue una variable env que contenga espacios a~/.zshrc

export SKIP="-Dskip1 -Dskip2"
export SKIP_NO_SPACES="-Dskip3"

Intenta usarlo

set -x  
mvn $SKIP $SKIP_NO_SPACES

El comando que se está ejecutando es en realidad

+-zsh:108> mvn '-Dskip1 -Dskip2' -Dskip3

Sólo se cita la variable que contiene espacios.

¿Cómo evitar que se agreguen comillas simples?

Si no se utilizan comillas ni escapes, se produce un error al abrir el shell:

/.zshrc:export:11: no válido en este contexto: -Dskip2

Respuesta1

Creo que estás (en cierto modo) abusando de las variables de entorno aquí. Ese espacio es parte de la variable, y cuando realiza la expansión de parámetros con $, simplemente pasará ese espacio al comando. mvnobtiene un argumento: su contenido variable.

Como observa correctamente Kamil Maciorowski en un comentario, esto es específico de Zsh. Lo que propones funcionaría en Bash si omitieras las comillas:

bash-5.0$ export test="foo bar"
bash-5.0$ ls $test
ls: bar: No such file or directory
ls: foo: No such file or directory
bash-5.0$ ls "$test"
ls: foo bar: No such file or directory

Una alternativa para hacer lo que quieres sería unaalias global(también una característica de Zsh, no de Bash), que se puede sustituir en cualquier parte de una línea. Mantiene sus espacios:

$ alias -g SKIP="-Dskip1 -Dskip2"
$ alias -g SKIP2="-Dskip3"
$ mvn SKIP SKIP2

Esto simplemente "añadirá" la cadena dondequiera que se agregue.

Respuesta2

El problema no tiene nada que ver con citar o escapar, tiene que ver con la división de palabras (o la falta de ella). En la mayoría de los shells, si usa una variable que contiene espacios en blanco sin ponerle comillas dobles, el valor se dividirá en "palabras" según ese espacio en blanco. Entonces, si la variable SKIPse establece en -Dskip1 -Dskip2(tenga en cuenta que no hay comillas ni escapes en este valor) y la usa como mvn $SKIP, se vuelve equivalente a mvn "-Dskip1" "-Dskip2".

Pero zsh no es la mayoría de los shells y (de forma predeterminada) no divide palabras. Si la variable está configurada en -Dskip1 -Dskip2, eso es lo que se pasa al comando,como un solo argumento. Las comillas que ve en el set -xresultado no están realmente allí, solo se agregan para dejar explícito que la cadena completa se pasa como un único argumento.

Hay un par de formas de decirle a zsh que desea realizar la división de palabras. Puedes usar el =modificador en la expansión:

mvn ${=SKIP}

O puede activar la división tipo sh para todas las expansiones con una opción de shell:

setopt shwordsplit
mvn $SKIP

...sin embargo, esto puede tener efectos secundarios desagradables, como una división inesperada de cosas que pensaba que eran argumentos únicos y la expansión de cualquier cosa que parezca un comodín de nombre de archivo en una lista de nombres de archivo. La división de palabras del Shell tiende a causar errores, por lo que está deshabilitada de forma predeterminada en zsh. La forma realmente adecuada de almacenar varias cadenas en una variable es utilizar una matriz en lugar de una variable simple:

SKIP=(-Dskip1 -Dskip2)
mvn "${SKIP[@]}"

Esto funcionará en zsh, bash y algunos otros shells (pero no en aquellos que no admiten matrices). Sin embargo, no puedes exportar matrices desde zsh (bueno, puedes ejecutarlo export SKIP, pero no hace nada). Las matrices son una característica del shell, mientras que las variables de entorno son una característica del sistema operativo que solo admite cadenas simples. ¿Realmente necesitas exportar estas opciones o simplemente las estás usando dentro del shell?

Respuesta3

Aparecen comillas simples en la línea de diagnóstico impresa debido a set -x. No llegan a mvn. El contenido de la variable ( -Dskip1 -Dskip2) llega mvncomo un argumento único porque zshno es necesario citar variables rígidamente (lo hace en shells compatibles con POSIX).

El problema parece estar en la forma en que zshse expanden las variables. Creo que quieres que se produzca la división de palabras cuando $SKIPno está entre comillas (como sucede en otros caparazones) pero en zshél normalmente no ocurre.

utilizar ${=SKIP}paraactivar la división de palabras después de la sustitución.

Ejemplo:

% SKIP='-Dskip1 -Dskip2' 
% printf "%s\n" $SKIP   
-Dskip1 -Dskip2
% printf "%s\n" ${=SKIP}
-Dskip1
-Dskip2
% 

información relacionada