Frage

Kann jemand dieses Verhalten erklären?Läuft:

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

führt dazu, dass nichts ausgegeben wird, während:

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

erzeugt die erwartete Ausgabe:

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 sowohl mit der Dash- als auch mit der Bash-Shell ausprobiert und bei beiden das gleiche Verhalten festgestellt.

War es hilfreich?

Lösung

Eine neue Ergänzung zu bash ist der lastpipe Option, die es ermöglicht, dass der letzte Befehl in einer Pipeline in der aktuellen Shell und nicht in einer Subshell ausgeführt wird, 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

wird tatsächlich ausgegeben

hello
world

Andere Tipps

#!/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.Unterschalen Kopien erben der Variablen der übergeordneten Shell, anstatt sie zu teilen.Versuche dies:

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

Die Klammern definieren einen Codebereich, der in einer Subshell ausgeführt wird, und $foo behält seinen ursprünglichen Wert bei, nachdem er darin geändert wurde.

Versuchen Sie jetzt 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 der Gruppierung, es wird keine Unterschale erstellt und das innerhalb der geschweiften Klammern geänderte $foo ist das gleiche $foo, das außerhalb der Klammern geändert wurde.

Versuchen Sie jetzt Folgendes:

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

Innerhalb der geschweiften Klammern erstellt die integrierte Lesefunktion $var1 und $var2 ordnungsgemäß und Sie können sehen, dass sie wiedergegeben werden.Außerhalb der geschweiften Klammern existieren sie nicht mehr.Der gesamte Code innerhalb der geschweiften Klammern wurde in einer Subshell ausgeführt weil es eine Komponente einer Pipeline ist.

Sie können beliebige Codemengen zwischen geschweiften Klammern einfügen, sodass Sie diese Pipeline-in-einen-Block-Konstruktion immer dann verwenden können, wenn Sie einen Block eines Shell-Skripts ausführen müssen, der die Ausgabe von etwas anderem analysiert.

Dies wurde bereits richtig beantwortet, die Lösung wurde jedoch noch nicht genannt.Verwenden Sie ksh, nicht bash.Vergleichen:

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

Zu:

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

Aufgrund kleiner Feinheiten wie dieser ist ksh eine überlegene Programmier-Shell.(Bash ist meiner Meinung nach die bessere interaktive Shell.)

read var1 var2 < <(echo "hello world")

Der Beitrag wurde ordnungsgemäß beantwortet, aber ich möchte einen alternativen Einzeiler anbieten, der vielleicht von Nutzen sein könnte.

Um Shell-Variablen durch Leerzeichen getrennte Werte von echo (oder stdout) zuzuweisen, könnten Sie die Verwendung von Shell-Arrays in Betracht ziehen:

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

In diesem Beispiel ist var ein Array und auf den Inhalt kann mit dem Konstrukt ${var[index]} zugegriffen werden, wobei index der Array-Index ist (beginnt mit 0).

Auf diese Weise können Sie dem entsprechenden Array-Index beliebig viele Parameter zuweisen.

Meine Meinung zu diesem Problem (mit Bash):

read var1 var2 <<< "hello world"
echo $var1 $var2

Alles klar, ich habe es herausgefunden!

Dies ist ein schwer zu erkennender Fehler, der jedoch auf die Art und Weise zurückzuführen ist, wie Pipes von der Shell verarbeitet werden.Jedes Element einer Pipeline wird in einem separaten Prozess ausgeführt.Wenn der Lesebefehl var1 und var2 festlegt, legt er sie in seiner eigenen Unterschale fest, nicht in der übergeordneten Schale.Wenn die Subshell beendet wird, gehen die Werte von var1 und var2 verloren.Sie können es jedoch versuchen

var1=$(echo "Hello")
echo var1

was die erwartete Antwort zurückgibt.Leider funktioniert dies nur für einzelne Variablen, Sie können nicht viele gleichzeitig festlegen.Um mehrere Variablen gleichzeitig festzulegen, müssen Sie entweder eine Variable einlesen und in mehrere Variablen zerlegen oder so etwas verwenden:

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

Ich gebe zwar zu, dass es nicht so elegant ist wie die Verwendung einer Pfeife, aber es funktioniert.Natürlich sollten Sie bedenken, dass read dazu gedacht war, aus Dateien in Variablen zu lesen, daher sollte es etwas schwieriger sein, es von der Standardeingabe zu lesen.

Dies liegt daran, dass die Pipe-Version eine Subshell erstellt, die die Variable in ihren lokalen Bereich einliest, der dann zerstört wird, wenn die Subshell beendet wird.

Führen Sie diesen Befehl aus

$ echo $$;cat | read a
10637

und verwenden Sie pstree -p, um sich die laufenden Prozesse anzusehen. Sie werden eine zusätzliche Shell sehen, die an Ihrer Haupt-Shell hängt.

    |                       |-bash(10637)-+-bash(10786)
    |                       |             `-cat(10785)

Versuchen:

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

Das Problem besteht, wie mehrere Personen festgestellt haben, darin, dass var1 und var2 in einer Subshell-Umgebung erstellt werden, die zerstört wird, wenn diese Subshell beendet wird.Dadurch wird vermieden, dass die Subshell zerstört wird, bis das Ergebnis zurückgegeben wurde.Eine andere Lösung ist:

result=`echo "hello world"`
read var1 var2 <<EOF
$result
EOF
echo $var1
echo $var2
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top