Umleitungseingaben zur Umleitung von Shell-Skripts

  • Kann jemand dieses Verhalten erklären? Wird ausgeführt:

     #!/bin/sh
    echo "hello world" | read var1 var2
    echo $var1
    echo $var2
     

    Ergebnisse Wenn nichts ausgegeben wird, während:

     #!/bin/sh
    echo "hello world" > test.file
    read var1 var2 < test.file
    echo $var1
    echo $var2
     

    die erwartete Ausgabe erzeugt:

     hello
    world
     

    Sollte die Pipe nicht in einem Schritt das tun, was die Umleitung zu test.file im zweiten Beispiel bewirkt hat? Ich habe den gleichen Code mit den Bash- und Bash-Shells ausprobiert und habe bei beiden das gleiche Verhalten erhalten.

    09 September 2008
    Jon EricsonMax
9 answers
  •  #!/bin/sh
    echo "hello world" | read var1 var2
    echo $var1
    echo $var2
     

    erzeugt keine Ausgabe, da Pipelines jede ihrer Komponenten in einer Subshell ausführen. Subshells erben Kopien der Variablen der übergeordneten Shell, anstatt sie gemeinsam zu nutzen. Versuchen Sie Folgendes:

     #!/bin/sh
    foo="contents of shell variable foo"
    echo $foo
    (
        echo $foo
        foo="foo contents modified"
        echo $foo
    )
    echo $foo
     

    Die Klammern definieren einen Code-Bereich, der in einer Subshell ausgeführt wird, und $ foo bleibt erhalten Sein ursprünglicher Wert, nachdem er in ihnen geändert wurde.

    Versuchen Sie nun Folgendes:

     #!/bin/sh
    foo="contents of shell variable foo"
    echo $foo
    {
        echo $foo
        foo="foo contents modified"
        echo $foo
    }
    echo $foo
     

    Die geschweiften Klammern dienen lediglich zum Gruppieren, es wird keine Subshell erstellt, und das $ foo, das in den geschweiften Klammern geändert wird, ist das gleiche $ foo, das außerhalb von ihnen geändert wurde.

    Versuchen Sie es jetzt :

     #!/bin/sh
    echo "hello world" | {
        read var1 var2
        echo $var1
        echo $var2
    }
    echo $var1
    echo $var2
     

    Innerhalb der geschweiften Klammern erstellt das Read-Builtin $ var1 und $ var2 richtig und Sie können sehen, dass sie richtig sind wiederhole Außerhalb der Zahnspange existieren sie nicht mehr. Der gesamte Code innerhalb der geschweiften Klammern wurde in einer Subshell ausgeführt, da er eine Komponente einer Pipeline ist. .

    Sie können beliebige Codemengen zwischen geschweifte Klammern setzen. Sie können also diese Piping-in-a-Block-Konstruktion verwenden, wenn Sie einen Shell-Skriptblock ausführen müssen, der die Ausgabe von etwas anderem analysiert.

    19 September 2008
    flabdablet
  • Dies wurde bereits richtig beantwortet, die Lösung wurde jedoch noch nicht angegeben. Verwenden Sie ksh, nicht bash. Vergleichen Sie:

     $ echo 'echo "hello world" | read var1 var2
    echo $var1
    echo $var2' | bash -s
     

    An:

     $ echo 'echo "hello world" | read var1 var2
    echo $var1
    echo $var2' | ksh -s
    hello
    world
     

    ksh ist wegen ihrer kleinen Feinheiten eine überlegene Programmierschale. (bash ist meiner Meinung nach die bessere interaktive Shell.)

    15 August 2008
    Jon EricsonMax
  • Eine kürzlich hinzugefügte Erweiterung von bash ist die Option lastpipe, mit der der letzte Befehl in einer Pipeline in der aktuellen Shell ausgeführt werden kann, nicht in einer Subshell, wenn die Jobsteuerung deaktiviert ist.

     #!/bin/bash
    set +m      # Deactiveate job control
    shopt -s lastpipe
    echo "hello world" | read var1 var2
    echo $var1
    echo $var2
     

    gibt tatsächlich

     hello
    world
     
    aus
    26 July 2012
    chepner
  •  read var1 var2 < <(echo "hello world")
     
    29 June 2011
    glenn jackman
  • Alles klar, ich habe es herausgefunden!

    Dies ist ein schwerwiegender Fehler, der zu beheben ist, jedoch auf die Art und Weise zurückzuführen, wie Pipes von der Shell verarbeitet werden. Jedes Element einer Pipeline wird in einem separaten Prozess ausgeführt. Wenn der Lesebefehl var1 und var2 setzt, wird ihm eine eigene Subshell zugewiesen, nicht die übergeordnete Shell. Wenn also die Subshell beendet wird, gehen die Werte von var1 und var2 verloren. Sie können jedoch auch versuchen,

     var1=$(echo "Hello")
    echo var1
     

    , wodurch die erwartete Antwort zurückgegeben wird. Leider funktioniert dies nur für einzelne Variablen. Sie können nicht viele gleichzeitig setzen. Um mehrere Variablen gleichzeitig festzulegen, müssen Sie diese entweder in eine Variable einlesen und in mehrere Variablen zerlegen oder Folgendes verwenden:

     set -- $(echo "Hello World")
    var1="$1" var2="$2"
    echo $var1
    echo $var2
     

    Auch wenn ich zugeben muss, dass es nicht so elegant ist wie eine Pfeife, funktioniert es doch. Natürlich sollten Sie bedenken, dass Lesen von Dateien in Variablen gelesen werden sollte. Daher sollte es schwieriger sein, das Lesen von Standardeingaben zuzulassen.

    05 August 2012
    Henk Langeveldfrankster
  • Der Post wurde richtig beantwortet, aber ich möchte einen alternativen Liner anbieten, der möglicherweise von Nutzen ist.

    Zum Zuweisen von Leerzeichen getrennte Werte aus echo (oder stdout für diese Angelegenheit) für Shell-Variablen könnten Sie die Verwendung von Shell-Arrays in Betracht ziehen:

     $ var=( $( echo 'hello world' ) )
    $ echo ${var[0]}
    hello
    $ echo ${var[1]}
    world
     

    Hier Beispiel var ist ein Array, auf das mit dem Konstrukt $ {var [index]} zugegriffen werden kann, wobei index der Arrayindex ist (beginnt mit 0).

    Auf diese Weise können dem jeweiligen Arrayindex beliebig viele Parameter zugewiesen werden.

    14 September 2008
  • Meine Meinung zu diesem Problem (mit Bash):

     read var1 var2 <<< "hello world"
    echo $var1 $var2
     
    29 June 2011
    glenn jackman
  • Dies liegt daran, dass in der Pipe-Version eine Subshell erstellt wird, die die Variable in ihren lokalen Bereich liest, die dann beim Beenden der Subshell gelöscht wird.

    Führen Sie diesen Befehl aus / p>

     $ echo $$;cat | read a
    10637
     

    und verwenden Sie pstree -p, um die laufenden Prozesse anzusehen. Sie sehen eine zusätzliche Shell an Ihrem Hauptschale.

         |                       |-bash(10637)-+-bash(10786)
        |                       |             `-cat(10785)
     
    06 August 2008
    Mark Harrisonyukondude
  • Versuchen Sie:

     echo "hello world" | (read var1 var2 ; echo $var1 ; echo $var2 )
     

    Das Problem ist, wie mehrere Personen festgestellt haben, dass var1 und var2 werden in einer Subshell-Umgebung erstellt, die beim Beenden der Subshell zerstört wird. Das Obige verhindert, dass die Subshell zerstört wird, bis das Ergebnis bestätigt wurde. Eine andere Lösung ist:

     result=`echo "hello world"`
    read var1 var2 <<EOF
    $result
    EOF
    echo $var1
    echo $var2
     
    17 September 2008
    pjz