GNU/Linux >> Tutoriels Linux >  >> Linux

Comprendre "ifs=Lire -r Ligne" ?

Je comprends évidemment que l'on peut ajouter de la valeur à la variable de séparation de champ interne. Par exemple :

$ IFS=blah
$ echo "$IFS"
blah
$ 

Je comprends également que read -r line enregistrera les données de stdin à la variable nommée line :

$ read -r line <<< blah
$ echo "$line"
blah
$ 

Cependant, comment une commande peut-elle affecter une valeur variable ? Et stocke-t-il d'abord les données de stdin à la variable line puis donner la valeur de line à IFS ?

Réponse acceptée :

Dans les shells POSIX, read , sans aucune option ne lit pas une ligne , il lit mots à partir d'une ligne (éventuellement suivie d'une barre oblique inverse), où les mots sont $IFS délimité et barre oblique inverse peuvent être utilisés pour échapper les délimiteurs (ou continuer les lignes).

La syntaxe générique est :

read word1 word2... remaining_words

read lit stdin un octet à la fois¹ jusqu'à ce qu'il trouve un caractère de retour à la ligne sans échappement (ou de fin d'entrée), le divise selon des règles complexes et stocke le résultat de cette division dans $word1 , $word2$remaining_words .

Par exemple sur une entrée comme :

  <tab> foo bar baz   blah   blah
whatever whatever

et avec la valeur par défaut de $IFS , read a b c attribuerait :

  • $afoo
  • $bbar baz
  • $cblah blahwhatever whatever

Maintenant, si un seul argument est passé, cela ne devient pas read line . C'est toujours read remaining_words . Le traitement de la barre oblique inverse est toujours effectué, les caractères d'espacement IFS² sont toujours supprimés du début et de la fin.

Le -r L'option supprime le traitement de la barre oblique inverse. Donc, cette même commande ci-dessus avec -r attribuerait plutôt

  • $afoo
  • $bbar
  • $cbaz blah blah

Maintenant, pour la partie fractionnement, il est important de réaliser qu'il existe deux classes de caractères pour $IFS  :les caractères d'espacement IFS² (y compris l'espace et la tabulation (et la nouvelle ligne, bien qu'ici cela n'a pas d'importance à moins que vous n'utilisiez -d), qui se trouvent également 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=: (: n'étant pas un caractère d'espace blanc IFS), une entrée comme :foo::bar:: serait divisé en "" , "foo" , "" , bar et "" (et un "" supplémentaire avec certaines implémentations, cela n'a pas d'importance sauf pour read -a ). Alors que si nous remplaçons ce : avec un espace, le découpage se fait uniquement en foo et bar . C'est-à-dire que ceux de début et de fin sont ignorés, et leurs séquences sont traitées comme une seule. Il existe des règles supplémentaires lorsque des espaces blancs et des caractères non blancs sont combinés dans $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'espacement non échappés de début et de fin soient supprimés, nous devons supprimer ces caractères d'espacement IFS d'IFS.

Même avec des caractères IFS non blancs, si la ligne d'entrée contient un (et un seul) de ces caractères et qu'il s'agit du dernier caractère de la ligne (comme IFS=: read -r word sur une entrée comme foo: ) avec des shells POSIX (pas zsh ni certains pdksh versions), cette entrée est considérée comme un foo mot car dans ces shells, les caractères $IFS sont considérés comme des terminateurs , donc word contiendra foo , pas foo: .

Ainsi, la manière canonique de lire une ligne d'entrée avec le read intégré est :

IFS= read -r line

(notez que pour la plupart des read implémentations, qui ne fonctionnent que pour les lignes de texte car le caractère NUL n'est pas pris en charge sauf dans zsh ).

Connexe :Linux – Partager des fichiers entre l'hôte Linux et l'invité Windows ?

Utilisation de var=value cmd la syntaxe s'assure que IFS n'est défini différemment que pour la durée de cette cmd commande.

Note historique

Le read builtin a été introduit par le shell Bourne et devait déjà lire des mots , pas de lignes. Il existe quelques différences importantes avec les shells POSIX modernes.

Le read du shell Bourne n'a pas pris en charge un -r option (qui a été introduite par le shell Korn), il n'y a donc aucun moyen de désactiver le traitement de la barre oblique inverse autre que le prétraitement de l'entrée avec quelque chose comme sed 's/\/&&/g' là.

Le shell Bourne n'avait pas cette notion de deux classes de caractères (qui a de nouveau été introduite par ksh). Dans le shell Bourne, tous les caractères subissent le même traitement que les caractères d'espacement IFS dans ksh, c'est-à-dire 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 un élément intégré (comme read est), var reste défini sur value après cmd avoir fini. C'est particulièrement critique avec $IFS car dans le shell Bourne, $IFS est utilisé pour tout diviser, pas seulement les expansions. Aussi, si vous supprimez le caractère espace de $IFS dans le shell Bourne, "[email protected]" ne fonctionne plus.

Dans le shell Bourne, la redirection d'une commande composée la fait s'exécuter dans un sous-shell (dans les premières versions, même des choses comme read var < file ou exec 3< file; read var <&3 n'a pas fonctionné), il était donc rare dans le shell Bourne d'utiliser read pour tout sauf l'entrée de l'utilisateur sur le terminal (où cette gestion de la continuation de ligne avait du sens)

Certains Unix (comme HP/UX, il y en a aussi un dans util-linux ) ont toujours une line commande pour lire une ligne d'entrée (qui était une commande UNIX standard jusqu'à la version 2 de la spécification UNIX unique).

C'est fondamentalement la même chose que head -n 1 sauf qu'il lit un octet à la fois pour s'assurer qu'il ne lit pas plus d'une ligne. Sur ces systèmes, vous pouvez :

line=`line`

Bien sûr, cela signifie générer un nouveau processus, exécuter une commande et lire sa sortie via un tube, donc beaucoup moins efficace que la ligne IFS= read -r line de ksh , mais toujours beaucoup plus intuitif.


Linux
  1. Comprendre les Si ?

  2. Boîte occupée lire le fichier ligne par ligne ?

  3. Lire un fichier orienté ligne qui ne se termine peut-être pas par une nouvelle ligne ?

  4. Utilitaire de ligne de commande pour récupérer le mot de passe, qui n'a pas d'écho en retour ?

  5. Comprendre le bureau Linux ?

Comprendre les autorisations de fichiers Linux

Commande de lecture bash

Comment lire un fichier ligne par ligne dans Bash

Commande Diff sous Linux

Comprendre les processus sous Linux

Comment lire les arguments de ligne de commande dans les scripts shell ?