Que signifie $ {1 + "$ @"} dans un script shell et en quoi est-il différent de "$ @"?

43

Dans la documentation Perl, perlrun (1) suggère de lancer les scripts Perl à l'aide d'un en-tête shell / Perl bilingue:

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

Qu'est-ce que ça ${1+"$@"}veut dire? J'ai essayé d'utiliser à la "$@"place (en utilisant Bash comme / bin / sh), et cela semble fonctionner aussi bien.


modifier

Deux réponses ci-dessous indiquent que c'est supposé l'être ${1:+"$@"}. Je connais la ${parameter:+word}syntaxe ("Use Alternate Value") décrite dans bash (1). Cependant, je ne suis pas convaincu, car

  1. Les deux ${1+"$@"}et "$@"fonctionnent très bien, même quand il n'y a pas de paramètres. Si je crée simple.sh en tant que

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

    et question.sh comme

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

    Je peux obtenir les deux travailler à l'identique:

    $ ./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. D'autres sources sur Internet utilisent également ${1+"$@"}, y compris un pirate qui semble savoir ce qu'il fait.

Peut ${parameter+word}- être une syntaxe alternative non documentée (ou obsolète) pour ${parameter:+word}? Quelqu'un pourrait-il confirmer cette hypothèse?

200_success
la source
On dirait des grawlixes
Martin Thoma

Réponses:

53

C'est pour la compatibilité avec le shell Bourne. Le shell Bourne était un ancien shell sorti avec la version 7 d'Unix en 1979 et qui était encore courant jusqu'au milieu des années 90, comme /bin/shsur la plupart des Unices commerciaux.

C'est l'ancêtre de la plupart des coquillages semblables à Bourneksh , bashou zsh.

Il comportait quelques fonctionnalités peu pratiques, dont beaucoup ont été corrigées ksh, les autres coques et la nouvelle spécification standard de sh, dont l’une est la suivante:

Avec le shell Bourne (au moins les variantes pour lesquelles il n’a pas été corrigé): se "$@"développe en un argument vide si la liste des paramètres de position est empty ( $# == 0) au lieu d’aucun argument.

${var+something}se développe en "quelque chose" à moins que $varnon défini. Il est clairement documenté dans tous les réservoirs, mais difficile à trouver dans la bashdocumentation car vous devez faire attention à cette phrase:

Lorsque vous n'effectuez pas de développement de sous-chaînes, bash utilise les formulaires décrits ci-dessous pour tester un paramètre non défini ou nul. L'omission des deux points entraîne un test uniquement pour un paramètre non défini .

Donc, ${1+"$@"}étend "$@"seulement à if si $1set ( $# > 0) qui contourne cette limitation du shell Bourne.

Notez que le shell Bourne est le seul shell avec ce problème. Modern shs ( shconforme à la spécification POSIX de sh(ce que le shell Bourne n'est pas)) n'a pas ce problème. Donc, vous n’avez besoin de cela que si vous avez besoin de votre code pour fonctionner sur de très vieux systèmes où il /bin/shpourrait s’agir d’un shell Bourne au lieu d’un shell standard (notez que POSIX ne spécifie pas l’emplacement de la norme sh, donc par exemple sous Solaris avant Solaris 11, /bin/shétait toujours un shell Bourne (bien qu’il n’ait pas ce problème particulier) alors que le standard / standard se shtrouvait dans un autre emplacement ( /usr/xpg4/bin/sh)).

Il y a un problème dans cette perlrunpage perldoc qui $0n'est pas cité.

Voir http://www.in-ulm.de/~mascheck/various/bourne_args/ pour plus d'informations.

Stéphane Chazelas
la source
Impossible de reproduire dans Heirloom. Probablement pas applicable à Solaris.
Ormaaj
2
@ormaaj. Oui, voir la page que j'ai liée. Il a été corrigé dans SVR3 et Solaris / SunOS au dessus de 5 rebasés sur SVR4. Et cette page mentionne que 4.1.x n’a pas eu le problème non plus.
Stéphane Chazelas
Ah, je vois. <3 Pages masquées.
Ormaaj
3

Il y a une différence entre:

command ""

et

command

Dans l'un, vous passez un argument qui est une chaîne vide. Dans le second cas, aucun argument n'a été transmis.

Pour les deux, « $ @ » correspondra à la même chose: "". Mais utiliser ${1:+"$@"}serait ""pour le premier et aucun argument ne serait transmis pour le second, ce qui était l'intention.

Cela devient important si vous exécutez quelque chose dans le script ci-dessous, appelé sshwrapper, que vous appelez avec une commande facultative ou sans argument pour obtenir un shell interactif.

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

Cela essaierait de s'exécuter "" sur l'hôte distant (qui ne fait que revenir), ce ne serait pas un shell interactif.

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

Lancerait un shell interactif sur l'hôte distant, car l'exécutable interpréterait correctement la variable comme étant nulle, pas une chaîne vide.

Consultez l’utilisation de "$ {paramètre: + mot}" ("Utiliser une autre valeur") et des chaînes d’extension de variable similaires dans les pages de manuel de bash.

Arcege
la source
cela semble s'appliquer uniquement au shell Bourne et non à une shimplémentation conforme à POSIX (voir autre réponse).
törzsmókus
4
Non, ${1:+"$@"}ne développerait aucun argument si $1était vide. Vous voulez ${1+"$@"}. En gros, vous l'avez inversé.
Stéphane Chazelas
0

J'ai résumé la réponse de Stéphane Chazelas:

  • $ {1: + "$ @"} 'tester si $ 1 est nul ou non défini
  • $ {1 + "$ @"} 'tester si $ 1 n'est pas défini

Donc, si utiliser le second avec le paramètre "", cela signifie que $ 1 est nul, mais il ne vérifie pas si c'est nul ou pas, il voit juste qu'il a déjà été réglé, mais qu'il soit vide ou non, donc il développera $ @ , mais vous utilisez $ {1: + "$ @"} 'avec "", il ne s’étendra $@plus.

Kevin
la source
7
Ceci est certainement résumé, même si cela n’a peut-être pas été précisé ...
Archemar