Le variabili che ottengono ripristino dopo il tempo di leggere ciclo che legge da un oleodotto

StackOverflow https://stackoverflow.com/questions/7313491

Domanda

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
}

Sto scrivendo uno script di shell bash per usare bzip2 per comprimere tutti i file .bsp all'interno di una directory. In questo script ho diverse variabili per i totali di conteggio (file, cerniere di successo, scansioni di integrità di successo), ma mi sembra di aver incontrato un problema.

Quando find $loc -name "*.bsp" esaurisce file per dare le uscite while read e while read, azzera fuori $filcount, $zipcount e $scacount (che sono tutti modificato (aumentato) all'interno initiate (), bzip () (che si chiama durante initiate ()) o bzipint () (che è chiamato anche in initiate ()).

Per prova se esso è a che fare con le variabili cambiando initiate () all'interno o altre funzioni accessibili da esso, ho usato eco $valid, che è definita al di fuori di initiate () (come $filcount, $zipcount, ecc), ma non è cambiato da un'altra initiate () funzione all'interno o initiate () suo interno.

È interessante notare che, $valid non viene reimpostato a 0, come le altre variabili all'interno avviare.

Qualcuno può dirmi il motivo per cui le mie variabili magicamente diventano resettato quando, mentre leggere uscite?

È stato utile?

Soluzione

mi sono imbattuto in questo problema ieri.

Il problema è che si sta facendo find $loc -name "*.bsp" | while read. Poiché si tratta di un tubo, il ciclo while read non può di fatto essere in esecuzione nello stesso processo bash come il resto dello script; bash deve generare un sottoprocesso modo che possa collegare lo stdout di find al stdin del ciclo while.

Questo è tutto molto intelligente, ma significa che tutte le variabili impostate nel ciclo non può essere visto dopo il ciclo, che totalmente sconfitto l'intero scopo del ciclo while che stavo scrivendo.

Puoi provare ad alimentare input per il ciclo senza l'utilizzo di un tubo, o ottenere l'uscita dal loop senza l'utilizzo di variabili. Ho finito con un abominio terrificante che coinvolge sia la scrittura di un file temporaneo e avvolgendo l'intero ciclo in $(...), in questo modo:

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

Il che mi ha fatto var insieme a tutte le cose che erano state eco dal loop. Probabilmente ho incasinato la sintassi di questo esempio; Non ho il codice che ho scritto a portata di mano in questo momento.

Altri suggerimenti

se si utilizza 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")

Per le opzioni Riassumere per utilizzando read alla fine del [l'equivalente concettuale di] una pipeline in POSIX-come gusci:

Per ricapitolare: in bash di default e conchiglie rigorosamente POSIX-compliant sempre, tutti i comandi in una corsa condotta in un subshell , quindi variabili creano o di modifica non saranno visibili al corrente shell (non esisterà dopo la fine della pipeline).

Il seguente copertine bash, ksh, zsh, e sh ([principalmente] POSIX-caratteristiche di sola gusci come dash) e spettacoli modi per evitare la creazione di una subshell così come per preservare le variabili creato / modificato con read .

Se non viene dato alcun numero di versione minima, supporre che anche "abbastanza vecchie" versioni supportano (le caratteristiche in questione sono stati in giro per molto tempo, ma non so precisamente quando sono stati introdotti.

Si noti che come alternativa [POSIX-compliant] per le soluzioni di seguito si può sempre catturare l'output di un comando in un file [temporanea], e poi alimentarlo a read come < file, che evita anche sottoshell.


ksh, e zsh: nessuna soluzione modifica / configurazione necessaria a tutti:

Il comando incorporato read di default viene eseguito in corrente shell quando utilizzato come ultima di comando nella pipeline.

Apparentemente, ksh e zsh di default Esegui qualsiasi comando nella ultima tappa di un oleodotto nella corrente shell .
Osservato in ksh 93u+ e zsh 5.0.5.
Se si conosce in particolare in che versione questa funzionalità è stata introdotta, fatemelo sapere.

#!/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+: utilizzare l'opzione di shell lastpipe

Nella versione bash 4.2 o superiore, attivando l'opzione di shell lastpipe fa sì che l'ultimo segmento di conduttura per l'esecuzione in corrente conchiglia, permettendo di leggere per creare le variabili visibili alla shell corrente.

#!/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: uso sostituzione

In parole povere, una sostituzione di processo è un modo per avere atto output di un comando come un file temporaneo.

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: utilizzare un stringa here con un < a href = "http://mywiki.wooledge.org/CommandSubstitution" rel = "noreferrer"> comando cambio

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'

Si noti la necessità di raddoppiare-citare la sostituzione di comando per proteggere la sua uscita da espansioni della shell .


POSIX-compliant soluzione (sh): utilizzare un qui-documento con un la sostituzione di comando

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

Si noti che, per impostazione predefinita, è necessario inserire il delimitatore finale - EOF, in questo caso -. Proprio all'inizio della linea, e che non personaggi devono seguirla

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top