Lectura de argumentos citados / escapados correctamente de una cadena

Me encuentro con un problema al pasar un argumento a un comando en un script de Bash.

poc.sh:

#!/bin/bash ARGS='"hi there" test' ./swap ${ARGS} 

intercambiar:

 #!/bin/sh echo "${2}" "${1}" 

La salida actual es:

 there" "hi 

Cambiando solo poc.sh (ya que creo que swap hace lo que quiero que sea correcto), ¿cómo obtengo poc.sh para pasar “hola allí” y probar como dos argumentos, con “hola ahí” sin comillas?

Algunas palabras introductorias

Si es posible, no use cadenas entre comillas como formato de entrada.

  • Es difícil analizar de forma coherente: diferentes shells tienen diferentes extensiones, y diferentes implementaciones que no son de shell implementan diferentes subconjuntos (ver los deltas entre shlex y shlex continuación).
  • Es difícil generar programáticamente. ksh y bash tienen printf '%q' , que generará una cadena cotizada por el shell con contenidos de una variable arbitraria, pero no existe equivalente en el estándar POSIX sh.
  • Es fácil analizar mal . Muchas personas que consumen este formato usan eval , que tiene importantes problemas de seguridad.

Las secuencias delimitadas por NUL son una práctica mucho mejor, ya que pueden representar con precisión cualquier matriz de shell o lista de argumentos posibles sin ambigüedad alguna.


xargs, con bashisms

Si obtiene su lista de argumentos de una fuente de entrada generada por el ser humano mediante el uso de comillas de shell, puede considerar utilizar xargs para analizarlo. Considerar:

 array=( ) while IFS= read -r -d ''; do array+=( "$REPLY" ) done < <(xargs printf '%s\0' <<<"$ARGS") swap "${array[@]}" 

... colocará el contenido analizado de $ARGS en la matriz de array . Si en cambio deseaba leer un archivo, sustituya de por <<<"$ARGS" .


xargs, POSIX-obediente

Si está intentando escribir un código que cumpla con POSIX sh, esto se vuelve más complicado. (Voy a asumir la entrada de archivos aquí para una complejidad reducida):

 # This does not work with entries containing literal newlines; you need bash for that. run_with_args() { while IFS= read -r entry; do set -- "$@" "$entry" done "$@" } xargs printf '%s\n'  

Estos enfoques son más seguros que ejecutar xargs ./swap medida en que generará un error si hay más o más argumentos de los que pueden acomodarse, en lugar de ejecutar argumentos en exceso como comandos separados.


Python shlex - en lugar de xargs - con bashisms

Si necesita un análisis sintáctico POSIX más preciso que los implementos xargs , considere usar el módulo Python shlex lugar:

 shlex_split() { python -c ' import shlex, sys for item in shlex.split(sys.stdin.read()): sys.stdout.write(item + "\0") ' } while IFS= read -r -d ''; do array+=( "$REPLY" ) done < <(shlex_split <<<"$ARGS") 

Las comillas incorporadas no protegen el espacio en blanco; ellos son tratados literalmente Usa una matriz en bash :

 args=( "hi there" test) ./swap "${args[@]}" 

En el shell POSIX, estás atascado usando eval (que es por lo que la mayoría de las shells admiten matrices).

 args='"hi there" test' eval "./swap $args" 

Como de costumbre, asegúrese de conocer el contenido de $args y comprender cómo se analizará la cadena resultante antes de usar eval .

Este podría no ser el enfoque más sólido, pero es simple y parece funcionar para su caso:

 ## demonstration matching the question $ ( ARGS='"hi there" test' ; ./swap ${ARGS} ) there" "hi ## simple solution, using 'xargs' $ ( ARGS='"hi there" test' ; echo ${ARGS} |xargs ./swap ) test hi there