Alcance de la variable Bash

104

Por favor, explíqueme por qué la última echodeclaración está en blanco. Espero que XCODEse incremente en el ciclo while a un valor de 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

Intenté usar la siguiente declaración en lugar del ++XCODEmétodo

XCODE=`expr $XCODE + 1`

y tampoco se imprimirá fuera de la declaración while. Creo que me falta algo sobre el alcance variable aquí, pero la vieja página de manual no me lo muestra.

Matt P
fuente
¿Dónde inicializa XCODE a algo que se pueda incrementar?
Paul Tomblin
Intenté lanzar un "XCODE = 0" en la parte superior del código, fuera de la declaración while
Matt P
Sin el cruft, funciona para mí. #! / bin / bash para i en 1 2 3 4 5; do echo $ ((++ XCODE)) hecho echo "fin:" $ XCODE Creo que su problema no tiene nada que ver con el alcance de las variables y todo que ver con lo que está sucediendo mientras tanto.
Paul Tomblin
De acuerdo ... ¿Parece que tiene que ver con el ciclo "while read"?
Matt P
Hay un FAQ de Bash sobre esto: mywiki.wooledge.org/BashFAQ/024
Fabio dice Reinstate Monica

Respuestas:

117

Debido a que está entrando en el bucle while, se crea un sub-shell para ejecutar el bucle while.

Ahora, este proceso hijo tiene su propia copia del entorno y no puede pasar ninguna variable a su padre (como en cualquier proceso Unix).

Por lo tanto, deberá reestructurar para que no se conecte al circuito. Alternativamente, puede ejecutar una función, por ejemplo, y echoel valor que desea devolver del subproceso.

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

pixelbeat
fuente
2
esto solo respondió a muchos de los problemas aparentemente aleatorios con los que me estaba encontrando con las secuencias de comandos bash.
Daniel Agans
Esta respuesta perfecta me molesta mucho y explica un comportamiento realmente extraño en nuestro sistema de CI.
KayCee
108

El problema es que los procesos combinados con una tubería se ejecutan en subcapas (y por lo tanto tienen su propio entorno). Pase lo que pase dentro whilede la tubería no afecta nada fuera de la tubería.

Su ejemplo específico se puede resolver reescribiendo la tubería a

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

o quizás

while ... do ... done < <(echo "$OUTPUT")
mweerden
fuente
32
Para aquellos que miran esto confundidos en cuanto a cuál es toda la sintaxis <() (como yo), se llama "Sustitución de proceso", y el uso específico detallado anteriormente se puede ver aquí: mywiki.wooledge.org/ProcessSubstitution
Ross Aiken
2
¡La sustitución de procesos es algo que todos deberían usar con regularidad! Es super util. Hago algo como vimdiff <(grep WARN log.1 | sort | uniq) <(grep WARN log.2 | sort | uniq)todos los días. Considere que puede utilizar varios a la vez y tratarlos como archivos ... ¡POSIBILIDADES!
Bruno Bronosky
8

Esto debería funcionar también (porque echo y while están en la misma subcapa):

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

Una opción más:

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

EDITAR: Aquí, xsel es un requisito (instalarlo). Alternativamente, puede usar xclip: en xclip -i -selection clipboard lugar de xsel -i -p

Rammix
fuente
Recibo un error: ./scraper.sh: línea 111: xsel: comando no encontrado ./scraper.sh: línea 114: xsel: comando no encontrado
3kstc
@ 3kstc obviamente, instale xsel. Además, puede usar xclip, pero su uso es un poco diferente. El punto principal aquí: primero, coloca la salida en un portapapeles (3 de ellos en Linux), segundo: lo toma desde allí y lo envía 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

mira si esos cambios ayudan

Kent Fredric
fuente
Al hacer esto, ahora obtengo un "0" para imprimir para la última declaración de eco. sin embargo, espero que el valor sea 1 y no cero. Además, ¿por qué el uso de la exportación? ¿Supongo que lo fuerza al medio ambiente?
Matt P
0

Otra opción es generar los resultados en un archivo desde el subshell y luego leerlo en el shell principal. algo como

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

Superé esto cuando estaba haciendo mi propio pequeño 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" )

El punto es que hago una subcapa con () que contiene mi variable SUM y el while, pero canalizo al entero () en lugar de al while en sí, lo que evita el problema.

Adrian mayo
fuente