Bash variabler Bereich


104

Bitte erklären Sie mir, warum die allerletzte echoAussage leer ist. Ich erwarte, dass dies XCODEin der while-Schleife auf einen Wert von 1 erhöht wird:

#!/bin/bash
OUTPUT="name1 ip ip status" # normally output of another command with multi line output

if [ -z "$OUTPUT" ]
then
        echo "Status WARN: No messages from SMcli"
        exit $STATE_WARNING
else
        echo "$OUTPUT"|while read NAME IP1 IP2 STATUS
        do
                if [ "$STATUS" != "Optimal" ]
                then
                        echo "CRIT: $NAME - $STATUS"
                        echo $((++XCODE))
                else
                        echo "OK: $NAME - $STATUS"
                fi
        done
fi

echo $XCODE

Ich habe versucht, die folgende Anweisung anstelle der ++XCODEMethode zu verwenden

XCODE=`expr $XCODE + 1`

und es wird auch nicht außerhalb der while-Anweisung gedruckt. Ich glaube, ich vermisse hier etwas über den variablen Bereich, aber die alte Manpage zeigt es mir nicht.


Wo initialisieren Sie XCODE mit etwas, das inkrementiert werden kann?
Paul Tomblin

Ich habe versucht, ein "XCODE = 0" am Anfang des Codes außerhalb der while-Anweisung zu setzen
Matt P

Ohne die Cruft funktioniert es bei mir. #! / bin / bash für i in 1 2 3 4 5; do echo $ ((++ XCODE)) done echo "fin:" $ XCODE Ich denke, Ihr Problem hat nichts mit variablem Scoping zu tun und alles damit, was in der Zeit passiert.
Paul Tomblin

Einverstanden .. es scheint, als hätte es mit der "while read" -Schleife zu tun?
Matt P

Antworten:


117

Da Sie in die while-Schleife einleiten, wird eine Unter-Shell erstellt, um die while-Schleife auszuführen.

Jetzt hat dieser untergeordnete Prozess eine eigene Kopie der Umgebung und kann keine Variablen an seinen übergeordneten Prozess zurückgeben (wie bei jedem Unix-Prozess).

Daher müssen Sie eine Umstrukturierung vornehmen, damit Sie nicht in die Schleife eintauchen. Alternativ können Sie beispielsweise eine Funktion ausführen und echoden gewünschten Wert vom Unterprozess zurückgeben.

http://tldp.org/LDP/abs/html/subshells.html#SUBSHELL


2
Dies beantwortete nur so viele der scheinbar zufälligen Probleme, auf die ich beim Bash-Scripting stieß.
Daniel Agans

Diese perfekte Antwort stört mich so sehr und erklärt ein wirklich seltsames Verhalten in unserem CI-System.
KayCee

108

Das Problem ist, dass mit einer Pipe zusammengestellte Prozesse in Subshells ausgeführt werden (und daher eine eigene Umgebung haben). Was auch immer innerhalb whiledes Rohrs passiert, wirkt sich nicht auf etwas außerhalb des Rohrs aus.

Ihr spezifisches Beispiel kann gelöst werden, indem Sie die Pipe in umschreiben

while ... do ... done <<< "$OUTPUT"

oder vielleicht

while ... do ... done < <(echo "$OUTPUT")

32
Für diejenigen, die dies verwirrt betrachten, was die gesamte <() - Syntax ist (wie ich), heißt es "Process Substitution", und die oben beschriebene spezifische Verwendung kann hier eingesehen werden: mywiki.wooledge.org/ProcessSubstitution
Ross Aiken

2
Prozesssubstitution sollte jeder regelmäßig anwenden! Es ist super nützlich. Ich mache so etwas wie vimdiff <(grep WARN log.1 | sort | uniq) <(grep WARN log.2 | sort | uniq)jeden Tag. Bedenken Sie, dass Sie mehrere gleichzeitig verwenden und wie Dateien behandeln können ... MÖGLICHKEITEN!
Bruno Bronosky

8

Dies sollte auch funktionieren (da sich Echo und While in derselben Subshell befinden):

#!/bin/bash
cat /tmp/randomFile | (while read line
do
    LINE="$LINE $line"
done && echo $LINE )

3

Noch eine Option:

#!/bin/bash
cat /some/file | while read line
do
  var="abc"
  echo $var | xsel -i -p  # redirect stdin to the X primary selection
done
var=$(xsel -o -p)  # redirect back to stdout
echo $var

EDIT: Hier ist xsel eine Voraussetzung (installieren Sie es). Alternativ können Sie xclip: xclip -i -selection clipboard anstelle von verwenden xsel -i -p


Ich erhalte eine Fehlermeldung: ./scraper.sh: Zeile 111: xsel: Befehl nicht gefunden ./scraper.sh: Zeile 114: xsel: Befehl nicht gefunden
3kstc

@ 3kstc natürlich xsel installieren. Sie können auch xclip verwenden, aber die Verwendung ist etwas anders. Hauptpunkt hier: 1. Sie legen die Ausgabe in eine Zwischenablage (3 davon unter Linux), 2. Sie greifen sie von dort und senden sie an stdout.
Rammix

1
 #!/bin/bash
 OUTPUT="name1 ip ip status"
+export XCODE=0;
 if [ -z "$OUTPUT" ]
----

                     echo "CRIT: $NAME - $STATUS"
-                    echo $((++XCODE))
+                    export XCODE=$(( $XCODE + 1 ))
             else

echo $XCODE

Überprüfen Sie, ob diese Änderungen hilfreich sind


Dabei erhalte ich jetzt eine "0" zum Drucken für die letzte Echoanweisung. Ich erwarte jedoch, dass der Wert 1 und nicht Null ist. Auch warum die Verwendung von Export? Ich nehme an, das zwingt es in die Umwelt?
Matt P

0

Eine andere Möglichkeit besteht darin, die Ergebnisse aus der Subshell in eine Datei auszugeben und sie dann in der übergeordneten Shell zu lesen. etwas wie

#!/bin/bash
EXPORTFILE=/tmp/exportfile${RANDOM}
cat /tmp/randomFile | while read line
do
    LINE="$LINE $line"
    echo $LINE > $EXPORTFILE
done
LINE=$(cat $EXPORTFILE)

0

Ich habe das umgangen, als ich mein eigenes kleines Du gemacht habe:

ls -l | sed '/total/d ; s/  */\t/g' | cut -f 5 | 
( SUM=0; while read SIZE; do SUM=$(($SUM+$SIZE)); done; echo "$(($SUM/1024/1024/1024))GB" )

Der Punkt ist, dass ich mit () eine Subshell mache, die meine SUM-Variable und das while enthält, aber ich pfeife in das ganze () anstatt in das while selbst, was das Gotcha vermeidet.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.