Les variables obtenir remise à zéro après la lecture en boucle qui lit à partir d'un pipeline

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

Question

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
}

J'écris un script shell bash utiliser bzip2 pour zip tous les fichiers dans un répertoire .bsp. Dans ce script, j'ai plusieurs variables pour les totaux de comptage (fichiers, fermetures éclair, avec succès des contrôles d'intégrité avec succès), mais il me semble avoir rencontré un problème.

Lorsque find $loc -name "*.bsp" court de fichiers pour donner les while read et sort while read, il zéros $filcount, $zipcount et $scacount (qui sont tous changé (augmenté) à l'intérieur initiate (), bzip () (qui est appelé au cours initiate ()) ou bzipint () (ce qui est également appelé à initiate ()).

Afin de tester si elle est quelque chose à voir avec des variables changeant à l'intérieur initiate () ou d'autres fonctions accessibles depuis, j'utilisé l'écho $valid, qui est définie en dehors de initiate () (comme $filcount, $zipcount, etc.), mais n'a pas changé de une autre fonction à l'intérieur ou à l'intérieur initiate () initiate () lui-même.

Il est intéressant de $valid ne soit pas remis à 0 comme les autres variables initient à l'intérieur.

Quelqu'un peut-il me dire pourquoi mes variables sont remplacées par magie remis à zéro lorsque while read sorties?

Était-ce utile?

La solution

je suis tombé sur ce problème hier.

Le problème est que vous faites find $loc -name "*.bsp" | while read. Parce que cela implique un tuyau, la boucle de while read ne peut pas être fait en cours d'exécution dans le même processus bash que le reste de votre script; bash a pour frayer un sous-off de manière à pouvoir connecter la sortie standard de la find à l'entrée standard de la boucle de while.

Ceci est très intelligent, mais cela signifie que toutes les variables définies dans la boucle ne peut pas être vu après la boucle, qui a totalement vaincu le but de la boucle de while j'écrivais.

Vous pouvez soit pour alimenter l'entrée à la boucle sans l'aide d'un tuyau, ou obtenir une sortie de la boucle sans utiliser des variables. Je me suis retrouvé avec une abomination horrifiant impliquant à la fois l'écriture dans un fichier temporaire et enveloppant la boucle entière dans $(...), comme suit:

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

Ce qui m'a ensemble var à toutes les choses qui avaient été résonnaient de la boucle. J'ai probablement foiré la syntaxe de cet exemple; Je n'ai pas le code que j'écrit à portée de main pour le moment.

Autres conseils

si vous utilisez 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")

Options Résumer en utilisant read à la fin de [l'équivalent conceptuel de] un pipeline dans POSIX comme des coquilles:

Pour récapituler: en bash par défaut et strictement coquilles conformes POSIX toujours, toutes les commandes dans une course de pipeline dans sous-shell , donc des variables ils créent Modifier ou ne seront pas visibles à l'écran courant shell (ne sera pas exister après que les extrémités des pipelines).

Voici couvre bash, ksh, zsh et sh ([principalement] coquilles POSIX caractéristiques uniquement telles que dash) et montre des façons de en évitant la création d'un sous-shell afin pour préserver les variables créées / modifiées par read .

Si aucun numéro de version minimale est donnée, supposons que même les versions « assez vieux » soutiennent (les caractéristiques en question ont été autour depuis longtemps, mais je ne sais pas précisément quand ils ont été introduits.

Notez que comme une alternative [POSIX conforme] aux solutions ci-dessous vous pouvez toujours capturer la sortie de l'ordre dans un fichier [temporaire], puis le nourrir à read comme < file, ce qui évite également les sous-shells.


ksh et zsh: pas de solution / changement de configuration nécessaire à tous:

La commande intégrée read par défaut fonctionne dans courant shell lorsqu'il est utilisé comme dernier commande en pipeline.

Apparemment, ksh et zsh par défaut run une commande dernier étape d'un pipeline dans le courant shell.
Observé à ksh 93u+ et zsh 5.0.5.
Si vous savez précisément dans quelle version cette fonctionnalité a été introduite, laissez-moi savoir.

#!/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+: utilisez l'option shell lastpipe

Dans la version bash 4.2 ou plus, activant l'option shell lastpipe provoque le dernier segment de pipeline à courir dans la courant shell, ce qui permet de lire pour créer des variables visibles à l'enveloppe actuelle.

#!/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: utilisation substitution

Grosso modo, une substitution de processus est un moyen d'avoir l'acte de sortie d'une commande comme un fichier temporaire.

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: utilisez un ici-string avec < a href = "http://mywiki.wooledge.org/CommandSubstitution" rel = "noreferrer"> commande de substitution

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'

Notez la nécessité de guillemet la substitution de commandes pour protéger sa sortie de extensions shell .


solution (sh) POSIX conforme: l'utilisation d'un ici-documents avec commande

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

Notez que, par défaut, vous devez placer délimiteur de fin - EOF, dans ce cas -. Au début de la ligne, et qu'aucun caractère doivent suivre

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top