Variablen, die nach der während der Leselesschleife zurückgesetzt werden, die aus einer Pipeline liest

StackOverflow https://stackoverflow.com/questions/7313491

Frage

initiate () {
read -p "Location(s) to look for .bsp files in? " loc
find $loc -name "*.bsp" | while read
do
    if [ -f "$loc.bz2" ]
    then
        continue
    else
        filcount=$[$filcount+1]
        bzip $loc
    fi
    if [ "$scan" == "1" ]; then bzipint $loc
    fi
    echo $filcount    #Correct counting
    echo $zipcount    #Correct counting
    echo $scacount    #Correct counting
    echo $valid       #Equal to 1
done

echo $filcount    #Reset to 0
echo $zipcount    #Reset to 0
echo $scacount    #Reset to 0
echo $valid       #Still equal to 1
}

Ich schreibe ein Bash -Shell -Skript, um sie zu verwenden bzip2 Alle aufzühen .bsp Dateien in einem Verzeichnis. In diesem Skript habe ich mehrere Variablen für die Zählung von Gesamtsummen (Dateien, erfolgreiche Reißverschlüsse, erfolgreiche Integritäts -Scans), aber ich habe anscheinend ein Problem gestoßen.

Wann find $loc -name "*.bsp" rennt keine Dateien aus, um dem zu geben while read und while read geht aus, es nuller $filcount, $zipcount und $scacount (alle werden im Inneren verändert (erhöht) initiate (), bzip () (was während während initiate ()) oder bzipint () (was auch in genannt wird initiate ()).

Um zu testen, ob es etwas mit Variablen zu tun hat, die sich im Inneren ändern initiate () oder andere Funktionen, auf die darauf zugegriffen wurde, habe ich Echo verwendet $valid, was außerhalb von definiert ist initiate () (wie $filcount, $zipcount, usw.), wird aber nicht von einer anderen Funktion im Inneren geändert initiate () oder im Inneren initiate () selbst.

Interessanterweise, $valid Wird nicht wie die anderen Variablen im Initiat auf 0 zurückgesetzt.

Kann mir jemand sagen, warum meine Variablen beim Lesen auf magische Weise zurückgesetzt werden?

War es hilfreich?

Lösung

Ich bin gestern auf dieses Problem gestoßen.

Das Problem ist, dass Sie es tun find $loc -name "*.bsp" | while read. Weil dies eine Pfeife beinhaltet, die while read Loop kann nicht im selben Bash -Prozess wie der Rest Ihres Skripts ausgeführt werden. Bash muss von einem Subprozess ausgehen, damit er den Stdout von verbinden kann find zum Stdin der while Schleife.

Dies ist alles sehr klug, aber es bedeutet, dass alle in der Schleife festgelegten Variablen nach der Schleife nicht gesehen werden können, was den gesamten Zweck des while Schleife ich schrieb.

Sie können entweder versuchen, die Eingabe in die Schleife zu versetzen, ohne ein Rohr zu verwenden, oder die Ausgabe von der Schleife ohne Variablen abrufen. Am Ende hatte ich einen schrecklichen Gräuel, der sowohl in eine temporäre Datei schreiben als auch die gesamte Schleife einwickelte $(...), wie so:

var="$(producer | while read line; do
    ...
    echo "${something}"
done)"

Was mich Var auf alle Dinge einsetzte, die aus der Schleife wiedergegeben worden waren. Ich habe wahrscheinlich die Syntax dieses Beispiels durcheinander gebracht. Ich habe nicht den Code, den ich im Moment praktisch geschrieben habe.

Andere Tipps

Wenn Sie Bash verwenden

while read
do
    if [ -f "$REPLY.bz2" ]
    then
        continue
    else
        filcount=$[$filcount+1]
        bzip $REPLY
    fi
    if [ "$scan" == "1" ]; then bzipint $REPLY
    fi
    echo $filcount    #Correct counting
    echo $zipcount    #Correct counting
    echo $scacount    #Correct counting
    echo $valid       #Equal to 1
done < <(find $loc -name "*.bsp")

