bash comando multi linea con commenti dopo il carattere di continuazione


29

Ritenere

echo \ # this is a comment
foo

Questo da:

$ sh foo.sh
 # this is a comment
foo.sh: line 2: foo: command not found

Dopo alcune ricerche sul web, ho trovato una soluzione di DigitalRoss sul sito sorella Stack Overflow. Quindi uno può fare

echo `: this is a comment` \
foo

o in alternativa

echo $(: this is a comment) \
foo

Tuttavia, DigitalRoss non ha spiegato perché queste soluzioni funzionano. Gradirei una spiegazione. Ha risposto con un commento:

C'era un gotocomando shell che si ramificava su etichette specificate come :qui. Non gotoc'è più ma puoi ancora usare la : whateversintassi ... :è una specie di commento analizzato ora.

Ma vorrei maggiori dettagli e contesto, inclusa una discussione sulla portabilità.

Certo, se qualcuno ha altre soluzioni, anche questo sarebbe buono.

Vedi anche la domanda precedente Come commentare i comandi multilinea negli script di shell? .


Porta a casa il messaggio dalla discussione qui sotto. L' `: this is a comment`è solo una sostituzione di comando. L'output di : this is a commentnon è nulla e viene sostituito al posto di `: this is a comment`.

Una scelta migliore è la seguente:

echo `# this is a comment` \
foo

Risposte:


25

I commenti terminano alla prima riga (vedere la regola di riconoscimento dei token di shell 10 ), senza consentire le righe di continuazione , quindi questo codice ha foouna riga di comando separata:

echo # this is a comment \
foo

Per quanto riguarda la tua prima proposta, la barra rovesciata non è seguita da una nuova riga, stai solo citando lo spazio: è equivalente a

echo ' # this is a comment'
foo

$(: this is a comment)sostituisce l'output del comando : this is a comment. Se l'output di quel comando è vuoto, questo è effettivamente un modo molto confuso per inserire un commento nel mezzo di una riga.

Non c'è magia in corso: :è un normale comando, l' utilità dei due punti , che non fa nulla. L'utilità due punti è utile soprattutto quando la sintassi della shell richiede un comando ma non hai nulla da fare.

# Sample code to compress files that don't look compressed
case "$1" in
  *.gz|*.tgz|*.bz2|*.zip|*.jar|*.od?) :;; # the file is already compressed
  *) bzip2 -9 "$1";;
esac

Un altro caso d'uso è un linguaggio per impostare una variabile se non è già impostata.

: "${foo:=default value}"

L'osservazione su goto è storica. L'utilità del colon risale a prima ancora della shell Bourne , fino alla shell Thompson , che aveva un'istruzione goto . I due punti allora significavano un'etichetta; i due punti sono una sintassi abbastanza comune per le etichette goto (è ancora presente in sed ).


Va bene, ho capito. Esistono modi migliori per inserire commenti in questo contesto di cui sei a conoscenza?
Faheem Mitha,

3
@FaheemMitha Come raccomandato nel thread che citi: spezza il tuo comando in blocchi gestibili e commenta ogni blocco. Se il tuo comando è così complicato da aver bisogno di un commento nel mezzo, è tempo di semplificarlo!
Gilles 'SO- smetti di essere malvagio' il

1
Bene, il comando in questione ha molti argomenti ... Sta convertendo un sacco di file video in un unico file. Non vedo un modo diretto per semplificarlo. Forse creare un elenco di qualche tipo e passarlo come argomento? Immagino che potrebbe essere un'altra domanda.
Faheem Mitha,

@FaheemMitha Esempio: make_FINDuno script quickie che crea un lungo elenco di argomenti a find. Qui, la motivazione per costruirlo pezzo per pezzo è che ogni pezzo proviene dal corpo di un anello, ma lo stesso stile consente di commentare ogni pezzo.
Gilles 'SO- smetti di essere malvagio' il

1
Grazie per l'esempio Il mio esempio è fondamentalmente solo un lungo elenco di nomi dati come argomenti a un comando. Voglio commenti allegati ad ogni nome, perché questo è il modo più semplice per me di mantenere il contesto. Non vedo alcun modo ovvio per spezzare il comando in pezzi e, se lo facessi, potrebbe allungarlo ancora di più ed è già abbastanza lungo.
Faheem Mitha,

