Bash -c con parametri posizionali


15

Di solito, $0in uno script è impostato il nome dello script o qualsiasi cosa sia stata invocata (incluso il percorso). Tuttavia, se uso bashcon l' -copzione, $0è impostato sul primo degli argomenti passati dopo la stringa di comando:

bash -c 'echo $0 ' foo bar 
# foo 

In effetti, sembra che i parametri posizionali siano stati spostati, ma inclusi $0. Tuttavia shiftnella stringa di comando non influisce $0(come di consueto):

bash -c 'echo $0; shift; echo $0 ' foo bar
# foo
# foo

Perché questo comportamento apparentemente strano per le stringhe di comando? Nota che sto cercando il motivo, la logica, dietro l'implementazione di un comportamento così strano.


Si potrebbe ipotizzare che una tale stringa di comando non avrebbe bisogno del $0parametro come di solito definito, quindi per l'economia viene usata anche per argomenti normali. Tuttavia, in quel caso il comportamento di shiftè strano. Un'altra possibilità è che $0viene utilizzata per definire il comportamento dei programmi ( bashcome chiamato come sho vimchiamato come vi), ma ciò non può essere, poiché $0qui viene visualizzato solo nella stringa di comando e non dai programmi richiamati al suo interno. Non riesco a pensare ad altri usi per $0, quindi non riesco a spiegarlo.


Come nota a margine, ho visto l'uso di -per l' $0argomento come un linguaggio, come in sh -c 'foo $1 $2' - a b. In questo modo sembra abbastanza normale (una volta scoperto cosa -significa, cioè)
Volker Siegel,

È possibile anche echo 'echo the other side of this pipe globs "$@"' | sh -s -- *, se, purtroppo, $0non è in genere un parametro impostabile con l' -sopzione di tream ... Può essere utilizzato in molti degli stessi modi xargsin generale è, però. E altri ancora.
Mikeserv,

@VolkerSiegel Sarebbe stato più normale --, quindi avrebbe potuto avere la solita interpretazione di "da qui inizia gli argomenti", vista in alcuni altri programmi. Ancora una volta, ciò potrebbe confondere chi non ha familiarità con il -cpensiero di avere --effettivamente questa interpretazione.
Muru,

Risposte:


10

Ciò ti dà l'opportunità di impostare / scegliere $0quando si utilizza uno script in linea. Altrimenti, $0sarebbe solo bash.

Quindi puoi fare per esempio:

$ bash -c 'wc -c < "${1?}"' getlength foo
4
$ bash -c 'wc -c < "${1?}"' getlength bar
getlength: bar: No such file or directory
$ bash -c 'wc -c < "${1?}"' getlength
getlength: 1: parameter null or not set

Non tutte le shell erano solite farlo. La shell Bourne ha fatto. La shell Korn (e Almquist) ha scelto $1invece di passare il primo parametro . POSIX alla fine è andato per il modo in cui Bourne, quindi kshe ashderivati ritornato a che più tardi (più su quello a http://www.in-ulm.de/~mascheck/various/find/#shell ). Ciò significava che per molto tempo sh(che a seconda del sistema era basato sulla shell Bourne, Almquist o Korn), non sapevi se il primo argomento fosse andato in discussione $0o $1, quindi, per la portabilità, dovevi fare cose come:

sh -c 'echo foo in "$1"' foo foo

O:

sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txt

Per fortuna, POSIX ha specificato il nuovo comportamento in cui entra il primo argomento $0, quindi ora possiamo fare in modo portabile:

sh -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txt

Ho appena eseguito: bash -c 'echo txt files are "$@"' meaningful-arg0-for-error *.txtsu Ubuntu 14.04, bash version 4.3.11(1)-release, e ho ottenuto: txt files are *.txt. Oo
muru,

2
@muru, che è corretto (e probabilmente non hai txtfile nella directory corrente). Vedi anchebash -c 'echo "${1?}"' foo
Stéphane Chazelas il

Ah sì. Questo è un esempio praticamente utile.
Muru,

1
sh -c 'shift "$2"; echo txt files are "$@"' tentative-arg0 3 2 *.txtè fantasiosamente inventivo!
Iruvar,

Oppure usi una soluzione completamente robusta (suggerita da Stéphane Chazelas in comp.unix.shell) SHELL -c 'shift $1; command' 2 1 arg1 arg2 ... haha ...
mikeserv

3

Questo comportamento è definito da POSIX :

sh -c nome_ comando [argomento ...]

Leggi i comandi dall'operando command_string. Impostare il valore del parametro speciale 0 (vedere Parametri speciali ) dal valore dell'operando command_name e dai parametri posizionali ($ 1, $ 2 e così via) in sequenza dagli operandi degli argomenti rimanenti.

Per quanto riguarda il motivo per cui vorresti quel comportamento: questo appiana il divario tra uno script e una -cstringa. È possibile convertire direttamente tra i due senza alcun cambiamento di comportamento. Altre aree si basano sul fatto che sono identiche.

È anche in linea con il modo in cui gli argomenti del programma funzionano in generale: questo alla fine si riduce a chiamare una delle execfunzioni, in cui è presente anche il primo argomento fornito $0, e altrettanto comunemente quell'argomento è lo stesso dell'eseguibile che stai eseguendo. A volte, però, vuoi un valore speciale lì, e non ci sarebbe altro modo per ottenerlo. Dato che l'argomento esiste, deve mappare qualcosa e l'utente deve essere in grado di impostare ciò che è.

Questa coerenza (e probabilmente un incidente storico) porta alla situazione che si trova.


Puoi fare un esempio (si spera del mondo reale) del secondo paragrafo?
Muru,

Attento con l'analogia con argv[0]. $0è impostato su argv[0]passato a execve()quando è come sho bash. Per gli script, $0è impostato sul percorso indicato come argomento 1, 2 ... all'interprete (e per gli script eseguiti direttamente, che proviene dal primo argomento del percorso per execve ()).
Stéphane Chazelas,
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.