Les quelques discussions suivantes sur ce site et StackOverflow ont été utiles pour comprendre comment cela IFS
fonctionne:
- Qu'est-ce que IFS dans le contexte de la mise en boucle?
- Comment boucler sur les lignes d'un fichier
- Bash, lire ligne par ligne à partir d'un fichier, avec IFS
Mais j'ai encore quelques petites questions. J'ai décidé de leur poser la même question car je pense que cela pourrait aider de meilleurs lecteurs:
Q1. IFS
est typiquement discuté dans le contexte de "division de champ". La division de champ est -elle identique à la division de mot ?
Q2: La spécification POSIX dit :
Si la valeur de IFS est nulle, aucune division de champ ne doit être effectuée.
La définition est IFS=
-elle identique à la valeur IFS
null? Est-ce que c'est ce que l'on entend par le mettre à un empty string
aussi?
Q3: Dans la spécification POSIX, j'ai lu ce qui suit:
Si IFS n'est pas défini, le shell doit se comporter comme si la valeur de IFS était
<space>, <tab> and <newline>
Dites que je veux restaurer la valeur par défaut de IFS
. Comment je fais ça? (plus spécifiquement, comment puis-je me référer à <tab>
et <newline>
?)
Q4: Enfin, comment ce code pourrait-il:
while IFS= read -r line
do
echo $line
done < /path_to_text_file
se comporter si nous nous changeons la première ligne
while read -r line # Use the default IFS value
ou pour:
while IFS=' ' read -r line
IFS
et un non définiIFS
sont très différents. La réponse à la question 4 est en partie fausse: les séparateurs intérieurs ne sont pas touchés ici, ils sont les premiers et les derniers.IFS
, toutes signifientIFS=
.IFS=' ' ; foo=( bar baz qux ) ; echo "${#foo[@]}"
. (Euh, quoi? Il devrait y avoir plusieurs délimiteurs d'espace, le moteur SO continue de les supprimer).read
; la dernière variable saisit tout ce qui reste sauf le dernier séparateur et laisse les séparateurs internes à l'intérieur.Q1: oui «Fractionnement de champs» et «fractionnement de mots» sont deux termes qui désignent le même concept.
Q2: oui Si
IFS
non défini (c'est-à-dire aprèsunset IFS
), il est équivalent àIFS
être défini sur$' \t\n'
(un espace, un onglet et une nouvelle ligne). SiIFS
est défini sur une valeur vide (c'est ce que “null” signifie ici) (c'est-à-dire aprèsIFS=
ouIFS=''
ouIFS=""
), aucune division de champ n'est effectuée (et$*
, qui utilise normalement le premier caractère de$IFS
, utilise un caractère d'espacement).Q3: Si vous souhaitez avoir le
IFS
comportement par défaut , vous pouvez utiliserunset IFS
. Si vous souhaitez définirIFS
explicitement cette valeur par défaut, vous pouvez mettre les caractères littéraux espace, tabulation et nouvelle ligne entre guillemets simples. Dans ksh93, bash ou zsh, vous pouvez utiliserIFS=$' \t\n'
. De manière portable, si vous voulez éviter d'avoir un caractère de tabulation littéral dans votre fichier source, vous pouvez utiliserQ4: avec
IFS
une valeur vide,read -r line
définitline
la ligne entière à l'exception de la nouvelle ligne de fin. AvecIFS=" "
, les espaces au début et à la fin de la ligne sont supprimés. Avec la valeur par défaut deIFS
, les tabulations et les espaces sont supprimés.la source
$@
, il existe certaines variations entre les shells dans des contextes non listés, par exempleIFS=; var=$@
). Il est à noter que lorsque IFS est vide, aucun fractionnement de mot n’est effectué mais $ var est toujours étendu à aucun argument au lieu d’un argument vide lorsque $ var est vide, et le globbing s’applique toujours. Vous devez donc toujours citer des variables (même désactiver globbing)Q1. Fractionnement de champs.
Oui, les deux pointent vers la même idée.
Q2: Quand IFS est-il nul ?
Oui, les trois signifient la même chose: aucune division de champ / mot ne doit être effectuée. En outre, cela affecte les champs d'impression (comme avec
echo "$*"
) tous les champs seront concaténés ensemble sans espace.Q3: (partie a) IFS non défini.
Ce qui est exactement équivalent à:
Cela signifie que la "division de champ" sera exactement la même chose avec une valeur IFS par défaut, ou non définie.
Cela ne signifie PAS que l'IFS fonctionnera de la même manière dans toutes les conditions. Étant plus spécifique, l'exécution
OldIFS=$IFS
définira la variableOldIFS
sur null , pas sur la valeur par défaut. Et essayer de remettre IFS en arrière, comme ceci,IFS=OldIFS
mettra IFS à null, ne le laissera pas non défini comme auparavant. Fais attention !!.Q3: (partie b) Restaurez IFS.
Pour zsh, ksh et bash (autant que je sache), IFS pourrait être défini sur la valeur par défaut comme suit:
Fait, vous n'avez besoin de lire rien d'autre.
Mais si vous devez redéfinir IFS pour sh, cela peut devenir complexe.
Jetons un coup d'oeil du plus facile au complet sans aucun inconvénient (sauf la complexité).
1.- Désactiver IFS.
Nous pourrions juste
unset IFS
(Lire Q3 partie a, ci-dessus.).2.- Permuter les caractères.
En guise de solution de contournement, permuter les valeurs de tabulation et de nouvelle ligne simplifie la définition de la valeur de IFS et fonctionne de manière équivalente.
Définissez IFS sur <espace> <nouvelle ligne> <tab> :
3.- Un simple? Solution:
Si certains scripts enfants ont besoin d'IFS correctement défini, vous pouvez toujours écrire manuellement:
Où la séquence tapée manuellement était:,
IFS=
'spacetabnewline'séquence qui a effectivement été tapée correctement ci-dessus (si vous devez confirmer, éditez cette réponse). Mais un copier / coller de votre navigateur va casser parce que le navigateur va serrer / cacher les espaces. Il est difficile de partager le code comme indiqué ci-dessus.4.- Solution complète.
Écrire du code qui peut être copié en toute sécurité implique généralement des échappées imprimables sans ambiguïté.
Nous avons besoin de code qui "produit" la valeur attendue. Mais, même si conceptuellement correct, ce code NE définira PAS de fin
\n
:Cela se produit parce que, dans la plupart des shells, toutes les nouvelles lignes
$(...)
ou les`...`
substitutions de commandes sont supprimées lors de l’agrandissement.Nous devons utiliser un truc pour sh:
Une autre solution consiste à définir IFS en tant que valeur d’environnement de bash (par exemple), puis d’appeler sh (les versions qui acceptent que IFS soit défini via l’environnement), comme suit:
En bref, sh fait de la réinitialisation d’IFS une situation par défaut plutôt étrange.
Q4: En code actuel:
Premièrement: je ne sais pas si le
echo $line
(avec le var NON cité) est là sur le porpouse, ou pas. Il introduit un deuxième niveau de "division de champ" que la lecture n'a pas. Je vais donc répondre aux deux. :)Avec ce code (pour que vous puissiez confirmer). Vous aurez besoin du xxd utile :
Je reçois:
La première valeur est juste la valeur correcte de
IFS=
'spacetabnewline'La ligne suivante
$a
contient toutes les valeurs hexadécimales de la variable et une nouvelle ligne '0a' à la fin, telle qu'elle sera donnée à chaque commande de lecture.La ligne suivante, pour laquelle IFS est null, n'effectue aucune «division de champ», mais la nouvelle ligne est supprimée (comme prévu).
Les trois lignes suivantes, IFS contenant un espace, supprimez les espaces initiaux et définissez la ligne var sur le solde restant.
Les quatre dernières lignes montrent ce que fera une variable non citée. Les valeurs seront réparties sur les espaces (plusieurs) et seront imprimées comme suit:
bar,baz,qux,
la source
unset IFS
efface IFS, même si IFS est par la suite présumé être "\ t \ n":Testé sur les versions 4.2.45 et 3.2.25 de Bash avec le même comportement.
la source
unset
deIFS
, comme expliqué dans les commentaires de la réponse acceptée ici.