Uno script di shell può stampare il suo argomento, citato come li scriveresti sul prompt della shell?


9

In uno script di shell, la mia comprensione è che si "$@"espande agli argomenti dello script, citandoli secondo necessità. Ad esempio, questo inoltra gli argomenti dello script a gcc:

gcc -fPIC "$@"

Quando si utilizza la sintassi bash pass-to-stdin <<<, tuttavia, "@$"non funziona come mi aspetto.

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

Chiamare lo script come ./test.sh foo "bar baz"da

foo bar baz

Mi aspetterei

foo "bar baz"

C'è un modo per scrivere uno script di shell che stampa i suoi argomenti come se li scrivessi sul prompt della shell? Ad esempio: un suggerimento su quale comando usare successivamente, compresi gli argomenti di script nel suggerimento.

Risposte:


5

Bene, si "$@"espande all'elenco dei parametri posizionali, un argomento per parametro posizionale.

Quando lo fai:

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

cmdviene invocato con questi 3 argomenti: la stringa vuota foo bare blah<newline>blah. La shell chiamerà la execve()chiamata di sistema con qualcosa del tipo:

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

Se vuoi ricostruire una riga di comando della shell (ovvero il codice nel linguaggio della shell) che riproduca la stessa invocazione, potresti fare qualcosa del tipo:

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, chiedendo diversi tipi di citazioni:

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(qui per bash, YMMV con altre shell):

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

Puoi anche usare l'opzione xtrace della shell che fa stampare alla shell ciò che sta per eseguire:

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

Sopra, abbiamo eseguito il :comando no-op con cmde i parametri posizionali come argomento. La mia conchiglia le ha stampate in modo gradevole e adatto per essere reintegrate nella conchiglia. Non tutte le shell lo fanno.


5
`"$@"` expands to the script arguments, quoting them as needed

No, non è quello che succede. La chiamata a un programma richiede un elenco di argomenti, ogni argomento è una stringa. Quando si esegue il programma di shell ./test.sh foo "bar baz", questo costruisce una chiamata con tre argomenti: ./test.sh, foo, e bar baz. (L'argomento zeroth è il nome del programma; questo consente ai programmi di sapere con quale nome vengono chiamati.) Il quoting è una caratteristica della shell, non una caratteristica delle chiamate al programma. La shell crea questo elenco quando effettua la chiamata.

"$@"copia direttamente l'elenco di argomenti passati allo script o alla funzione nell'elenco di argomenti nella chiamata in cui viene utilizzato. Non è prevista alcuna quotazione poiché non è stato eseguito alcun analisi della shell su tali elenchi.

In cat <<< "$@", stai usando "$@"in un contesto in cui è richiesta una singola stringa. L' <<<operatore richiede una stringa, non un elenco di stringhe. In questo contesto, bash prende gli elementi dell'elenco e li unisce con uno spazio in mezzo.

Per il debug degli script, se si esegue set -x( set +xper disattivare), che attiva una modalità di traccia in cui viene stampato ciascun comando prima dell'esecuzione. In bash, quella traccia ha delle virgolette che rendono possibile incollare il comando in una shell (questo non è vero per ogni shimplementazione).

Se si dispone di una stringa e si desidera trasformarla in sintassi di origine shell che analizza nuovamente la stringa originale, è possibile racchiuderla tra virgolette singole e sostituire ogni singola virgoletta all'interno della stringa con '\''.

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

La sintassi di sostituzione della stringa è specifica per ksh93 / bash / zsh / mksh. In semplice sh, è necessario eseguire il ciclo sopra la stringa.

for raw do
  quoted=
  while case "$raw" in *\'*) true;; *) false;; esac; do
    quoted="$quoted'\\''${raw%%\'*}"
    raw="${raw#*\'}"
  done
  printf %s "'$quoted$raw' "
done
echo

2

"$@" si espande agli argomenti dello script, citandoli secondo necessità

Beh, in un certo senso. Ai fini pratici dovrebbe essere abbastanza vicino, e il manuale di riferimento lo dice"$@" is equivalent to "$1" "$2" ...

Quindi, con i due parametri fooe bar baz, questi sarebbero simili:

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

(Tranne il fatto che se i parametri contenessero caratteri speciali anziché solo stringhe semplici, non sarebbero più espansi dopo l'espansione $@e $1...)

Ma anche se consideriamo $@sostituito dai parametri tra virgolette, le virgolette non sarebbero lì per echovedere, allo stesso modo in cui gccnon ottengono nemmeno le virgolette.

<<<è un po 'di un'eccezione alla "$@"== "$1" "$2" ...regola, è esplicitamente menzionato che The result is supplied as a single string to the command on its standard inputdopo aver attraversato espansione di parametro e variabile e la rimozione citazione tra gli altri. Quindi, come al solito, <<< "foo"dà solo fooinput, allo stesso modo somecmd "foo"dà solo foocome argomento.

Chiamando la sceneggiatura come ./test.sh foo "bar baz"[...] mi sarei aspettato foo "bar baz"

Se le virgolette fossero rimaste, dovrebbe essere ancora "foo" "bar baz". La shell o qualsiasi comando in esecuzione non ha idea di quale fosse la quotazione durante l'esecuzione del comando. O se ci fosse anche qualche citazione di cui parlare, la chiamata di sistema riceve solo un elenco di stringhe con terminazione null e le virgolette sono solo una caratteristica del linguaggio shell. Altre lingue possono avere altre convenzioni.


0

Una soluzione alternativa per bash

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

Bash non supporta le sostituzioni nidificate, quindi grazie a /programming/12303974/assign-array-to-variable#12304017 per aver mostrato come riassegnare un array. Vedere man bash( https://linux.die.net/man/1/bash ) per dettagli su array, espansione e sostituzione di pattern (sotto espansione parametri).

Analisi

Bash inserisce i parametri della riga di comando come un array $@

q contiene il carattere di citazione.

Le virgolette doppie attorno all'espansione dei parametri ${ ... }mantengono i singoli parametri come elementi distinti e li avvolge in una ( )stringa che consente di assegnarli come array.

/#/$qin un parametro di espansione sostituisce l'inizio del modello (come regex ^) con il carattere di quotazione.

/%/$qin un parametro di espansione sostituisce la fine del pattern (come regex $) con il carattere di quotazione.

Caso d'uso: interrogare MySQL per un elenco di indirizzi e-mail dalla riga di comando

Esistono alcune modifiche alle istruzioni precedenti per utilizzare un carattere di virgolette diverso, aggiungere virgole tra i parametri e rimuovere la virgola finale. E ovviamente sto male mettendo la password nell'invocazione mysql. Quindi fammi causa.

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

Basta notare che la citazione con virgolette doppie non è molto utile per proteggere barre rovesciate, $espansioni e altre virgolette doppie. Ecco perché le altre risposte usano virgolette singole mentre vanno in alcune lunghezze per gestire virgolette singole all'interno della stringa o utilizzare le funzionalità della shell per produrre una copia della stringa tra virgolette.
ilkkachu,

@ilkkachu debitamente notato! Ecco perché (tra le altre ragioni) ho votato tutte le risposte precedenti. Anche perché ho aggiunto questo caso d'uso. Spero che il perfetto non sia nemico del bene.
Jeff
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.