Was bedeutet $ {1 + "$ @"} in einem Shell-Skript und wie unterscheidet es sich von "$ @"?

43

In der Perl-Dokumentation schlägt Perlrun (1) vor, Perl-Skripte mit einem zweisprachigen Shell- / Perl-Header zu starten:

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

Was heißt ${1+"$@"}das? Ich habe versucht, "$@"stattdessen (mit Bash als / bin / sh), und es scheint genauso gut zu funktionieren.


Bearbeiten

Zwei Antworten unten sagen, dass es sein soll ${1:+"$@"}. Mir ist die ${parameter:+word}in bash (1) dokumentierte Syntax ("Use Alternate Value") bekannt. Ich bin jedoch nicht überzeugt, weil

  1. Beides ${1+"$@"}und "$@"funktionieren einwandfrei, auch wenn keine Parameter vorhanden sind. Wenn ich simple.sh als erstelle

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

    und question.sh als

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

    Ich kann beide dazu bringen, identisch zu arbeiten:

    $ ./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. Andere Quellen im Internet verwenden ebenfalls ${1+"$@"}, darunter ein Hacker, der zu wissen scheint, was er tut.

Vielleicht ${parameter+word}ist eine undokumentierte alternative (oder veraltete) Syntax für ${parameter:+word}? Könnte jemand diese Hypothese bestätigen?

200_erfolg
quelle
Sieht aus wie Grawlixes
Martin Thoma

Antworten:

53

Dies dient der Kompatibilität mit der Bourne-Shell. Die Bourne-Shell war eine alte Shell, die 1979 zum ersten Mal mit Unix Version 7 veröffentlicht wurde und bis Mitte der 90er Jahre wie /bin/shbei den meisten kommerziellen Unices üblich war.

Es ist der Vorfahr der meisten Bourne-ähnlichen Muscheln wie ksh, bashoder zsh.

Es hatte einige unangenehme Eigenschaften, von denen viele in kshund die anderen Schalen und die neue Standardspezifikation von repariert worden sind sh, von denen eine dieses ist:

Mit der Bourne-Shell (zumindest in den Varianten, in denen dies nicht behoben wurde): "$@"Wird zu einem leeren Argument erweitert, wenn die Liste der Positionsparameter leer ist ( $# == 0), anstatt überhaupt kein Argument.

${var+something}Erweitert sich zu "etwas", es $varsei denn, es ist nicht festgelegt. Es ist in allen Shells klar dokumentiert, aber in der bashDokumentation schwer zu finden, da Sie auf diesen Satz achten müssen:

Wenn Sie keine Teilzeichenfolgenerweiterung durchführen und die unten dokumentierten Formulare verwenden, testet bash auf einen Parameter, der nicht festgelegt oder null ist. Das Weglassen des Doppelpunkts führt zu einem Test nur für einen Parameter, der nicht festgelegt ist .

So ${1+"$@"}dehnt sich auf "$@"nur , wenn $1gesetzt ( $# > 0) , die arbeitet rund um die Begrenzung der Bourne - Shell.

Beachten Sie, dass die Bourne-Shell die einzige Shell mit diesem Problem ist. Moderne shs (die shder POSIX-Spezifikation entsprechen sh(was die Bourne-Shell nicht tut)) haben dieses Problem nicht. Sie benötigen dies also nur, wenn Sie Ihren Code für sehr alte Systeme benötigen, auf denen /bin/shmöglicherweise eine Bourne-Shell anstelle einer Standard-Shell verwendet wird (beachten Sie, dass POSIX den Speicherort des Standards nicht angibt sh, z. B. unter Solaris vor Solaris 11. /bin/shwar immer noch eine Bourne-Shell (obwohl es dieses spezielle Problem nicht gab), während sich das normale / standard shan einem anderen Ort befand ( /usr/xpg4/bin/sh)).

Es gibt ein Problem in dieser perlrunPerldoc-Seite, das $0jedoch nicht in Anführungszeichen gesetzt ist.

Weitere Informationen finden Sie unter http://www.in-ulm.de/~mascheck/various/bourne_args/ .

Stéphane Chazelas
quelle
Kann nicht im Erbstück reproduzieren. Gilt wahrscheinlich nicht für Solaris.
Ormaaj
2
@ormaaj. Ja, siehe die Seite, die ich verlinkt habe. Es wurde in SVR3 behoben, und Solaris / SunOS über 5 basierte auf SVR4. Und diese Seite erwähnt, dass 4.1.x das Problem auch nicht hatte.
Stéphane Chazelas
Ah ich sehe. <3 Mascheck-Seiten.
Ormaaj
3

Es gibt einen Unterschied zwischen:

command ""

und

command

In einem übergeben Sie ein Argument, das eine leere Zeichenfolge ist. Im zweiten Fall werden keine Argumente übergeben.

Für beide „$ @“ wird auf die gleiche Sache zu: "". Aber der Einsatz ${1:+"$@"}wäre ""für die erste und keine Argumente für die zweite übergeben, die die Absicht war.

Dies ist wichtig, wenn Sie das folgende Skript namens sshwrapper ausführen, das Sie mit einem optionalen Befehl oder ohne Argumente aufrufen, um eine interaktive Shell abzurufen.

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

Dies würde versuchen, "" auf dem entfernten Host (der gerade zurückkehrt) zu "" laufen, es wäre keine interaktive Shell.

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

Startet eine interaktive Shell auf dem Remote-Host, da die ausführbare Datei die Variable nicht als leere Zeichenfolge interpretiert.

Lesen Sie die Verwendung von "$ {parameter: + word}" ("Use Alternate Value") und ähnlichen variablen Erweiterungszeichenfolgen in den bash-Handbuchseiten.

Arcege
quelle
Dies scheint nur für die Bourne-Shell und nicht für eine POSIX-konforme shImplementierung zu gelten (siehe andere Antwort).
Törzsmókus
4
Nein, ${1:+"$@"}würde zu keinem Argument führen, wenn $1leer wäre. Sie möchten ${1+"$@"}. Grundsätzlich haben Sie es umgekehrt.
Stéphane Chazelas
0

Ich habe die Antwort von Stéphane Chazelas zusammengefasst:

  • $ {1: + "$ @"} 'testet, ob $ 1 null oder nicht gesetzt ist
  • $ {1 + "$ @"} 'testet, ob $ 1 nicht gesetzt ist

Wenn also die zweite mit dem Parameter "" verwendet wird, bedeutet dies, dass $ 1 null ist, aber es wird nicht geprüft, ob es null ist oder nicht. Es wird nur angezeigt, dass es bereits festgelegt wurde. Wenn Sie jedoch $ {1: + "$ @"} 'mit "" verwenden, wird es nicht $@mehr erweitert.

Kevin
quelle
7
Dies ist sicherlich zusammengefasst, obwohl es nicht klarstellen könnte ...
Archemar