Pregunta

initiate () {
read -p "Location(s) to look for .bsp files in? " loc
find $loc -name "*.bsp" | while read
do
    if [ -f "$loc.bz2" ]
    then
        continue
    else
        filcount=$[$filcount+1]
        bzip $loc
    fi
    if [ "$scan" == "1" ]; then bzipint $loc
    fi
    echo $filcount    #Correct counting
    echo $zipcount    #Correct counting
    echo $scacount    #Correct counting
    echo $valid       #Equal to 1
done

echo $filcount    #Reset to 0
echo $zipcount    #Reset to 0
echo $scacount    #Reset to 0
echo $valid       #Still equal to 1
}

Estoy escribiendo un script de shell bash para usar bzip2 Para encender todo .bsp archivos dentro de un directorio. En este script tengo varias variables para contar totales (archivos, zips exitosos, escaneos de integridad exitosos), sin embargo, parece que me he encontrado con un problema.

Cuando find $loc -name "*.bsp" se queda sin archivos para dar el while read y while read sale, sale $filcount, $zipcount y $scacount (todos los cuales se cambian (aumentan) en el interior initiate (), bzip () (que se llama durante initiate ()) o bzipint () (que también se llama en initiate ()).

Para probar si tiene algo que ver con las variables que cambian dentro initiate () u otras funciones a las que se accede desde él, utilicé Echo $valid, que se define fuera de initiate () (me gusta $filcount, $zipcount, etc.), pero no se cambia de otra función dentro initiate () o dentro initiate () sí mismo.

Suficientemente interesante, $valid no se reinicia a 0 como las otras variables dentro del inicio.

¿Alguien puede decirme por qué mis variables se reinician mágicamente cuando se lee?

¿Fue útil?

Solución

Ayer me encontré con este problema.

El problema es que estás haciendo find $loc -name "*.bsp" | while read. Porque esto involucra una tubería, el while read Loop no se puede ejecutar en el mismo proceso de bash que el resto de su script; Bash tiene que generar un subproceso para que pueda conectar el stdout de find al stdin del while círculo.

Todo esto es muy inteligente, pero significa que las variables establecidas en el bucle no se pueden ver después del bucle, lo que derrotó totalmente a todo el propósito del while bucle que estaba escribiendo.

Puede intentar alimentar la entrada al bucle sin usar una tubería, o obtener salida del bucle sin usar variables. Terminé con una horrible abominación que involucraba tanto la escritura en un archivo temporal como envolviendo todo el bucle en $(...), al igual que:

var="$(producer | while read line; do
    ...
    echo "${something}"
done)"

Lo que me consiguió VAR en todas las cosas que se habían hecho eco desde el bucle. Probablemente arruiné la sintaxis de ese ejemplo; No tengo el código que escribí a Handy en este momento.

Otros consejos

Si usas Bash

while read
do
    if [ -f "$REPLY.bz2" ]
    then
        continue
    else
        filcount=$[$filcount+1]
        bzip $REPLY
    fi
    if [ "$scan" == "1" ]; then bzipint $REPLY
    fi
    echo $filcount    #Correct counting
    echo $zipcount    #Correct counting
    echo $scacount    #Correct counting
    echo $valid       #Equal to 1
done < <(find $loc -name "*.bsp")

A Resumir opciones por usando read Al final de [el equivalente conceptual de] una tubería En conchas tipo Posix:

Para recapitular: en bash de forma predeterminada y en shells estrictamente compatibles con Posix siempre, Todos los comandos en una tubería se ejecutan en un subshell, asi que Las variables que crean o modifican no serán visibles para el Actual caparazón (No existirá después de que termine la tubería).

El seguimiento cubiertas bash, ksh, zsh, y sh ([en su mayoría] conchas de solo características de Posix como dash) y shows formas de evitar la creación de una subshell para preservar las variables creadas / modificadas por read.

Si no se da un número de versión mínimo, suponga que incluso las versiones "bastante antiguas" lo admiten (las características en cuestión han existido durante mucho tiempo, pero no sé específicamente cuándo se introdujeron.

Tenga en cuenta que como una alternativa [compatible con POSIX] a las soluciones a continuación Siempre puede capturar la salida de un comando en un archivo [temporal] y luego alimentarlo a read como < file, que también evita subshells.


ksh, y zsh: No se necesita cambio de solución/configuración en absoluto:

los read incorporado por defecto corre en el Actual cáscara cuando se usa como el ultimo comando en la tubería.

Aparentemente, ksh y zsh por defecto correr ningún comando en el ultimo etapa de una tubería en el Actual caparazón.
Observado en ksh 93u+ y zsh 5.0.5.
Si sabe específicamente en qué versión se introdujo esta función, avíseme.

#!/usr/bin/env ksh
#!/usr/bin/env zsh

out= # initialize output variable

# Pipe multiple lines to the `while` loop and collect the values in the output variable.
printf '%s\n' one two three | 
 while read -r var; do
   out+="$var/"
 done

echo "$out" # -> 'one/two/three/'

bash 4.2+: utilizar el lastpipe opción de shell

En Bash, versión 4.2 o superior, encendiendo la opción de shell lastpipe hace que el último segmento de la tubería se ejecute en el Actual shell, permitiendo que la lectura cree variables visibles para el shell actual.

#!/usr/bin/env bash

shopt -s lastpipe # bash 4.2+: make the last pipeline command run in *current* shell

out=
printf '%s\n' one two three | 
 while read -r var; do
   out+="$var/"
 done

echo "$out" # -> 'one/two/three/'

bash, ksh, zsh: usar sustitución de procesos

Hablando libremente, una sustitución del proceso es una forma de tener una actuación de salida de comando como un archivo temporal.

out=
while read -r var; do
  out+="$var/"
done < <(printf '%s\n' one two three) # <(...) is the process substitution

echo "$out" # -> 'one/two/three'

bash, ksh, zsh: utilizar una aquí con un sustitución de comandos

out=
while read -r var; do
  out+="$var/"
done <<< "$(printf '%s\n' one two three)" # <<< is the here-string operator

echo "$out" # -> 'one/two/three'

Tenga en cuenta la necesidad de cotizar doble la sustitución del comando para proteger su salida de expansiones de concha.


Solución compatible con Posix (sh): utilizar una aquí-documento con un sustitución de comandos

#!/bin/sh

out=
while read -r var; do
  out="$out$var/"
done <<EOF # <<EOF ... EOF is the here-doc
$(printf '%s\n' one two three)
EOF

echo "$out" # -> 'one/two/three'

Tenga en cuenta que, por defecto, debe colocar el delimitador final - EOF, en este caso, al comienzo de la línea, y que ningún personaje debe seguirlo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top