Le piège est que
IFS=; while read..
définit le IFS
pour tout l'environnement shell en dehors de la boucle, alors que
while IFS= read
le redéfinit uniquement pour l' read
invocation (sauf dans le shell Bourne). Vous pouvez vérifier que faire une boucle comme
while IFS= read xxx; ... done
puis après cette boucle, echo "blabalbla $IFS ooooooo"
imprime
blabalbla
ooooooo
alors qu'après
IFS=; read xxx; ... done
les IFS
séjours redéfinis: echo "blabalbla $IFS ooooooo"
imprime maintenant
blabalbla ooooooo
Donc , si vous utilisez la deuxième forme, vous devez vous rappeler de réinitialiser: IFS=$' \t\n'
.
La deuxième partie de cette question a été fusionnée ici . J'ai donc supprimé la réponse correspondante d'ici.
rozcietrzewiacz
la source
while IFS=X read
ne se sépare pasX
, maiswhile IFS=X; read
...while
ne fait pas beaucoup de sens - la condition pourwhile
extrémités à ce point - virgule, donc il n'y a pas de boucle réelle ...read
devient juste la première commande dans la boucle d' un élément ou non ... ? Qu'en est-il dudo
moment ..?while
condition (avantdo
).IFS=
, maisIFS=X
ne fonctionne pas ... (ou peut-être que je suis sur le même sujet depuis un moment ... une pause-café est nécessaire :)Regardons un exemple, avec du texte d'entrée soigneusement conçu:
C'est deux lignes, la première commençant par un espace et se terminant par une barre oblique inverse. Tout d’abord, regardons ce qui se passe sans précaution
read
(mais en utilisantprintf '%s\n' "$text"
pour imprimer soigneusement$text
sans aucun risque d’agrandissement). (Ci-dessous,$
l'invite du shell.)read
a mangé les barres obliques inverses: la barre oblique inversée-newline a pour effet d’ignorer la nouvelle ligne et la barre oblique inversée-tout ignore cette première barre oblique inversée. Pour éviter que les antislashs soient traités spécialement, nous utilisonsread -r
.C'est mieux, nous avons deux lignes comme prévu. Les deux lignes contiennent presque le contenu souhaité: le double espace entre
hello
etworld
a été conservé, car il se trouve dans laline
variable. D'autre part, l'espace initial était épuisé. C’est parce queread
lit autant de mots que vous transmettez les variables, sauf que la dernière variable contient le reste de la ligne - mais elle commence toujours par le premier mot, c’est-à-dire que les espaces initiaux sont supprimés.Donc, pour lire chaque ligne littéralement, nous devons nous assurer qu’aucun fractionnement de mots n’est en cours. Nous faisons cela en définissant la
IFS
variable sur une valeur vide.Notez comment nous avons défini
IFS
spécifiquement pour la durée de la fonctionread
intégrée . LesIFS= read -r line
ensembles de la variable d'environnementIFS
(pour une valeur vide) spécifiquement pour l'exécutionread
. Il s'agit d'une instance de la syntaxe de commande simple générale : une séquence (éventuellement vide) d'assignations de variables suivie d'un nom de commande et de ses arguments (vous pouvez également insérer des redirections à tout moment). Comme ilread
s'agit d'un paramètre intégré, la variable ne se termine jamais dans l'environnement d'un processus externe; néanmoins, la valeur de$IFS
est ce que nous attribuons là-bas tant qu’ilread
est exécuté¹. Notez que ceread
n’est pas un élément intégré spécial , l’affectation ne dure que pour sa durée.Nous veillons donc à ne pas modifier la valeur de
IFS
pour d’autres instructions qui peuvent en dépendre. Ce code fonctionnera quel que soit le codeIFS
initial défini par le code environnant et ne causera aucun problème si le code contenu dans la boucle s'appuie surIFS
.Contraste avec cet extrait de code, qui recherche les fichiers dans un chemin séparé par deux-points. La liste des noms de fichiers est lue dans un fichier, un nom de fichier par ligne.
Si la boucle était
while IFS=; read -r name; do …
, alorsfor dir in $PATH
ne serait pas divisé$PATH
en composants séparés par des deux-points. Si le code l'étaitIFS=; while read …
, il serait encore plus évident que ceIFS
ne soit pas défini:
dans le corps de la boucle.Bien sûr, il serait possible de restaurer la valeur de
IFS
après exécutionread
. Mais cela nécessiterait de connaître la valeur précédente, ce qui représente un effort supplémentaire.IFS= read
est le moyen le plus simple (et, idéalement, aussi le plus court).¹ Et, s’il
read
est interrompu par un signal piégé, éventuellement pendant l’exécution de la trappe, cela n’est pas spécifié par POSIX et dépend du shell en pratique.la source
while IFS= read
(sans point-virgule après=
) n’est pas une forme spéciale dewhile
ou deIFS
ou deread
.. La construction est générique: ie.anyvar=anyvalue anycommand
. Le manque de;
réglage aprèsanyvar
rend la portée deanyvar
local àanycommand
.. La boucle while - do / done est 100% indépendante de la portée locale deany_var
.Mis à part le (déjà clarifié) les
IFS
différences de cadrage entre leswhile IFS='' read
,IFS=''; while read
et leswhile IFS=''; read
idiomes (par-commande vs script / shell largeIFS
portée variable), la leçon à retenir est que vous perdez les plus grands et les espaces de fin d'une ligne d'entrée si la variable IFS est défini sur (contient un) espace.Cela peut avoir des conséquences assez graves si les chemins de fichiers sont en cours de traitement.
Par conséquent, définir la variable IFS sur la chaîne vide est tout sauf une mauvaise idée, car cela garantit que les espaces de début et de fin d'une ligne ne sont pas supprimés.
Voir aussi: Bash, lire ligne par ligne à partir d'un fichier, avec IFS
la source
Inspiré par la réponse de Yuzem
Si vous voulez définir
IFS
un personnage réel, cela a fonctionné pour moila source