Zu Fassen Sie die Optionen zusammen zum Verwendung read Am Ende des konzeptionellen Äquivalents einer Pipeline In posix-ähnlichen Muscheln:

Zusammenfassung: standardmäßig in Bash und in streng posix-konformen Muscheln immer, immer, Alle Befehle in einer Pipeline in a Unterschale, Also Variablen, die sie erstellen oder geändert haben aktuell Hülse (existiert nicht nach dem Ende der Pipeline).

Folgende Abdeckungen bash, ksh, zsh, und sh ([meistens] POSIX-Features nur Muscheln wie dash) und Shows Möglichkeiten zur Vermeidung der Schaffung einer Unterschale, um die von Variablen erstellten / modifizierten Variablen zu erhalten read.

Wenn keine minimale Versionsnummer angegeben ist, gehen Sie davon aus, dass selbst "ziemlich alte" Versionen sie unterstützen (die fraglichen Funktionen gibt es schon lange, aber ich weiß nicht genau, wann sie vorgestellt wurden.

Beachten Sie das als [posix-konforme] Alternative zu den folgenden Lösungen Sie können jederzeit die Ausgabe eines Befehls in einer [temporären] Datei erfassen und ihn dann füttern read wie < file, was auch Unterschalen vermeidet.


ksh, und zsh: Keine Problemumgehung/Konfigurationsänderung erforderlich:

Das read gebaut standardmäßig läuft in der aktuell Schale, wenn sie als die verwendet werden letzte Befehl in Pipeline.

Scheinbar, ksh und zsh standardmäßig Lauf irgendein Befehl in der letzte Stadium einer Pipeline in der aktuell Hülse.
Beobachtet in ksh 93u+ und zsh 5.0.5.
Wenn Sie genau wissen, in welcher Version diese Funktion eingeführt wurde, lassen Sie es mich wissen.

#!/usr/bin/env ksh
#!/usr/bin/env zsh

out= # initialize output variable

# Pipe multiple lines to the `while` loop and collect the values in the output variable.
printf '%s\n' one two three | 
 while read -r var; do
   out+="$var/"
 done

echo "$out" # -> 'one/two/three/'

bash 4.2+: Verwenden Sie die lastpipe Shelloption

In Bash Version 4.2 oder höher schalten Sie die Shell -Option ein lastpipe verursacht das letzte Pipeline -Segment in der aktuell Shell, so dass lesen Sie Variablen erstellen, die für die aktuelle Shell sichtbar sind.

#!/usr/bin/env bash

shopt -s lastpipe # bash 4.2+: make the last pipeline command run in *current* shell

out=
printf '%s\n' one two three | 
 while read -r var; do
   out+="$var/"
 done

echo "$out" # -> 'one/two/three/'

bash, ksh, zsh: verwenden Prozesssubstitution

Eine Prozesssubstitution ist locker gesagt, dass die Ausgabe eines Befehls wie eine temporäre Datei handelt.

out=
while read -r var; do
  out+="$var/"
done < <(printf '%s\n' one two three) # <(...) is the process substitution

echo "$out" # -> 'one/two/three'

bash, ksh, zsh: verwenden ein hier-saiten mit einer Befehlssubstitution

out=
while read -r var; do
  out+="$var/"
done <<< "$(printf '%s\n' one two three)" # <<< is the here-string operator

echo "$out" # -> 'one/two/three'

Beachten Sie die Notwendigkeit, die Befehlssubstitution zu verdoppeln, um ihre Ausgabe vor Hülle -Erweiterungen.


POSIX-konforme Lösung (sh): verwenden ein hier-dokument mit einer Befehlssubstitution

#!/bin/sh

out=
while read -r var; do
  out="$out$var/"
done <<EOF # <<EOF ... EOF is the here-doc
$(printf '%s\n' one two three)
EOF

echo "$out" # -> 'one/two/three'

Beachten Sie, dass Sie standardmäßig den Endtrennzeichen platzieren müssen - EOF, In diesem Fall - zu Beginn der Linie, und dass keine Charaktere folgen dürfen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top