Q1. Fractionnement de champs.
La division de champ est-elle identique à la division de mot?
Oui, les deux pointent vers la même idée.
Q2: Quand IFS est-il nul ?
La définition est IFS=''
-elle identique à null, identique à une chaîne vide?
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.
Dans la spécification POSIX, j'ai lu ce qui suit :
Si IFS n'est pas défini, le shell se comportera comme si la valeur de IFS était <espace> <tab> <nouvelle ligne> .
Ce qui est exactement équivalent à:
Avec un unset IFS
, le shell doit se comporter comme si IFS était la valeur par défaut.
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 variable OldIFS
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.
Comment pourrais-je restaurer la valeur d'IFS par défaut. Dites que je veux restaurer la valeur par défaut de IFS. Comment je fais ça? (plus spécifiquement, comment me référer à <tab> et à <nouvelle ligne> ?)
Pour zsh, ksh et bash (autant que je sache), IFS pourrait être défini sur la valeur par défaut comme suit:
IFS=$' \t\n' # works with zsh, ksh, bash.
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> :
sh -c 'IFS=$(echo " \n\t"); printf "%s" "$IFS"|xxd' # Works.
3.- Un simple? Solution:
Si certains scripts enfants ont besoin d'IFS correctement défini, vous pouvez toujours écrire manuellement:
IFS = '
'
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
:
sh -c 'IFS=$(echo " \t\n"); printf "%s" "$IFS"|xxd' # wrong.
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:
sh -c 'IFS="$(printf " \t\nx")"; IFS="${IFS%x}"; printf "$IFS"|xxd' # Correct.
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:
env IFS=$' \t\n' sh -c 'printf "%s" "$IFS"|xxd'
En bref, sh fait de la réinitialisation d’IFS une situation par défaut plutôt étrange.
Q4: En code actuel:
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
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 :
#!/bin/ksh
# Correctly set IFS as described above.
defIFS="$(printf " \t\nx")"; defIFS="${defIFS%x}";
IFS="$defIFS"
printf "IFS value: "
printf "%s" "$IFS"| xxd -p
a=' bar baz quz '; l="${#a}"
printf "var value : %${l}s-" "$a" ; printf "%s\n" "$a" | xxd -p
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf 'Values quoted :\n' "" # With values quoted:
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS default quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space quoted : %${l}s-" "$line" ;
printf "%s" "$line" |xxd -p; done;
printf '%s\n' "Values unquoted :" # Now with values unquoted:
printf "%s\n" "$a" | while IFS='x' read -r line; do
printf "IFS --x-- unquoted : "
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS='' read -r line; do
printf "IFS null unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
printf "%s\n" "$a" | while IFS="$defIFS" read -r line; do
printf "IFS defau unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
unset IFS; printf "%s\n" "$a" | while read -r line; do
printf "IFS unset unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
IFS="$defIFS" # set IFS back to default.
printf "%s\n" "$a" | while IFS=' ' read -r line; do
printf "IFS space unquoted : ";
printf "%s, " $line; printf "%s," $line |xxd -p; done
Je reçois:
$ ./stackexchange-Understanding-IFS.sh
IFS value: 20090a
var value : bar baz quz -20202062617220202062617a20202071757a2020200a
IFS --x-- : bar baz quz -20202062617220202062617a20202071757a202020
Values quoted :
IFS null quoted : bar baz quz -20202062617220202062617a20202071757a202020
IFS default quoted : bar baz quz-62617220202062617a20202071757a
IFS unset quoted : bar baz quz-62617220202062617a20202071757a
IFS space quoted : bar baz quz-62617220202062617a20202071757a
Values unquoted :
IFS --x-- unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS null unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS defau unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS unset unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
IFS space unquoted : bar, baz, quz, 6261722c62617a2c71757a2c
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,
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.