uso sentencias script programacion poner las invertida funcion control como comillas comilla argumentos bash variables syntax quoting expansion

bash - programacion - sentencias de control en shell script



¿Por qué shell ignora las comillas en los argumentos que se le pasan a través de las variables? (4)

Esto funciona como se anuncia:

# example 1 #!/bin/bash grep -ir ''hello world'' .

Esto no hace

# example 2 #!/bin/bash argumentString="-ir ''hello world''" grep $argumentString .

A pesar de que ''hello world'' está encerrado entre comillas en el segundo ejemplo, grep interpreta ''hello como un argumento y world'' como otro, lo que significa que, en este caso, ''hello será el patrón de búsqueda y world'' será la ruta de búsqueda .

De nuevo, esto solo sucede cuando los argumentos se expanden desde la variable argumentString . grep interpreta correctamente ''hello world'' como un solo argumento en el primer ejemplo.

¿Alguien puede explicar por qué esto es? ¿Existe una forma adecuada de expandir una variable de cadena que preservará la sintaxis de cada carácter de manera que sea interpretada correctamente por los comandos del shell?


Por qué

Cuando la cadena se expande, se divide en palabras, pero no se reevalúa para encontrar caracteres especiales como comillas o signos de dólar o ... Esta es la forma en que el shell se ha comportado ''siempre'', ya que el shell Bourne retrocede en 1978 o más o menos

Fijar

En bash , usa una matriz para mantener los argumentos:

argumentArray=(-ir ''hello world'') grep "${argumentArray[@]}" .

O, si es valiente / temerario, use eval :

argumentString="-ir ''hello world''" eval "grep $argumentString ."

Por otro lado, la discreción es a menudo la mejor parte del valor, y trabajar con eval es un lugar donde la discreción es mejor que la valentía. Si no tiene el control completo de la cadena que se eval (si hay alguna entrada del usuario en la cadena de comando que no se haya validado rigurosamente), entonces se está abriendo a problemas potencialmente graves.

Tenga en cuenta que la secuencia de expansiones para Bash se describe en Expansiones de Shell en el manual de GNU Bash. Observe en particular las secciones 3.5.3 Expansión de parámetros de shell, 3.5.7 División de palabras y 3.5.9 Eliminación de cotización.


Al analizar esto y otras preguntas relacionadas, me sorprende que nadie haya mencionado una subshell explícita. Para bash y otros shells modernos, puede ejecutar una línea de comando explícitamente. En bash, requiere la opción -c.

argumentString="-ir ''hello world''" bash -c "grep $argumentString ."

Funciona exactamente como el interrogador original deseado. Hay dos restricciones a esta técnica:

  1. Solo puede usar comillas simples dentro de las cadenas de comando o argumento.
  2. Solo las variables de entorno exportadas estarán disponibles para el comando

Además, esta técnica maneja la redirección y la canalización, y otros shellismos también funcionan. También puede usar los comandos internos de bash, así como cualquier otro comando que funcione en la línea de comandos, porque esencialmente le está pidiendo a un evento de subshell que lo interprete directamente como una línea de comandos. Aquí hay un ejemplo más complejo, una variante ls -l algo gratuita y compleja.

cmd="prefix=`pwd` && ls | xargs -n 1 echo /'In $prefix:/'" bash -c "$cmd"

He construido procesadores de comandos de esta manera y con matrices de parámetros. Generalmente, esta forma es mucho más fácil de escribir y depurar, y es trivial hacer eco del comando que está ejecutando. OTOH, los arreglos de parámetros funcionan bien cuando realmente tienen arreglos abstractos de parámetros, en lugar de solo querer una variante de comando simple.


Cuando coloca caracteres de comillas en variables, simplemente se convierten en literales simples (consulte mywiki.wooledge.org/BashFAQ/050 ; gracias @tripleee por señalar este enlace)

En su lugar, intente usar una matriz para pasar sus argumentos:

argumentString=(-ir ''hello world'') grep "${argumentString[@]}" .


Dentro de las comillas dobles, las comillas simples pierden su significado especial, por lo que son simples personajes ordinarios. Para que funcione, usa algo como eval :

eval grep $argumentString .

que debe volver a ensamblar correctamente la línea de comandos de las cadenas concatenadas.