Zakres zmiennej Bash

104

Proszę wyjaśnić mi, dlaczego ostatnie echostwierdzenie jest puste? Spodziewam się, że XCODEw pętli while zostanie zwiększona do wartości 1:

#!/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

Próbowałem użyć następującej instrukcji zamiast ++XCODEmetody

XCODE=`expr $XCODE + 1`

i to też nie będzie drukowane poza instrukcją while. Myślę, że brakuje mi czegoś na temat zakresu zmiennej, ale stara strona podręcznika nie pokazuje mi tego.

Matt P.
źródło
Gdzie inicjalizujesz XCODE do czegoś, co można zwiększyć?
Paul Tomblin,
Próbowałem wrzucić „XCODE = 0” na górze kodu, poza instrukcją while
Matt P
U mnie działa bez okrucieństwa. #! / bin / bash for i in 1 2 3 4 5; do echo $ ((++ XCODE)) done echo "fin:" $ XCODE Myślę, że twój problem nie ma nic wspólnego z określaniem zakresu zmiennych, a wszystko, co się dzieje w danej chwili.
Paul Tomblin
Zgoda… wygląda na to, że ma to związek z pętlą „podczas czytania”?
Matt P,
Jest Bash FAQ na ten temat: mywiki.wooledge.org/BashFAQ/024
Fabio mówi dozbrojenie Monica

Odpowiedzi:

117

Ponieważ przechodzisz do pętli while, tworzona jest powłoka podrzędna do uruchamiania pętli while.

Teraz ten proces potomny ma swoją własną kopię środowiska i nie może przekazać żadnych zmiennych z powrotem do swojego rodzica (jak w każdym procesie uniksowym).

Dlatego musisz zmienić strukturę, aby nie wchodzić w pętlę. Alternatywnie możesz na przykład uruchomić funkcję i echowartość, którą chcesz zwrócić z procesu podrzędnego.

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

pixelbeat
źródło
2
To właśnie stanowiło odpowiedź na tak wiele pozornie przypadkowych problemów, z którymi miałem do czynienia podczas pisania skryptów bash.
Daniel Agans
Ta doskonała odpowiedź bardzo mnie denerwuje i wyjaśnia naprawdę dziwne zachowanie w naszym systemie CI.
KayCee
108

Problem polega na tym, że procesy złożone razem z potokiem są wykonywane w podpowłokach (a zatem mają własne środowisko). Cokolwiek dzieje się wewnątrz while, nie wpływa na nic poza rurą.

Twój konkretny przykład można rozwiązać, przepisując potok na

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

a może

while ... do ... done < <(echo "$OUTPUT")
mweerden
źródło
32
Dla tych, którzy patrzą na to zdezorientowani co do tego, czym jest cała składnia <() (tak jak ja), nazywa się to „Zastępowanie procesu”, a konkretne użycie opisane powyżej można zobaczyć tutaj: mywiki.wooledge.org/ProcessSubstitution
Ross Aiken,
2
Zastępowanie procesów to coś, czego każdy powinien używać regularnie! To jest bardzo przydatne. Robię coś takiego vimdiff <(grep WARN log.1 | sort | uniq) <(grep WARN log.2 | sort | uniq)każdego dnia. Weź pod uwagę, że możesz używać wielu na raz i traktować je jak pliki ... MOŻLIWOŚCI!
Bruno Bronosky
8

To również powinno działać (ponieważ echo i while znajdują się w tej samej podpowłoce):

#!/bin/bash
cat /tmp/randomFile | (while read line
do
    LINE="$LINE $line"
done && echo $LINE )
sano
źródło
3

Jeszcze jedna opcja:

#!/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

EDYCJA: Tutaj xsel jest wymaganiem (zainstaluj go). Alternatywnie możesz użyć xclip: xclip -i -selection clipboard zamiast xsel -i -p

Rammix
źródło
Pojawia się błąd: ./scraper.sh: wiersz 111: xsel: nie znaleziono polecenia ./scraper.sh: wiersz 114: xsel: nie znaleziono polecenia
3kstc
@ 3kstc oczywiście zainstaluj xsel. Możesz także użyć xclip, ale jego użycie jest trochę inne. Główny punkt: 1. umieszczasz wyjście w schowku (3 z nich w Linuksie), 2. - bierzesz go stamtąd i wysyłasz na standardowe wyjście.
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

zobacz, czy te zmiany pomogą

Kent Fredric
źródło
Robiąc to, otrzymuję teraz „0” do wydrukowania dla ostatniej instrukcji echo. jednak oczekuję, że wartość będzie wynosić 1, a nie zero. Poza tym, po co eksportować? Zakładam, że wymusza to na środowisku?
Matt P,
0

Inną opcją jest zapisanie wyników do pliku z podpowłoki, a następnie odczytanie go w powłoce nadrzędnej. coś jak

#!/bin/bash
EXPORTFILE=/tmp/exportfile${RANDOM}
cat /tmp/randomFile | while read line
do
    LINE="$LINE $line"
    echo $LINE > $EXPORTFILE
done
LINE=$(cat $EXPORTFILE)
wolno-myśliciel
źródło
0

Poradziłem sobie z tym, kiedy tworzyłem swój własny mały du:

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" )

Chodzi o to, że tworzę podpowłokę z () zawierającą moją zmienną SUM i while, ale przesyłam rurkę do całości () zamiast do samej while, co pozwala uniknąć problemu.

Adrian May
źródło