J'ai une variable qui contient la sortie multiligne d'une commande. Quel est le moyen le plus efficace de lire la sortie ligne par ligne à partir de la variable?
Par exemple:
jobs="$(jobs)"
if [ "$jobs" ]; then
# read lines from $jobs
fi
Vous pouvez utiliser une boucle while avec une substitution de processus:
while read -r line
do
echo "$line"
done < <(jobs)
Un moyen optimal de lire une variable multiligne consiste à définir une IFS
variable vierge et printf
la variable avec un retour à la fin:
# Printf '%s\n' "$var" is necessary because printf '%s' "$var" on a
# variable that doesn't end with a newline then the while loop will
# completely miss the last line of the variable.
while IFS= read -r line
do
echo "$line"
done < <(printf '%s\n' "$var")
Remarque: conformément à la vérification du shell sc2031 , l'utilisation d'un processus est préférable à un tuyau pour éviter [subtilement] la création d'un sous-shell.
Sachez également qu'en nommant la variable, jobs
vous risquez de créer une confusion, puisqu'il s'agit également du nom d'une commande shell commune.
while IFS= read
.... Si vous souhaitez éviter \ interprétation, utilisezread -r
echo
enprintf %s
, afin que votre script fonctionne même avec une entrée non apprivoisée./tmp
répertoire soit en écriture, car il dépend de la possibilité de créer un fichier de travail temporaire. Si vous vous trouvez sur un système restreint/tmp
en lecture seule (et que vous ne pouvez pas le modifier), vous serez heureux de la possibilité d’utiliser une autre solution, par exemple avec leprintf
tuyau.printf "%s\n" "$var" | while IFS= read -r line
Pour traiter la sortie d'une commande ligne par ligne ( explication ):
Si vous avez déjà les données dans une variable:
printf %s "$foo"
est presque identique àecho "$foo"
, mais affiche$foo
littéralement, alors queecho "$foo"
peut être interprété$foo
comme une option de la commande echo si elle commence par un-
, et peut développer des séquences de barres obliques inverses$foo
dans certains shells.Notez que dans certains shells (ash, bash, pdksh, mais pas ksh ou zsh), le côté droit d'un pipeline s'exécute dans un processus séparé, de sorte que toute variable définie dans la boucle est perdue. Par exemple, le script de comptage de lignes suivant imprime 0 dans ces shells:
Une solution de contournement consiste à placer le reste du script (ou au moins la partie nécessitant la valeur de
$n
la boucle) dans une liste de commandes:Si agir sur les lignes non vides est suffisant et que l'entrée n'est pas énorme, vous pouvez utiliser le fractionnement des mots:
Explication: le réglage
IFS
sur une nouvelle ligne entraîne la division des mots uniquement sur les lignes nouvelles (par opposition à tout caractère blanc sous le paramètre par défaut).set -f
désactive globbing (extension de caractères génériques), ce qui arriverait autrement au résultat d'une substitution de commande$(jobs)
ou d'une variable substitutino$foo
. Lafor
boucle agit sur toutes les parties de$(jobs)
, qui sont toutes les lignes non vides de la sortie de la commande. Enfin, restaurez la navigation et lesIFS
paramètres.la source
local IFS=something
. Cela n'affectera pas la valeur globale. IIRC,unset IFS
ne vous ramène pas à la valeur par défaut (et ne fonctionne certainement pas si ce n'était pas la valeur par défaut au préalable).Problème: si vous utilisez la boucle while, elle s'exécutera dans un sous-shell et toutes les variables seront perdues. Solution: utiliser pour la boucle
la source
while read
boucle dans bash signifie que la boucle while est dans un sous-shell, donc les variables ne sont pas globales.while read;do ;done <<< "$var"
rend le corps de la boucle pas un sous-shell. (Récent bash a la possibilité de mettre le corps d'unecmd | while
boucle pas dans un sous-shell, comme ksh l'a toujours fait.)Dans les dernières versions de bash, utilisez
mapfile
oureadarray
pour lire efficacement le résultat de la commande dans des tableaux.Disclaimer: horrible exemple, mais vous pouvez toujours trouver une meilleure commande à utiliser que vous-même
la source
readarray
une fonction et appelez-la plusieurs fois.Références:
while
IFS
read
la source
-r
C'est une bonne idée aussi. Il empêche\` interpretation... (it is in your links, but its probably worth mentioning, just to round out your
IFS = `(ce qui est essentiel pour éviter de perdre des espaces)Vous pouvez utiliser <<< pour lire simplement la variable contenant les données séparées par une nouvelle ligne:
la source