Quelle est la signification de IFS = $ '\ n' dans les scripts bash?

163

Au début d'un script shell bash se trouve la ligne suivante:

IFS=$'\n'

Quelle est la signification de cette collection de symboles?

Abdul Al Hazred
la source
3
Voir aussi unix.stackexchange.com/questions/26784/understanding-ifs et les questions qu’il cite.
Gilles
La syntaxe vim met cela en évidence comme une erreur pour moi; réparer?
theonlygusti
IFS=$'\n'est un penchant (+ d’autres coquillages, utilisez la
citation

Réponses:

199

IFSest synonyme de "séparateur de champ interne". Le shell l’utilise pour déterminer comment scinder le mot, c’est-à-dire reconnaître les limites de mot.

Essayez ceci dans un shell comme bash (d'autres shells peuvent gérer cela différemment, par exemple zsh):

mystring="foo:bar baz rab"
for word in $mystring; do
  echo "Word: $word"
done

La valeur par défaut pour se IFScompose de caractères d'espacement (pour être précis: espace, tabulation et nouvelle ligne). Chaque caractère peut être une limite de mot. Donc, avec la valeur par défaut de IFS, la boucle ci-dessus affichera:

Word: foo:bar
Word: baz
Word: rab

En d'autres termes, le shell pense que les espaces sont une limite de mots.

Maintenant, essayez de régler IFS=:avant d’exécuter la boucle. Cette fois, le résultat est:

Word: foo
Word: bar baz rab

Le shell se divise également mystringen mots - mais à présent, il ne traite que les deux points comme la limite de mot.

Le premier caractère de IFSest spécial: il est utilisé pour délimiter des mots dans la sortie lorsque vous utilisez la $*variable spéciale (exemple tiré du Guide de script de Bash avancé , où vous pouvez également trouver plus d'informations sur des variables spéciales comme celle-ci):

$ bash -c 'set w x y z; IFS=":-;"; echo "$*"'
w:x:y:z

Comparer aux:

$ bash -c 'set w x y z; IFS="-:;"; echo "$*"'
w-x-y-z

Notez que dans les deux exemples, la coquille continue de traiter tous les personnages :, -et ;que les limites de mots. La seule chose qui change est le comportement de $*.

Une autre chose importante à savoir concerne le traitement de ce que l'on appelle les "espaces blancs IFS" . Fondamentalement, dès que IFSles caractères d'espacement sont inclus, les espaces de début et de fin sont supprimés de la chaîne à scinder avant d'être traités et une séquence de caractères d'espacement consécutifs délimite également les champs. Toutefois, cela ne s'applique qu'aux caractères d'espacement réellement présents dans IFS.

Par exemple, regardons la chaîne "a:b:: c d "(espace de fin et deux espaces entre cet d).

  1. Avec IFS=:elle serait divisée en quatre champs: "a", "b", ""(chaîne vide) et " c d "(encore une fois, deux espaces entre cet d). Notez les espaces de début et de fin dans le dernier champ.
  2. Avec IFS=' :', il serait divisé en cinq domaines: "a", "b", ""(chaîne vide), "c"et "d". Aucun espace de début et de fin nulle part.

Notez que plusieurs caractères d'espacement consécutifs délimitent deux champs dans le deuxième exemple, tandis que plusieurs points consécutifs ne le sont pas (car ils ne sont pas des caractères d'espacement).

En ce qui concerne IFS=$'\n', qui est une ksh93syntaxe également pris en charge par bash, zsh, mkshet FreeBSD sh(avec des variations entre les coquilles). Citant la page de manuel bash:

Les mots de la forme $ 'chaîne' sont traités spécialement. Le mot se développe en "chaîne", avec les caractères d'échappement avec une barre oblique inversée remplacés comme spécifié par la norme ANSI C.

\nest la séquence d'échappement pour une nouvelle ligne, IFSfinit par être défini sur un seul caractère de nouvelle ligne.

Tblue
la source
3
C'est bien, mais à mon avis, vous feriez mieux de lire et de comprendre la spécification POSIX plutôt que le bashguide de script, ou peu importe. Dans l’ensemble, l’information disponible sur des liens comme celui-ci manque de façon importante. Quoi qu’il en soit, il manque donc deux points cruciaux concernant la division du shell - le globbing et les espaces blancs IFS.
mikeserv
@ mikeserv Merci, j'ai ajouté des informations sur les espaces IFS. Je ne savais pas à ce sujet. :)
Tblue
4
Pas aussi pertinent, mais si vous êtes curieux, vous voudrez peut-être comprendre comment unset IFSun shell se comporte de manière très différente de IFS=. Le premier octet dans IFS est également spécial "${named_array[*]}"- mais cela n’a aucune importance lorsque l’extension n’est pas citée ..
mikeserv le
Quelques points supplémentaires: Le fractionnement d'un mot, régi par $IFSest l'une des deux choses principales exécutées lors de l'expansion d'une variable non citée dans le contexte d'une liste (c'est la splitpartie de l' split+globopérateur). L'autre est globbing. Lorsque vous utilisez le fractionnement du travail, vous devez généralement émettre set -fpour désactiver la globpièce.
Stéphane Chazelas
3
3- $IFSest également utilisé par la readcommande intégrée
Stéphane Chazelas le
22

Dans les guillemets simples en dollars, certains caractères sont évalués spécialement. Par exemple, \nest traduit en nouvelle ligne.

Donc, cette ligne particulière affecte newline à la variable IFS. IFS, à son tour, est une variable spéciale dans bash: séparateur de champ interne. Comme man bashdit, il

est utilisé pour séparer les mots après le développement et pour séparer les lignes en mots avec la readcommande interne. La valeur par défaut est <space><tab><newline>.

choroba
la source
5
+1 pour indiquer dollared single quotesce qui est différent des simples guillemets simples.
Snowcrash
2
@Snowcrash +1 pour avoir dit +1 pour la mention de guillemets simples libellés en dollars, ce qui est différent des guillemets simples . Désolé, je ne pouvais pas m'en empêcher :) Mais en réalité, c'est une très bonne chose à souligner, car c'est important!
Pryftan le
1
@Pryftan +1 pour +1 pour +1 pour ... tu sais ... c'est vraiment important.
0xc0de
@ 0xc0de certainement d'accord! Merci pour ça! :)
Pryftan le
15

Pour faire court, IFS=$'\n'attribuez newline \nà variable IFS.

$'string'construct est un mécanisme de citation qui décode les séquences d'échappement de type ANSI C. Cette syntaxe provient ksh93et est portable shell moderne comme bash, zsh, pdksh, busybox sh.

Cette syntaxe n'est pas définie par POSIX, mais a été acceptée pour le problème 7 de SUS .

cuonglm
la source
-1

J'ai préféré expliquer $IFSpar l'exemple:
supposons que vous vouliez utiliser cp, mv ou un autre processus de traitement de fichier, IFS est vide par défaut, lorsque vos fichiers ont un caractère méta ou un espace tel que:
Linux Administration.pdfou Free Software Fundation.ogg, bien sûr, vous aurez un problème, car: Linux considère un séparer param et Administartion considèrent un séparateur séparé.Alors bash a built-in variable, ensuite vous pouvez initialiser à IFS==$(echo -en "\n\b"), puis bash ignorer tout caractère méta et l'espace entre le nom de fichier, Par exemple:

#!/bin/bash
SAVEIFS=$IFS
IFS=$(echo -en "\n\b")
mymusicdir=~/test/dd
find $mymusicdir -name "*" -execdir rename 's/ /_/g' "{}" +
IFS=$SAVEIFS
Golfe Persique
la source