bash modifie son comportement en fonction de la valeur de la variable «IFS»

18

Lorsque je définit la IFSvariable sur un espace, bashtraite plusieurs espaces comme un seul espace ( myprogramest un programme qui imprime les arguments de ligne de commande qu'il reçoit):

IFS=" "
x="hello   hi   world"
./myprogram $x
argv[1] = hello
argv[2] = hi
argv[3] = world

Mais quand je mets la IFSvariable à une virgule, bashne traite pas plusieurs virgules comme une virgule:

IFS=","
x="hello,,,hi,,,world"
./myprogram $x
argv[1] = hello
argv[2] = 
argv[3] = 
argv[4] = hi
argv[5] = 
argv[6] = 
argv[7] = world

Pourquoi donc?

user267935
la source
Juste pour référence, "IFS" signifie séparateur de champ interne .
pr1268

Réponses:

21

Ceci est documenté dans man bash. Une seule occurrence de n'importe quel caractère dans IFS qui n'est pas un espace délimite un champ.

De man bash:

Le shell traite chaque caractère d'IFS comme un délimiteur et divise les résultats des autres extensions en mots utilisant ces caractères comme terminateurs de champ. Si IFS n'est pas défini, ou si sa valeur est exactement <space><tab><newline>la valeur par défaut, les séquences de <space>, <tab>et <newline>au début et à la fin des résultats des extensions précédentes sont ignorées, et toute séquence de caractères IFS qui ne se trouve pas au début ou à la fin sert à délimiter mots. Si IFS a une valeur autre que la valeur par défaut, les séquences de l'espace, de la tabulation et de la nouvelle ligne des caractères d'espacement sont ignorées au début et à la fin du mot, tant que le caractère d'espacement est dans la valeur d'IFS (un caractère d'espacement IFS ). Tout caractère dans IFS qui n'est pas un espace blanc IFS, ainsi que tout caractère d'espace blanc IFS adjacent, délimite un champ. Une séquence de caractères blancs IFS est également traitée comme un délimiteur. Si la valeur de IFS est nulle, aucun fractionnement de mot ne se produit. [Je souligne.]

Exemples: division de champ

Si IFS n'a pas de caractères d'espaces, alors les espaces sont inclus dans les champs:

$ ( IFS=',' x='one , two,three'; printf "<%s>\n" $x )
<one >
< two>
<three>

Si IFS a à la fois des blancs et une virgule, les séquences de blancs, suivies d'une virgule, suivies de séquences de blancs sont traitées comme un seul délimiteur:

$ ( IFS=' ,' x='one , two,three'; printf "<%s>\n" $x )
<one>
<two>
<three>

Les séquences de virgules sont interprétées comme des séquences de champs vides:

$ ( IFS=' ,' x='one,,,two,three'; printf "<%s>\n" $x )
<one>
<>
<>
<two>
<three>

Exemples: espaces blancs de début et de fin

Si IFS ne contient aucun espace, tous les espaces de début et de fin sont conservés dans les champs:

$ ( IFS=',' x='  one , two,three  ,'; printf "<%s>\n" $x )
<  one >
< two>
<three  >

Si IFS contient des blancs, toutes les séquences de blancs de début ou de fin sont supprimées:

$ ( IFS=' ,' x='  one , two,three  ,'; printf "<%s>\n" $x )
<one>
<two>
<three>
John1024
la source
peut-être vaut-il également la peine d’insister sur "alors les séquences des espaces, tabulations et sauts de caractères sont ignorées au début et à la fin du mot, tant que le caractère des espaces est dans la valeur de IFS"
Jeff Schaller
@JeffSchaller Excellente idée: je viens d'ajouter une section à ce sujet.
John1024
que faire si vous avez un fichier séparé par des tabulations avec des valeurs manquantes? c'est-à-dire que vous ne voulez pas que les séquences d'onglets soient traitées comme un seul onglet. De plus, les champs contiennent des virgules, vous ne pouvez donc pas l'utiliser comme délimiteur. La seule solution est-elle d'utiliser un autre délimiteur (pas des tabulations)?
Davos
@Davos Pour les données avec chaque champ délimité par un seul onglet, il peut être plus naturel d'utiliser d'autres outils qui gèrent cela facilement, comme awkavec l' -F'\t'option ou cut. Si vous avez une version récente de bash, vous pouvez également analyser les champs à l'aide readarrayde l' -d$'\t'option.
John1024