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.


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,

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


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

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 )

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


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ą


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)

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.