Hi,
das read builtin soll, wenn ich es richtig verstehe, eine Zeile von der Standardeingabe lesen, diese nach $IFS auseinandernehmen und dann in die als Argumente übergebenen Variablen speichern. Warum funktioniert das scheinbar nur in Subshells?
Illustration:
<code> echo "1" | read x ; echo $x echo "2" | ( read x; echo $x; ) echo "3" | { read x; echo $x; } echo "4" | { read x; } ; echo $x </code>
Nur 2 und 3 funktionieren bei mir wie erwartet. Der Unterschied zwischen 3 und 4 ist mir vollkommen schleierhaft. Der Unterschied zwischen (...) und {...} ist doch gerade, dass bei (...) gilt "Variable assignments and builtin commands that affect the shell's environment do not remain in effect after the command completes". Müsste das dann nicht bei {...} genau anders sein? Bitte klärt mich irgend jemand auf?
Viele Grüße Frank Benkstein.
On Sat, Nov 27, 2004 at 03:23:49PM +0100, Frank Benkstein wrote:
<code> echo "1" | read x ; echo $x echo "4" | { read x; } ; echo $x
Zwischen diesen beiden gibt es keinen semantischen unterschied. Geschweifte klammern werden zur Gruppierung verwendet. Da hier bei 4 nur ein Befehl zwischen den Klammern steht gibt es auch nichts zu gruppieren.
echo "2" | ( read x; echo $x; ) echo "3" | { read x; echo $x; }
</code>
Hier wird korrekt gruppiert.
Das Problem ist, dass eine Pipe immer eine Subshell fork()t, d.h fuer den 1. und 4. Fall, dass das "read x" in einer Subshell ausgefuehrt wird. Diese Subshell hat aber keine Moeglichkeit der Vatershell zu sagen, welchen Wert $x nun hat. (Nach dem ; beendet sich die Subshell wieder). Im 2. und 3. Fall fuehrt aber die Subshell das "echo $x" aus. Im 2. Fall werden womoeglich sogar 2 Subshells gestartet, eine durch die Pipe, die andere durch die runden Klammern.
Haeufig stoesst man bei while-read-Schleifen auf das Problem.
dmesg | while read f do x="$f" done echo $x;
Die while-Schleife wird in diesem Fall in der Subshell ausgefuehrt, und kann folglich das $x des Vaterprozesses nicht setzen. Loesung in diesem Fall sind named pipes.
FIFO=/tmp/fifo.$$ mkfifo $FIFO
dmesg > $FIFO & while read f x="$f" done < $FIFO rm $FIFO echo $x
Der Trick ist, das Kommando vor der Pipe in einer Subshell auszufuehren.
Ich hoffe das war halbwegs verstaendlich.
/GM
Hallo,
On Sat, 27 Nov 2004 17:30:39 +0100 Gerhard Meier wrote:
Das Problem ist, dass eine Pipe immer eine Subshell fork()t,
Hier liegt also der Hase im Glashaus! Wie doof.
[named pipes] Ich hoffe das war halbwegs verstaendlich.
Danke, ja. Aber viel zu viel Overhead für mich. Ich hatte eigentlich folgendes Ziel:
$1 ist ein Hostname mit Pfad der Form example/home. Beides soll in zwei getrennte Variablen, ich dachte mir das so:
<code> echo "$1" | sed -e "s:/: &:" | read HOST REMOTE_DIR </code>
Es wäre so schön und einfach gewesen. Wie geht es richtig?
Viele Grüße Frank Benkstein.
Hi Frank,
On Sat, Nov 27, 2004 at 18:33:19 +0100, Frank Benkstein wrote:
Danke, ja. Aber viel zu viel Overhead fuer mich. Ich hatte eigentlich folgendes Ziel:
$1 ist ein Hostname mit Pfad der Form example/home. Beides soll in zwei getrennte Variablen, ich dachte mir das so:
<code> echo "$1" | sed -e "s:/: &:" | read HOST REMOTE_DIR </code>
HOST=${1%%/*} REMOTE_DIR=${1#*/}
Erklaerung zu den ${..%..} und ${..#..} Konstrukten in man bash, suchen nach "Parameter Expansion".
bye, Chris
Hi, Chris und Gerhard.
On Sat, Nov 27, 2004 at 18:33:19 +0100, Frank Benkstein wrote:
$1 ist ein Hostname mit Pfad der Form example/home. Beides soll in zwei getrennte Variablen, ich dachte mir das so:
<code> echo "$1" | sed -e "s:/: &:" | read HOST REMOTE_DIR </code>
On Sat, 27 Nov 2004 19:32:11 +0100 Christian Perle wrote:
HOST=${1%%/*} REMOTE_DIR=${1#*/}
On Sat, 27 Nov 2004 19:43:13 +0100 Gerhard Meier wrote:
HOST="${1%%:*}" REMOTE_DIR="${1#*:}"
Obskur aber fast das, was ich will. Den Rest bekomme ich (hoffentlich) selber hin. Danke.
Schönen Abend noch Frank.
On Sat, Nov 27, 2004 at 05:30:39PM +0100, Gerhard Meier wrote:
Haeufig stoesst man bei while-read-Schleifen auf das Problem.
dmesg | while read f do x="$f" done echo $x;
Die while-Schleife wird in diesem Fall in der Subshell ausgefuehrt, und kann folglich das $x des Vaterprozesses nicht setzen. Loesung in diesem Fall sind named pipes.
FIFO=/tmp/fifo.$$ mkfifo $FIFO
ohne mktemp und Fehlerbehandlung? Mutig, mutig... :-)
dmesg > $FIFO & while read f x="$f" done < $FIFO rm $FIFO echo $x
Der Trick ist, das Kommando vor der Pipe in einer Subshell auszufuehren.
while read f; do x="$f" done < <(dmesg) echo $x
On Sat, Nov 27, 2004 at 09:18:11PM +0100, Stefan Seyfried wrote:
FIFO=/tmp/fifo.$$ mkfifo $FIFO
ohne mktemp und Fehlerbehandlung? Mutig, mutig... :-)
Es ging doch nur ums Prinzip ;)
while read f; do x="$f" done < <(dmesg) echo $x
Ja, es war eine Bashfrage, ich kann aber nur sh, und das ist gut so. ;)
/GM
Hi,
On Sat, 27 Nov 2004 21:18:11 +0100 Stefan Seyfried wrote:
while read f; do x="$f" done < <(dmesg) echo $x
Hm...
<code> while read a b ; do HOST="${a}" REMOTE_DIR="${b}" done < <(echo "$1" | sed -e "s:/: &:") </code>
Immernoch krank, aber man kann sehen, was es macht, ohne Parameter Expansion zu können.
Ich weiß,
<code> echo "${1/// /}" </code>
in der <()-Subshell ginge auch, aber *hust*.
Danke nochmals.
Grüße Frank.
lug-dd@mailman.schlittermann.de