¿Puede un script de shell imprimir su argumento, citado como lo escribiría en el indicador de shell?

9

En un script de shell, entiendo que se "$@"expande a los argumentos del script, citándolos según sea necesario. Por ejemplo, esto reenvía los argumentos del script a gcc:

gcc -fPIC "$@"

Sin <<<embargo, cuando utilizo la sintaxis bash pass-to-stdin , "@$"no funciona como lo esperaría.

#!/bin/bash
cat <<< "$@"

Llamar al guión como ./test.sh foo "bar baz"da

foo bar baz

Yo esperaría

foo "bar baz"

¿Hay alguna manera de escribir un script de shell que imprima sus argumentos como los escribiría en el indicador de shell? Por ejemplo: una pista sobre qué comando usar a continuación, incluidos los argumentos del script en la pista.

Alex Jasmin
fuente

Respuestas:

5

Bueno, se "$@"expande a la lista de parámetros posicionales, un argumento por parámetro posicional.

Cuando tu lo hagas:

set '' 'foo bar' $'blah\nblah'
cmd "$@"

cmdse invoca con esos 3 argumentos: la cadena vacía foo bary blah<newline>blah. El shell llamará a la llamada del execve()sistema con algo como:

execve("/path/to/cmd", ["cmd", "", "foo bar", "blah\nblah"], [envvars...]);

Si desea reconstruir una línea de comando de shell (que es código en el lenguaje de shell) que reproduciría esa misma invocación, podría hacer algo como:

awk -v q="'" '
  function shellquote(s) {
    gsub(q, q "\\" q q, s)
    return q s q
  }
  BEGIN {
    for (i = 1; i < ARGC; i++) {
      printf "%s", sep shellquote(ARGV[i])
      sep = " "
    }
    printf "\n"
  }' cmd "$@"

O con zsh, pidiendo diferentes tipos de citas:

set '' 'foo bar' $'blah\nblah'
$ print -r -- cmd "${(q)@}"
cmd '' foo\ bar blah$'\n'blah
$ print -r -- cmd "${(qq)@}"
cmd '' 'foo bar' 'blah
blah'
$ print -r -- cmd "${(qqq)@}"
cmd "" "foo bar" "blah
blah"
$ print -r -- cmd "${(qqqq)@}"
cmd $'' $'foo bar' $'blah\nblah'

O con zsh, basho ksh93(aquí para bash, YMMV con otros proyectiles):

$ set '' 'foo bar' $'blah\nblah'
$ printf cmd; printf ' %q' "$@"; printf '\n'
cmd '' foo\ bar $'blah\nblah'

También puede usar la opción xtrace del shell que hace que el shell imprima lo que va a ejecutar:

(PS4=; set -x; : cmd "$@")
: cmd '' 'foo bar' 'blah
blah'

Arriba, ejecutamos el :comando no-op con cmdy los parámetros posicionales como argumento. Mi caparazón los imprimió de una manera agradable y citada, adecuada para reintroducir en el caparazón. No todas las conchas hacen eso.

Stéphane Chazelas
fuente
5
`"$@"` expands to the script arguments, quoting them as needed

No, esto no es lo que pasa. Llamar a un programa toma una lista de argumentos, cada argumento es una cadena. Cuando se ejecuta el programa de shell ./test.sh foo "bar baz", esto construye una llamada con tres argumentos: ./test.sh, foo, y bar baz. (El argumento cero es el nombre del programa; esto permite que los programas sepan con qué nombre se llaman). Las citas son una característica del shell, no una característica de las llamadas a programas. El shell construye esta lista cuando realiza la llamada.

"$@"copia directamente la lista de argumentos pasados ​​al script o función a la lista de argumentos en la llamada donde se usa. No hay citas involucradas ya que no se realiza un análisis de shell en esas listas.

