En el siguiente programa, si configuro la variable $foo
en el valor 1 dentro de la primera if
instrucción, funciona en el sentido de que su valor se recuerda después de la instrucción if. Sin embargo, cuando configuro la misma variable en el valor 2 dentro de un if
que está dentro de una while
declaración, se olvida después del while
ciclo. Se comporta como si estuviera usando algún tipo de copia de la variable $foo
dentro del while
bucle y estoy modificando solo esa copia en particular. Aquí hay un programa de prueba completo:
#!/bin/bash
set -e
set -u
foo=0
bar="hello"
if [[ "$bar" == "hello" ]]
then
foo=1
echo "Setting \$foo to 1: $foo"
fi
echo "Variable \$foo after if statement: $foo"
lines="first line\nsecond line\nthird line"
echo -e $lines | while read line
do
if [[ "$line" == "second line" ]]
then
foo=2
echo "Variable \$foo updated to $foo inside if inside while loop"
fi
echo "Value of \$foo in while loop body: $foo"
done
echo "Variable \$foo after while loop: $foo"
# Output:
# $ ./testbash.sh
# Setting $foo to 1: 1
# Variable $foo after if statement: 1
# Value of $foo in while loop body: 1
# Variable $foo updated to 2 inside if inside while loop
# Value of $foo in while loop body: 2
# Value of $foo in while loop body: 2
# Variable $foo after while loop: 1
# bash --version
# GNU bash, version 4.1.10(4)-release (i686-pc-cygwin)
bash
while-loop
scope
sh
Eric Lilja
fuente
fuente
SC2030: Modification of foo is local (to subshell caused by pipeline).
Respuestas:
los
while
bucle se ejecuta en una subshell. Por lo tanto, los cambios que realice en la variable no estarán disponibles una vez que la subshell salga.En su lugar, puede usar una cadena here para volver a escribir el ciclo while para estar en el proceso de shell principal; solo
echo -e $lines
se ejecutará en una subshell:Puede deshacerse de lo bastante feo
echo
en la cadena de aquí arriba expandiendo las secuencias de barra invertida inmediatamente al asignarlines
. La$'...'
forma de cita se puede usar allí:fuente
<<< "$(echo -e "$lines")"
a simple<<< "$lines"
tail -f
lugar de texto fijo?while read -r line; do echo "LINE: $line"; done < <(tail -f file)
(obviamente, el bucle no terminará ya que continúa esperando la entrada detail
).while
bucle o elfor
bucle; más bien el uso de subshell, es decir, incmd1 | cmd2
,cmd2
está en un subshell. Entonces, sifor
se ejecuta un bucle en una subshell, se mostrará el comportamiento inesperado / problemático.ACTUALIZADO # 2
La explicación está en la respuesta de Blue Moons.
Soluciones alternativas:
Eliminar
echo
Agregue el eco dentro del documento aquí-es-el-documento
Ejecutar
echo
en segundo plano:Redireccionar a un identificador de archivo explícitamente (¡Cuidado con el espacio
< <
!):O simplemente redirigir a
stdin
:Y uno para
chepner
(eliminarecho
):La variable
$lines
se puede convertir en una matriz sin iniciar un nuevo sub-shell. Los caracteres\
yn
se deben convertir a algún carácter (por ejemplo, un carácter de línea real nuevo) y utilizar la variable IFS (Separador de campo interno) para dividir la cadena en elementos de la matriz. Esto se puede hacer como:El resultado es
fuente
lines
único propósito de la variable parece ser alimentar elwhile
ciclo.for line in $(echo -e $lines); do ... done
Usted es el 742342º usuario que hace estas preguntas frecuentes sobre bash. La respuesta también describe el caso general de las variables establecidas en subcapas creadas por tuberías:
fuente
Hmmm ... casi juraría que esto funcionó para el shell Bourne original, pero no tengo acceso a una copia en ejecución ahora para verificar.
Sin embargo, hay una solución muy trivial al problema.
Cambie la primera línea del guión de:
a
Et voila! Una lectura al final de una tubería funciona bien, suponiendo que tenga instalado el shell Korn.
fuente
Utilizo stderr para almacenar dentro de un bucle, y leo desde afuera. Aquí var i se establece inicialmente y se lee dentro del bucle como 1.
O utilice el método heredoc para reducir la cantidad de código en una subshell. Tenga en cuenta que el valor iterativo i se puede leer fuera del ciclo while.
fuente
¿Qué tal un método muy simple
fuente
Esta es una pregunta interesante y toca un concepto muy básico en Bourne shell y subshell. Aquí proporciono una solución que es diferente de las soluciones anteriores haciendo algún tipo de filtrado. Daré un ejemplo que puede ser útil en la vida real. Este es un fragmento para verificar que los archivos descargados se ajustan a una suma de verificación conocida. El archivo de suma de comprobación tiene el siguiente aspecto (mostrando solo 3 líneas):
El script de shell:
El padre y el subshell se comunican a través del comando echo. Puede elegir un texto fácil de analizar para el shell principal. Este método no rompe su forma normal de pensar, solo que tiene que hacer un procesamiento posterior. Puede usar grep, sed, awk y más para hacerlo.
fuente