Domeniul de aplicare a variabilelor Bash (Programare, Bash, Domeniu De Aplicare, Țeavă)

Matt P a intrebat.

Vă rog să-mi explicați de ce ultima echo declarație este goală? Mă aștept ca XCODE este incrementată în bucla while la valoarea 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

Am încercat să folosesc următoarea instrucțiune în loc de ++XCODE metoda

XCODE=`expr $XCODE + 1`

și nici aceasta nu se imprimă în afara declarației while. Cred că îmi scapă ceva despre domeniul de aplicare a variabilelor, dar pagina de manual nu-mi arată nimic.

Comentarii

  • Unde inițializați XCODE în ceva care poate fi incrementat? –  > Por Paul Tomblin.
  • Am încercat să arunc un „XCODE=0” în partea de sus a codului, în afara instrucțiunii while –  > Por Matt P.
  • Fără această greșeală, pentru mine funcționează. #!/bin/bash for i in 1 2 3 4 5; do echo $((++XCODE)) done done echo echo „fin:” $XCODE Cred că problema ta nu are nimic de-a face cu domeniul de cuprindere a variabilelor și totul are de-a face cu ceea ce se întâmplă în while. –  > Por Paul Tomblin.
  • De acord… se pare că are legătură cu bucla „while read”? –  > Por Matt P.
  • Există o întrebare frecventă Bash despre acest lucru: mywiki.wooledge.org/BashFAQ/024 –  > Por Fabio spune Reinstaurați-o pe Monica.
7 răspunsuri
pixelbeat

Pentru că faci piping în bucla while, se creează un sub-shell pentru a rula bucla while.

Acum, acest proces copil are propria copie a mediului și nu poate transmite nicio variabilă înapoi la părintele său (ca în orice proces unix).

Prin urmare, va trebui să vă restructurați astfel încât să nu intrați în buclă.Alternativ, ați putea rula într-o funcție, de exemplu, și echo valoarea pe care o doriți returnată de subproces.

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

Comentarii

  • acest lucru tocmai a răspuns la atât de multe dintre problemele aparent aleatorii pe care le întâlneam cu scripturile bash. –  > Por Daniel Agans.
  • Acest răspuns perfect mă supără atât de mult și explică un comportament foarte ciudat în sistemul nostru CI. –  > Por KayCee.
mweerden

Problema este că procesele puse împreună cu o țeavă sunt executate în subshell-uri (și, prin urmare, au propriul lor mediu). Orice se întâmplă în cadrul while nu afectează nimic în afara pipe-ului.

Exemplul tău specific poate fi rezolvat prin rescrierea pipe-ului în

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

sau poate

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

Comentarii

    33

  • Pentru cei care nu înțeleg ce este toată sintaxa <() (așa cum am fost și eu), aceasta se numește „Process Substitution”, iar utilizarea specifică detaliată mai sus poate fi văzută aici: mywiki.wooledge.org/ProcessSubstitution –  > Por Ross Aiken.
  • Process Substitution este ceva ce toată lumea ar trebui să folosească în mod regulat! Este foarte utilă. Eu fac ceva de genul vimdiff <(grep WARN log.1 | sort | uniq) <(grep WARN log.2 | sort | uniq) în fiecare zi. Luați în considerare faptul că puteți utiliza mai multe deodată și le puteți trata ca pe niște fișiere… POSIBILITĂȚI! –  > Por Bruno Bronosky.
sano

Acest lucru ar trebui să funcționeze la fel de bine (deoarece echo și while sunt în același subshell):

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

Rammix

Încă o opțiune:

#!/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: Aici, xsel este o cerință (instalați-l). alternativ, puteți folosi xclip: xclip -i -selection clipboardîn loc de xsel -i -p

Comentarii

  • Primesc o eroare: ./scraper.sh: linia 111: xsel: comanda nu a fost găsită ./scraper.sh: linia 114: xsel: comanda nu a fost găsită –  > Por 3kstc.
  • @3kstc evident, instalați xsel. De asemenea, puteți folosi xclip, dar utilizarea sa este puțin diferită. Punctul principal aici: În primul rând, puneți ieșirea într-un clipboard (3 în Linux), în al doilea rând – o luați de acolo și o trimiteți la stdout. –  > Por Rammix.
freethinker

O altă opțiune este de a ieși rezultatele într-un fișier din subshell și apoi de a le citi în shell-ul părinte. ceva de genul

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

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

vezi dacă aceste modificări ajută

Comentarii

  • Când fac acest lucru, acum primesc un „0” pentru a imprima pentru ultima declarație echo. totuși mă aștept ca valoarea să fie 1 nu zero. De asemenea, de ce se folosește exportul? Presupun că asta o forțează în mediu? –  > Por Matt P.
Adrian May

Am reușit să ocolesc acest lucru când îmi făceam propriul meu mic 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" )

Ideea este că fac un subshell cu ( ) care conține variabila SUM și while, dar fac pipe în întregul ( ) în loc de while în sine, ceea ce evită problema.