web-dev-qa-db-fra.com

Capture de plusieurs lignes dans une variable Bash

J'ai un script 'myscript' qui affiche les éléments suivants:

abc
def
ghi

dans un autre script, j'appelle:

declare RESULT=$(./myscript)

et $RESULT obtient la valeur

abc def ghi

Existe-t-il un moyen de stocker le résultat avec les nouvelles lignes ou avec le caractère '\ n' afin que je puisse l'afficher avec 'echo -e'?

524
Parker

En fait, le résultat contient ce que vous voulez - pour démontrer:

echo "$RESULT"

Ce que vous montrez est ce que vous obtenez:

echo $RESULT

Comme indiqué dans les commentaires, la différence est que (1) la version entre guillemets de la variable (echo "$RESULT") préserve l’espacement interne de la valeur exactement telle qu’elle est représentée dans la variable - saut de ligne, onglets, plusieurs blancs et tout - alors que (2) la version sans guillemets (echo $RESULT) remplace chaque séquence d'un ou plusieurs espaces, onglets et nouvelles lignes par un seul espace. Ainsi (1) conserve la forme de la variable d’entrée, alors que (2) crée une seule ligne de sortie potentiellement très longue avec des "mots" séparés par des espaces simples (où un "mot" est une séquence de caractères non blancs; pas être alphanumériques dans aucun des mots).

985
Jonathan Leffler

Un autre piège avec ceci est que substitution de commande - $() - supprime les nouvelles lignes. Probablement pas toujours important, mais si vous voulez vraiment conserver exactement ce qui a été produit, vous devrez utiliser une autre ligne et quelques citations:

RESULTX="$(./myscript; echo x)"
RESULT="${RESULTX%x}"

Ceci est particulièrement important si vous voulez gérer tous les noms de fichiers possibles (pour éviter tout comportement indéfini, comme opérer sur le mauvais fichier).

85
l0b0

Si vous êtes intéressé par des lignes spécifiques, utilisez un tableau de résultats:

declare RESULT=($(./myscript))  # (..) = array
echo "First line: ${RESULT[0]}"
echo "Second line: ${RESULT[1]}"
echo "N-th line: ${RESULT[N]}"
17
user2574210

En plus de la réponse donnée par @ l0b0, je venais juste de me trouver dans la situation où je devais conserver toutes les nouvelles lignes de sortie du script et vérifier le code de retour du script. Et le problème avec la réponse de l0b0 est que 'echo x' réinitialisait $? retour à zéro ... alors j'ai réussi à trouver cette solution très rusé:

RESULTX="$(./myscript; echo x$?)"
RETURNCODE=${RESULTX##*x}
RESULT="${RESULTX%x*}"
15
Lurchman

Que diriez-vous de cela, il lira chaque ligne à une variable et pourra être utilisé par la suite! dire que la sortie myscript est redirigée vers un fichier nommé myscript_output

awk '{while ( (getline var < "myscript_output") >0){print var;} close ("myscript_output");}'
0
Rahul Reddy

Après avoir essayé la plupart des solutions ici, la chose la plus simple que j’ai trouvée était l’évidence: utiliser un fichier temporaire. Je ne suis pas sûr de ce que vous voulez faire avec votre sortie à lignes multiples, mais vous pouvez ensuite gérer cela ligne par ligne en utilisant read. La seule chose que vous ne pouvez pas vraiment faire est de coller facilement le tout dans la même variable, mais dans la plupart des cas, c'est plus facile à gérer.

./myscript.sh > /tmp/foo
while read line ; do 
    echo 'whatever you want to do with $line'
done < /tmp/foo

Rapide bidouille pour lui faire faire l'action demandée:

result=""
./myscript.sh > /tmp/foo
while read line ; do
  result="$result$line\n"
done < /tmp/foo
echo -e $result

Notez que cela ajoute une ligne supplémentaire. Si vous travaillez dessus, vous pouvez coder, je suis trop paresseux.


EDIT: Bien que ce cas fonctionne parfaitement bien, les personnes qui lisent ceci doivent savoir que vous pouvez facilement écraser votre stdin à l'intérieur de la boucle while, vous donnant ainsi un script permettant d'exécuter une ligne, d'effacer stdin et de quitter. Comme ssh va faire ça je pense? Je viens de le voir récemment, d'autres exemples de code ici: https://unix.stackexchange.com/questions/24260/reading-lines-from-a-file-with-bash-for-vs-while =

Encore une fois! Cette fois avec un descripteur de fichier différent (stdin, stdout, stderr sont 0-2, nous pouvons donc utiliser & 3 ou plus dans bash).

result=""
./test>/tmp/foo
while read line  <&3; do
    result="$result$line\n"
done 3</tmp/foo
echo -e $result

vous pouvez également utiliser mktemp, mais il ne s'agit que d'un exemple de code rapide. Utilisation pour mktemp ressemble à:

filenamevar=`mktemp /tmp/tempXXXXXX`
./test > $filenamevar

Utilisez ensuite $ filenamevar comme vous le feriez avec le nom réel d’un fichier. Il n'est probablement pas nécessaire de l'expliquer ici, mais quelqu'un s'est plaint dans les commentaires.

0
user1279741