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.


¿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

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


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

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 )

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


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


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)

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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.