Cosa significa $ {1 + “$ @”} in uno script di shell e in cosa differisce da “$ @”?


43

Nella documentazione Perl, perlrun (1) suggerisce di lanciare script Perl usando una shell bilingue / intestazione Perl:

#!/bin/sh
#! -*-perl-*-
eval 'exec perl -x -wS $0 ${1+"$@"}'
    if 0;

Cosa ${1+"$@"}significa? Ho provato a usare "$@"invece (usando Bash come / bin / sh), e sembra funzionare altrettanto bene.


modificare

Due risposte sotto dicono che dovrebbe essere ${1:+"$@"}. Sono a conoscenza della ${parameter:+word}sintassi ("Usa valore alternativo") documentata in bash (1). Tuttavia, non sono convinto, perché

  1. Sia ${1+"$@"}e "$@"funzionano bene, anche quando non ci sono parametri. Se creo simple.sh come

    #!/bin/sh
    eval 'exec /usr/bin/perl -x -S -- $0 "$@"'
        if 0;
    #!perl
    use Data::Dumper;
    print Dumper(\@ARGV);
    

    e question.sh as

    #!/bin/sh
    eval 'exec /usr/bin/perl -x -S -- $0 ${1+"$@"}'
        if 0;
    #!perl
    use Data::Dumper;
    print Dumper(\@ARGV);
    

    Posso far funzionare entrambi in modo identico:

    $ ./question.sh 
    $VAR1 = [];
    $ ./question.sh a
    $VAR1 = [
              'a'
            ];
    $ ./question.sh a 'b c'
    $VAR1 = [
              'a',
              'b c'
            ];
    $ ./question.sh ""
    $VAR1 = [
              ''
            ];
    $ ./simple.sh 
    $VAR1 = [];
    $ ./simple.sh a
    $VAR1 = [
              'a'
            ];
    $ ./simple.sh a 'b c'
    $VAR1 = [
              'a',
              'b c'
            ];
    $ ./simple.sh ""
    $VAR1 = [
              ''
            ];
    
  2. Anche altre fonti su Internet usano ${1+"$@"}, tra cui un hacker che sembra sapere cosa sta facendo.

Forse ${parameter+word}è una sintassi alternativa (o deprecata) senza documenti per ${parameter:+word}? Qualcuno potrebbe confermare questa ipotesi?


Risposte:


53

Questo è per compatibilità con la shell Bourne. La shell Bourne era una vecchia shell che fu rilasciata per la prima volta con Unix versione 7 nel 1979 ed era ancora comune fino alla metà degli anni '90 come /bin/shnella maggior parte degli Unices commerciali.

È l'antenato della maggior parte delle conchiglie tipo Bourne come ksh, basho zsh.

Aveva alcune caratteristiche imbarazzanti, molte delle quali sono state riparate kshe le altre shell e le nuove specifiche standard di sh, una delle quali è questa:

Con la shell Bourne (almeno quelle varianti in cui non è stato corretto): si "$@"espande in un argomento vuoto se l'elenco dei parametri posizionali è vuoto ( $# == 0) invece che nessun argomento.

${var+something}si espande in "qualcosa" a meno che non $varsia disinserito. È chiaramente documentato in tutte le shell ma è difficile da trovare nella bashdocumentazione in quanto è necessario prestare attenzione a questa frase:

Quando non si esegue l'espansione della sottostringa, utilizzando i moduli documentati di seguito, bash esegue il test per un parametro non impostato o nullo. Se si omettono i due punti si ottiene un test solo per un parametro non impostato .

Quindi si ${1+"$@"}espande "$@"solo se $1is set ( $# > 0) che aggira quella limitazione della shell Bourne.

Si noti che la shell Bourne è l'unica shell con quel problema. I moderni sh(che sono shconformi alle specifiche POSIX di sh(di cui la shell Bourne non è)) non hanno questo problema. Quindi è necessario solo se è necessario che il codice funzioni su sistemi molto vecchi in cui /bin/shpotrebbe essere una shell Bourne anziché una shell standard (si noti che POSIX non specifica la posizione dello standard sh, quindi ad esempio su Solaris prima di Solaris 11, /bin/shera ancora una shell Bourne (sebbene non avesse quel particolare problema) mentre il normale / standard shera in un'altra posizione ( /usr/xpg4/bin/sh)).

C'è un problema in quella perlrunpagina perldoc che $0non è citato però.

Vedi http://www.in-ulm.de/~mascheck/various/bourne_args/ per maggiori informazioni.


Impossibile riprodurre in Heirloom. Probabilmente non applicabile a Solaris.
ormaaj

2
@ormaaj. Sì, vedi la pagina che ho collegato. È stato risolto in SVR3 e Solaris / SunOS sopra 5 è stato ridisegnato su SVR4. E quella pagina menziona che 4.1.x non aveva nemmeno il problema.
Stéphane Chazelas,

Ah capisco <3 pagine Mascheck.
ormaaj,

3

C'è una differenza tra:

command ""

e

command

In uno, stai passando un argomento che è una stringa vuota. Nel secondo, non ci sono argomenti passati.

Per entrambi, "$ @" si equivale alla stessa cosa: "". Ma usare ${1:+"$@"}sarebbe ""per il primo e nessun argomento passato per il secondo, che era l'intento.

Questo diventa importante se stai facendo qualcosa lo script qui sotto, chiamato sshwrapper, che chiami con un comando opzionale o senza argomenti per ottenere una shell interattiva.

: sshwrapper [command]
exec /usr/bin/ssh "${HOSTNAME:-localhost}" "$@"

Questo tenterebbe di eseguire "" sull'host remoto (che restituisce), non sarebbe una shell interattiva.

: sshwrapper [command]
exec /usr/bin/ssh "${HOSTNAME:-localhost}" ${1:+"$@"}

Avrebbe avviato una shell interattiva sull'host remoto perché exec avrebbe interpretato correttamente la variabile come nulla, non come una stringa vuota.

Leggi l'uso di "$ {parametro: + parola}" ("Usa valore alternativo") e stringhe di espansione variabili simili nelle pagine del manuale di bash.


questo sembra valere solo per la shell Bourne e non per qualsiasi shimplementazione conforme a POSIX (vedi altra risposta).
törzsmókus,

4
No, ${1:+"$@"}si espanderebbe a nessun argomento se $1fosse vuoto. Tu vuoi ${1+"$@"}. Fondamentalmente lo hai invertito.
Stéphane Chazelas,

0

Ho riassunto la risposta di Stéphane Chazelas:

  • $ {1: + "$ @"} 'test se $ 1 null o non impostato
  • $ {1 + "$ @"} 'test se $ 1 non impostato

quindi se usi il secondo con il parametro "", significa che $ 1 è nullo, ma non verifica se è nullo o meno, vede solo che è già stato impostato, tuttavia è vuoto o no, quindi espanderà $ @ , ma usi $ {1: + "$ @"} 'con "", non si espanderà $@più.


7
Questo certamente riassunto, anche se potrebbe non chiarire ...
Archemar,
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.