web-dev-qa-db-fra.com

Lecture de la sortie d'une commande dans un tableau dans Bash

J'ai besoin de lire le résultat d'une commande de mon script dans un tableau. La commande est, par exemple:

ps aux | grep | grep | x 

et il donne la sortie ligne par ligne comme ceci:

10
20
30

J'ai besoin de lire les valeurs de la sortie de la commande dans un tableau, puis je ferai un travail si la taille du tableau est inférieure à trois.

69
barp

Les autres réponses se briseront si la sortie de la commande contient des espaces (ce qui est assez fréquent) ou des caractères globaux comme *, ?, [...].

Pour obtenir la sortie d'une commande dans un tableau, il y a essentiellement 2 façons:

  1. Avec Bash≥4, utilisez mapfile— c'est le plus efficace:

    mapfile -t my_array < <( my_command )
    
  2. Sinon, une boucle lisant la sortie (plus lente mais sûre):

    my_array=()
    while IFS= read -r line; do
        my_array+=( "$line" )
    done < <( my_command )
    

Vous verrez probablement beaucoup de cela:

my_array=( $( my_command) )

Mais ne l'utilisez pas! Regarde comme c'est cassé:

$ # this is the command used to test:
$ echo "one two"; echo "three four"
one two
three four
$ my_array=( $( echo "one two"; echo "three four" ) )
$ declare -p my_array
declare -a my_array='([0]="one" [1]="two" [2]="three" [3]="four")'
$ # Not good! now look:
$ mapfile -t my_array < <(echo "one two"; echo "three four")
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # Good!

Ensuite, certaines personnes recommandent d’utiliser IFS=$'\n' pour résoudre ce problème:

$ IFS=$'\n'
$ my_array=( $(echo "one two"; echo "three four") )
$ declare -p my_array
declare -a my_array='([0]="one two" [1]="three four")'
$ # It works!

Mais maintenant, utilisons une autre commande:

$ echo "* one two"; echo "[three four]"
* one two
[three four]
$ IFS=$'\n'
$ my_array=( $(echo "* one two"; echo "[three four]") )
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="t")'
$ # What?

C'est parce que j'ai un fichier appelé t dans le répertoire en cours… et que ce nom de fichier correspond à glob[three four]… À ce stade, certaines personnes recommandent d'utiliser set -f pour désactiver le globbing: mais regardez: vous devez changer IFS et utiliser set -f pour pouvoir réparer une technique cassée (et vous ne la réparez même pas vraiment)! En faisant cela, nous sommes vraiment en train de lutter contre le Shell, pas travaillant avec le Shell.

$ mapfile -t my_array < <( echo "* one two"; echo "[three four]")
$ declare -p my_array
declare -a my_array='([0]="* one two" [1]="[three four]")'

ici, nous travaillons avec Shell!

98
gniourf_gniourf

Vous pouvez utiliser

my_array=( $(<command>) )

pour stocker le résultat de la commande <command> dans le tableau my_array.

Vous pouvez accéder à la longueur de ce tableau en utilisant

my_array_length=${#my_array[@]}

Maintenant, la longueur est stockée dans my_array_length.

Imaginez que vous allez placer les noms de fichiers et de répertoires (sous le dossier actuel) dans un tableau et compter ses éléments. Le script serait comme;

my_array=( `ls` )
my_array_length=${#my_array[@]}
echo $my_array_length

Ou, vous pouvez parcourir ce tableau en ajoutant le script suivant:

for element in "${my_array[@]}"
do
   echo "${element}"
done
8
Youness