En cat <<< "$@", está utilizando "$@"en un contexto donde se requiere una sola cadena. El <<<operador` requiere una cadena, no una lista de cadenas. En este contexto, bash toma los elementos de la lista y los une con un espacio intermedio.

Para la depuración de script, si ejecuta set -x( set +xpara desactivar), eso activa un modo de rastreo donde cada comando se imprime antes de ejecutarse. En bash, esa traza tiene comillas que hacen posible pegar el comando nuevamente en un shell (esto no es cierto para cada shimplementación).

Si tiene una cadena y desea convertirla en una sintaxis de origen de shell que se analice nuevamente en la cadena original, puede rodearla con comillas simples y reemplazar cada comilla dentro de la cadena con '\''.

for x do
  printf %s "'${x//\'/\'\\\'\'}' "
done
echo

La sintaxis de reemplazo de cadena es específica de ksh93 / bash / zsh / mksh. En sh simple, debe recorrer la cadena.

for raw do
  quoted=
  while case "$raw" in *\'*) true;; *) false;; esac; do
    quoted="$quoted'\\''${raw%%\'*}"
    raw="${raw#*\'}"
  done
  printf %s "'$quoted$raw' "
done
echo
Gilles 'SO- deja de ser malvado'
fuente
2

"$@" se expande a los argumentos del script, citándolos según sea necesario

Especie de. Para fines prácticos, debe estar lo suficientemente cerca, y el manual de referencia dice que"$@" is equivalent to "$1" "$2" ...

Entonces, con los dos parámetros fooy bar baz, estos serían similares:

echo "$@"
echo "$1" "$2"
echo "foo" "bar baz"

(Excepto que si los parámetros contuvieran caracteres especiales en lugar de cadenas simples, no se expandirían nuevamente después de expandir $@y $1...)

Pero incluso si consideramos $@reemplazado por los parámetros entre comillas, las comillas no estarían allí para echover, de manera similar a eso gcctampoco se obtienen las comillas.

<<<es una pequeña excepción a la regla "$@"== "$1" "$2" ..., se menciona explícitamente que The result is supplied as a single string to the command on its standard inputdespués de pasar por la expansión de parámetros y variables y la eliminación de comillas, entre otros. Entonces, como de costumbre, <<< "foo"solo da foocomo entrada, de la misma manera somecmd "foo"solo da foocomo argumento.

Llamar al guión como ./test.sh foo "bar baz"[...] esperaría foo "bar baz"

Si las citas permanecieran, todavía tendría que ser así "foo" "bar baz". El shell o cualquier comando en ejecución no tiene idea de cuál era la cita cuando se ejecutó el comando. O si incluso hubo alguna cita de la que hablar, la llamada al sistema solo recibe una lista de cadenas terminadas en nulo y las citas son solo una característica del lenguaje shell. Otros idiomas pueden tener otras convenciones.

ilkkachu
fuente
0

Una solución alternativa para bash

q='"'; t=( "${@/#/$q}" ); u=( "${t[@]/%/$q}" ); echo ${u[@]}

Bash no admite sustituciones anidadas, así que gracias a /programming/12303974/assign-array-to-variable#12304017 por mostrar cómo reasignar una matriz. Consulte man bash( https://linux.die.net/man/1/bash ) para obtener detalles sobre matrices, expansión y sustitución de patrones (bajo expansión de parámetros).

Análisis

Bash coloca los parámetros de la línea de comandos como una matriz en $@

q tiene el carácter de cita

Las comillas dobles alrededor de la expansión de parámetros ${ ... }conservan los parámetros individuales como elementos distintos y al envolverlos le ( )permite asignarlos como una matriz a una variable.

/#/$qen un parámetro, la expansión sustituye el comienzo del patrón (como regex ^) con el carácter entre comillas.

/%/$qen un parámetro, la expansión sustituye el final del patrón (como regex $) con el carácter entre comillas.

Caso de uso: consultar MySQL para obtener una lista de direcciones de correo electrónico desde la línea de comandos

Hay algunos cambios en las declaraciones anteriores para usar un carácter de cita diferente, agregar comas entre los parámetros y quitar la coma final. Y, por supuesto, estoy siendo malo al poner la contraseña en la invocación de mysql. Entonces demándame.

q="'"; t=( "${@/#/$q}" ); u="${t[@]/%/$q,}"; v="u.email in( ${u%,} )"
mysql -uprod_program -h10.90.2.11 -pxxxxxxxxxxxx my_database <<END
select ...
from users u
join ....
where $v # <<<<<<<<<<<<<<<<<< here is where all the hard work pays off :-)
group by user_id, prog_id
;
END
Jeff
fuente
Solo tenga en cuenta que las comillas dobles no son muy útiles para proteger las barras diagonales inversas, las $expansiones y otras comillas dobles. Es por eso que las otras respuestas usan comillas simples mientras van a algunas longitudes para manejar comillas simples dentro de la cadena, o usan las propias características del shell para producir una copia citada de la cadena.
ilkkachu
@ilkkachu Debidamente notado! Es por eso (entre otras razones) que voté por todas las respuestas anteriores. También por qué agregué este caso de uso. Ojalá lo perfecto no sea enemigo de lo bueno.
Jeff