Ambito variabile Bash


104

Per favore, spiegami perché l'ultima echoaffermazione è vuota? Mi aspetto che XCODEvenga incrementato nel ciclo while a un valore di 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

Ho provato a utilizzare la seguente dichiarazione invece del ++XCODEmetodo

XCODE=`expr $XCODE + 1`

e anche questo non verrà stampato al di fuori dell'istruzione while. Penso che mi manchi qualcosa sull'ambito variabile qui, ma la vecchia pagina man non me lo mostra.


Dove inizializzi XCODE su qualcosa che può essere incrementato?
Paul Tomblin,

Ho provato a lanciare un "XCODE = 0" all'inizio del codice, al di fuori dell'istruzione while
Matt P

Senza il cruft, funziona per me. #! / bin / bash per i in 1 2 3 4 5; do echo $ ((++ XCODE)) done echo "fin:" $ XCODE Penso che il tuo problema non abbia nulla a che fare con lo scoping variabile e tutto ciò che ha a che fare con ciò che sta accadendo nel frattempo.
Paul Tomblin,

D'accordo .. sembra che abbia a che fare con il ciclo "durante la lettura"?
Matt P

C'è una FAQ di Bash su questo: mywiki.wooledge.org/BashFAQ/024
Fabio dice

Risposte:


117

Poiché stai collegando il ciclo while, viene creata una sottostruttura per eseguire il ciclo while.

Ora questo processo figlio ha la sua copia dell'ambiente e non può passare alcuna variabile al suo genitore (come in qualsiasi processo unix).

Pertanto dovrai ristrutturare in modo da non collegarti al circuito. In alternativa è possibile eseguire in una funzione, ad esempio, e echoil valore che si desidera restituire dal sottoprocesso.

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


2
questo ha risposto a molti dei problemi apparentemente casuali in cui mi sono imbattuto con lo script bash.
Daniel Agans,

Questa risposta perfetta mi turba così tanto e spiega un comportamento davvero strano nel nostro sistema CI.
KayCee

108

Il problema è che i processi messi insieme con una pipe vengono eseguiti in subshell (e quindi hanno un proprio ambiente). Qualunque cosa accada all'interno whiledel tubo non influisce su nulla al di fuori del tubo.

Il tuo esempio specifico può essere risolto riscrivendo la pipe in

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

o forse

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

32
Per coloro che stanno guardando questo confuso su quale sia l'intera sintassi <() (come lo ero io), si chiama "Sostituzione del processo", e l'uso specifico descritto sopra può essere visto qui: mywiki.wooledge.org/ProcessSubstitution
Ross Aiken

2
La sostituzione del processo è qualcosa che tutti dovrebbero usare regolarmente! È super utile. Faccio qualcosa come vimdiff <(grep WARN log.1 | sort | uniq) <(grep WARN log.2 | sort | uniq)ogni giorno. Considera che puoi usarne più in una volta e trattarli come file ... POSSIBILITÀ!
Bruno Bronosky

8

Anche questo dovrebbe funzionare (perché echo e while sono nella stessa sottoshell):

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

3

Un'altra opzione:

#!/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: qui, xsel è un requisito (installalo). In alternativa, puoi usare xclip: xclip -i -selection clipboard invece di xsel -i -p


Ottengo un errore: ./scraper.sh: riga 111: xsel: comando non trovato ./scraper.sh: riga 114: xsel: comando non trovato
3kstc

@ 3kstc ovviamente, installa xsel. Inoltre, puoi usare xclip, ma il suo utilizzo è leggermente diverso. Punto principale qui: 1 ° metti l'output negli appunti (3 di loro in Linux), 2 ° - lo prendi da lì e lo mandi a 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

vedere se questi cambiamenti aiutano


Quando si esegue questa operazione, ora ottengo uno "0" da stampare per l'ultima istruzione echo. tuttavia mi aspetto che il valore sia 1 non zero. Inoltre, perché l'uso dell'esportazione? Presumo che lo costringa nell'ambiente?
Matt P

0

Un'altra opzione è l'output dei risultati in un file dalla subshell e quindi leggerlo nella shell genitore. qualcosa di simile a

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

0

Ho aggirato questo quando stavo creando il mio piccolo 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" )

Il punto è che creo una subshell con () contenente la mia variabile SUM e il while, ma inserisco l'intero () invece che il tempo stesso, il che evita il gotcha.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.