read -a array -d '\ n' <foo, exit code 1

8

Si j'essaye d'exécuter

read -a fooArr -d '\n' < bar

le code de sortie est 1 - même s'il accomplit ce que je veux; place chaque ligne de bardans un élément du tableau fooArr(en utilisant bash 4.2.37).

Quelqu'un peut-il expliquer pourquoi cela se produit


J'ai trouvé d'autres moyens de résoudre ce problème, comme ceux ci-dessous, donc ce n'est pas ce que je demande.

for ((i=1;; i++)); do
    read "fooArr$i" || break;
done < bar

ou

mapfile -t fooArr < bar
RasmusWL
la source

Réponses:

14

Ce qui doit être expliqué est que la commande semble fonctionner, pas son code de sortie

'\n'est composé de deux caractères: une barre oblique inverse \et une lettre n. Ce dont vous pensiez avoir besoin, c'était d' $'\n'un saut de ligne (mais ce ne serait pas bien non plus, voir ci-dessous).

L' -doption fait ceci:

  -d delim  continue until the first character of DELIM is read, rather
            than newline

Donc, sans cette option, readlirait jusqu'à une nouvelle ligne, diviserait la ligne en mots en utilisant les caractères $IFScomme séparateurs et placerait les mots dans le tableau. Si vous spécifiez -d $'\n', en définissant le délimiteur de ligne sur une nouvelle ligne, cela ferait exactement la même chose . Le réglage -d '\n'signifie qu'il sera lu jusqu'à la première barre oblique inverse (mais, encore une fois, voir ci-dessous), qui est le premier caractère de delim. Puisqu'il n'y a pas de barre oblique inverse dans votre fichier, le readse terminera à la fin du fichier et:

Exit Status:
The return code is zero, unless end-of-file is encountered, read times out,
or an invalid file descriptor is supplied as the argument to -u.

C'est pourquoi le code de sortie est 1.

Du fait que vous pensez que la commande a fonctionné, nous pouvons conclure qu'il n'y a pas d'espaces dans le fichier, de sorte qu'après readavoir lu l'intégralité du fichier dans l'espoir futile de trouver une barre oblique inverse, il sera divisé par des espaces (la valeur par défaut de $IFS), y compris les sauts de ligne. Ainsi, chaque ligne (ou chaque mot, si une ligne contient plusieurs mots) est stockée dans le tableau.

Le cas mystérieux de la barre oblique inversée

Maintenant, comment savais-je que le fichier ne contenait aucune barre oblique inverse? Parce que vous n'avez pas fourni le -rdrapeau à read:

  -r                do not allow backslashes to escape any characters

Donc, si vous aviez des barres obliques inverses dans le fichier, elles auraient été supprimées, à moins que vous n'en ayez deux de suite. Et, bien sûr, il y a des preuves qui readavaient un code de sortie de 1, ce qui démontre qu'il n'a pas trouvé de barre oblique inverse, donc il n'y en avait pas deux de suite non plus.

Plats à emporter

Bash ne serait pas bash s'il n'y avait pas de pièges cachés derrière à peu près toutes les commandes, et readne fait pas exception. En voici deux:

  1. Sauf indication contraire -r, readinterprètera les séquences d'échappement de barre oblique inverse. À moins que ce ne soit réellement ce que vous voulez (ce qui est occasionnellement, mais seulement occasionnellement), vous devez vous rappeler de spécifier -rpour éviter que les caractères disparaissent dans les rares cas où il y a des barres obliques inverses dans l'entrée.

  2. Le fait que readrenvoie un code de sortie de 1 ne signifie pas qu'il a échoué. Il a peut-être bien réussi, sauf pour trouver le terminateur de ligne. Soyez donc prudent avec une boucle comme celle-ci: while read -r LINE; do something with LINE; done car elle échouera do somethingavec la dernière ligne dans le cas rare où la dernière ligne n'a pas de nouvelle ligne à la fin.

  3. read -r LINE conserve les barres obliques inverses, mais ne préserve pas les espaces de début ou de fin.

rici
la source
Merci! Je pensais que j'avais essayé l'approche $ '\ n', mais je suppose que non: / bien de savoir à propos de -r cependant
RasmusWL
2
"le rare cas où la dernière ligne n'avait pas de nouvelle ligne" ne me semble pas être une raison impérieuse de conseiller "ne jamais utiliser while read". Sinon, réponse fantastique.
glenn jackman
@glennjackman: Vous pouvez utiliser while readtant que vous n'avez rien dans la boucle. Sinon, c'est un insecte qui attend de vous mordre - et croyez-moi, j'ai été mordu.
rici
2
La dernière ligne a toujours une nouvelle ligne. Voilà comment une ligne est définie: elle se termine par une nouvelle ligne. Si un fichier non vide ne se termine pas par une nouvelle ligne, il ne s'agit pas d'un fichier texte et vous ne pouvez pas utiliser d'outils de traitement de texte tels que l'utilitaire shell read.
Gilles 'SO- arrête d'être méchant'
2
@glennjackman: Moi, je parcours les fichiers en colonnes avec awk, principalement. Pour itérer des lignes entières où le fichier n'est pas trop gros, mapfilec'est plutôt cool. Pour un hack rapide et sale, j'utiliserai la boucle while interdite, mais j'ai arrêté de mettre cela dans les scripts de production. YMMV et moi avons adouci l'avertissement dans la réponse.
rici
4

C'est le comportement attendu:

Le code retour est nul, sauf en cas de fin de fichier, [...]

start cmd:> echo a b c | { read -a testarray; echo $?; }
0

start cmd:> echo -n a b c | { read -a testarray; echo $?; }
1
Hauke ​​Laging
la source