6

È possibile ottenere ciò utilizzando array Bash, ad es

#!/bin/bash
CMD=(
  echo  # this is a comment
  foo
  )

"${CMD[@]}"

Ciò definisce un array $CMDe quindi lo espande. Una volta espansa viene valutata la linea risultante, quindi in questo caso echo fooviene eseguita.

Il testo tra (e )definisce l'array ed è soggetto alla solita sintassi bash, quindi tutto su una riga dopo #viene ignorato.

Nota sulla conservazione degli spazi bianchi citati

${CMD[@]}si espande in un'unica stringa che è la concatenazione di tutti gli elementi, separati da uno spazio. Una volta espanso, Bash analizzava la stringa in token nel solito modo (vedi $ IFS ), che spesso non è quello che vogliamo.

Al contrario, se l'espansione è racchiusa tra virgolette, ovvero "${CMD[@]}"ogni elemento dell'array viene conservato. Considera la differenza tra hello world second iteme "hello world" "second item".

Esempio illustrativo:

# LIST=("hello world" "second item")

# for ITEM in ${LIST[@]}; do echo $ITEM; done
hello
world
second
item

# for ITEM in "${LIST[@]}"; do echo $ITEM; done
hello world
second item

Grazie per la risposta, @RobM. Quindi, il tuo suggerimento è di includere i commenti / meta-informazioni sugli elementi nell'elenco nella matrice bash stessa? Questa domanda sarebbe probabilmente più utile se avessi incluso un esempio del tipo di comando che stavo cercando di usare. Non so perché non l'ho fatto.
Faheem Mitha,

Bah, risulta che non ho letto attentamente la tua domanda. Pensavo stessi facendo la domanda a cui ti sei collegato. Ops. :)
RobM

${CMD[@]}non si espande in una singola stringa. - Si espande in molte stringhe: non solo suddivise per ciascun elemento dell'array, ma anche su spazi bianchi in ciascun elemento. (
Vale a

4

Non farlo $(: comment). Questo non è un commento - è una subshell - un altro processo di shell completamente nuovo per la maggior parte delle shell. Il tuo obiettivo è quello di fare meno w / il tuo input, non di più, che è ciò che farebbe, anche se è inutile.

Puoi invece fare ...

printf '<%s>\n' some args here ${-##*"${--

                my long comment block

                }"}  and "more ${-##*"${--

                and another one in the
                middle of the quoted string
                there shouldn\'t b\e any special &
                (character) `echo issues here i hope >&2`
                basically anything that isn\'t a close \}
                $(echo the shell is looking for one >&2)
                }$(echo "}'"\" need backslash escaping >&2
                                )${-##*${--

                nesting is cool though

             }}"}here too"

}'" need backslash escaping
<some>
<args>
<here>
<and>
<more here too>

Fondamentalmente quello che sta succedendo lì è che la shell sta facendo una sostituzione. Sostituisce il valore del parametro shell speciale $-due volte ogni volta. È comunque una stringa corta, ma è sempre impostata - e quindi la sostituzione interna - che viene interpretata come un modello per strapparsi dall'esterno - non si espande nei contenuti tra parentesi quando uso il -modulo di espansione.

Qui:

bash -x <<""
printf %s\\n '${-##*"'${-- a set param doesn\'t expand to this optional text }'"}'

+ printf '%s\n' '${-##*"hxB"}'
${-##*"hxB"}

Vedere? Quindi si è appena espanso due volte. Non appena la shell rileva che il parametro è impostato, tutto nel campo di espansione opzionale viene scartato, praticamente, e si espande a tutto il suo valore che viene rimosso da se stesso e quindi a nulla. Nella maggior parte delle shell non è nemmeno necessario sfuggire alle virgolette, mabash necessario.

Meglio ancora è:

COMMENT=
echo      ${COMMENT-"
           this will work the same way
           but the stripping isn\'t
           necessary because, while
           the variable is set, it is also
           empty, and so it will expand to
           its value - which is nothing
           "} this you\'ll see

this you'll see
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.