Certaines personnes ont cette notion erronée qui read
est la commande de lire une ligne. Ce n'est pas.
read
lit les mots d'une ligne (éventuellement d'une barre oblique inversée), où les mots sont $IFS
délimités et une barre oblique inverse peut être utilisée pour échapper aux délimiteurs (ou aux lignes continues).
La syntaxe générique est la suivante:
read word1 word2... remaining_words
read
lit stdin un octet à la fois jusqu'à ce qu'il trouve un caractère de saut de ligne sans échappement (ou de fin d'entrée), qui se divise selon des règles complexes et stocke le résultat de cette division en $word1
, $word2
... $remaining_words
.
Par exemple sur une entrée comme:
<tab> foo bar\ baz bl\ah blah\
whatever whatever
et avec la valeur par défaut de $IFS
, read a b c
attribuerait:
$a
⇐ foo
$b
⇐ bar baz
$c
⇐ blah blahwhatever whatever
Maintenant, si on ne passe qu'un seul argument, cela ne devient pas read line
. C'est encore read remaining_words
. Le traitement de la barre oblique inverse est toujours effectué, les caractères IFS sont toujours supprimés au début et à la fin.
L' -r
option supprime le traitement des barres obliques inverses. Donc, cette même commande ci-dessus avec -r
au lieu d'attribuer
$a
⇐ foo
$b
⇐ bar\
$c
⇐ baz bl\ah blah\
Maintenant, pour la partie scission, il est important de comprendre qu’il existe deux classes de caractères $IFS
: les caractères IFS blancs (à savoir, espace et tabulation (et newline, bien que cela ne soit pas important sauf si vous utilisez -d), ce qui se produit également. être dans la valeur par défaut de $IFS
) et les autres. Le traitement de ces deux classes de caractères est différent.
Avec IFS=:
( :
étant pas un caractère IFS des espaces), une entrée comme :foo::bar::
serait divisé en ""
, "foo"
, ""
, bar
et ""
(et un supplément ""
avec certaines implémentations si cela ne importe pas , sauf pour read -a
). Tandis que si nous remplaçons cela :
par de l’espace, la division se fait uniquement en foo
et bar
. C’est-à-dire que les principales et les dernières sont ignorées et que leurs séquences sont traitées comme une seule. Il existe des règles supplémentaires lorsque des espaces et des caractères non-blancs sont combinés $IFS
. Certaines implémentations peuvent ajouter / supprimer le traitement spécial en doublant les caractères dans IFS ( IFS=::
ou IFS=' '
).
Donc, ici, si nous ne voulons pas que les caractères d’espace blanc qui restent et qui ne soient pas échappés soient supprimés, nous devons supprimer ces caractères d’espace blanc IFS de IFS.
Même avec les caractères IFS-non-blancs, si la ligne d’entrée contient un (et un seul) de ces caractères et que c’est le dernier caractère de la ligne (comme IFS=: read -r word
sur une entrée similaire foo:
) avec des shells POSIX ( zsh
ni certaines pdksh
versions), cette entrée est considéré comme l' un foo
mot parce que dans ces coquilles, les caractères $IFS
sont considérés comme terminateurs , donc word
contiendra foo
, non foo:
.
Ainsi, la manière canonique de lire une ligne d’entrée avec l’ read
intégré est:
IFS= read -r line
(notez que pour la plupart des read
implémentations, cela ne fonctionne que pour les lignes de texte car le caractère NUL n'est pas pris en charge, sauf dans zsh
).
L'utilisation de la var=value cmd
syntaxe permet de s'assurer IFS
que le paramétrage est différent pour la durée de cette cmd
commande.
Note d'histoire
Le read
construit a été introduit par le shell Bourne et devait déjà lire des mots , pas des lignes. Il existe quelques différences importantes avec les coques POSIX modernes.
Le shell Bourne read
ne prenant pas en charge une -r
option (introduite par le shell Korn), il n’ya donc aucun moyen de désactiver le traitement des barres obliques inverses autrement que de prétraiter l’entrée avec quelque chose de similaire sed 's/\\/&&/g'
.
Le shell Bourne n'avait pas cette notion de deux classes de caractères (qui a de nouveau été introduite par ksh). Dans le Bourne shell tous les personnages subissent le même traitement que IFS Espaces blancs font ksh, qui est IFS=: read a b c
sur une entrée comme foo::bar
attribuerait bar
à $b
, pas la chaîne vide.
Dans le shell Bourne, avec:
var=value cmd
Si cmd
est intégré (comme l' read
est), var
reste défini value
après la cmd
fin. C’est particulièrement critique $IFS
car dans le shell Bourne, tout $IFS
est utilisé, pas seulement les extensions. De même, si vous supprimez le caractère d'espacement $IFS
dans le shell Bourne, cela "$@"
ne fonctionnera plus.
Dans le shell Bourne, la redirection d’une commande composée l’exécute dans un sous-shell (dans les versions les plus anciennes, même si cela ne fonctionnait pas read var < file
ou exec 3< file; read var <&3
ne fonctionnait pas). Il était donc rare que le shell Bourne soit utilisé read
pour autre chose que la saisie de l’utilisateur sur le terminal. (où le traitement de continuation de ligne était logique)
Certains Unices (comme HP / UX, il y en a aussi un util-linux
) ont toujours une line
commande pour lire une ligne d’entrée (une commande UNIX standard jusqu’à la spécification Single UNIX version 2 ).
C'est fondamentalement la même chose, head -n 1
sauf qu'elle lit un octet à la fois pour s'assurer qu'elle ne lit pas plus d'une ligne. Sur ces systèmes, vous pouvez faire:
line=`line`
Bien sûr, cela signifie créer un nouveau processus, exécuter une commande et lire sa sortie via un tube, ce qui est beaucoup moins efficace que celui de ksh IFS= read -r line
, mais toujours beaucoup plus intuitif.