Les variables obtenir remise à zéro après la lecture en boucle qui lit à partir d'un pipeline
-
26-10-2019 - |
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?
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