Quand puis-je utiliser un IFS temporaire pour le fractionnement du champ?

En bash, dites que vous avez var=abc , alors:

 $ IFS=. printf "%s\n" $var abc 

Cependant, une telle utilisation de l' IFS prend effet lors de la création d'un tableau:

 $ IFS=. arr=($var) $ printf "%s\n" "${arr[@]}" a b c 

C'est très pratique, bien sûr, mais où est-ce documenté? Une lecture rapide des sections sur les arrays ou la division de mots dans la documentation Bash ne donne aucune indication d'une façon ou d'une autre. Une search d' IFS travers la documentation d'une seule page ne fournit aucune indication sur cet effet non plus.

Je ne sais pas quand je peux faire de façon fiable:

 IFS=x do something 

Et attendez-vous à ce que l' IFS affecte le fractionnement du champ.

L'idée de base est que VAR=VALUE some-command met VAR en VALUE pour l'exécution de some-command quand some-command est une command externe, et il n'a pas plus de fantaisie que cela. Si vous combinez cette intuition avec une certaine connaissance du fonctionnement d'un shell, vous devriez find la bonne réponse dans la plupart des cas. La reference POSIX est "Commandes simples" dans le chapitre "Langage de command Shell" .

Si some-command est une command externe , VAR=VALUE some-command est équivalent à env VAR=VALUE some-command . VAR est exporté dans l'environnement d'une some-command , et sa valeur (ou l'absence d'une valeur) dans le shell ne change pas.

Si some-command est une fonction , alors VAR=VALUE some-command est équivalente à VAR=VALUE; some-command VAR=VALUE; some-command , c'est VAR=VALUE; some-command dire que l'affectation rest en place après le return de la fonction et que la variable n'est pas exscope dans l'environnement. La raison en est la design du shell Bourne (et par la suite avec compatibilité ascendante): il n'a pas été possible de sauvegarder et de restaurer des valeurs variables autour de l'exécution d'une fonction. L'export de la variable n'a pas de sens puisqu'une fonction s'exécute dans le shell lui-même. Cependant, ksh (y compris à la fois ATT ksh93 et ​​pdksh / mksh), bash et zsh implémentent le comportement plus utile où VAR est défini uniquement pendant l'exécution de la fonction (il est également exporté). En ksh , ceci est fait si la fonction est définie avec la function NAME … syntaxe ksh function NAME … , pas si elle est définie avec la syntaxe standard NAME () . En bash , cela se fait uniquement en mode bash, pas en mode POSIX (lorsqu'il est exécuté avec POSIXLY_CORRECT=1 ). Dans zsh , ceci est fait si l'option posix_builtins n'est pas définie; cette option n'est pas définie par défaut mais est activée par emulate sh ou emulate ksh .

Si some-command est un builtin, le comportement dépend du type de builtin. Les builtins spéciaux se comportent comme des fonctions. Les built-ins spéciaux sont ceux qui doivent être implémentés à l'intérieur du shell car ils affectent le shell d'état (par exemple, break affecte le stream de contrôle, cd affecte le directory courant, set affecte les parameters de position et les options …). Les autres architectures ne sont embeddedes que pour des raisons de performances et de commodité (la plupart du time, par exemple, la fonctionnalité bash printf -v ne peut être implémentée que par un builtin) et elles se comportent comme une command externe.

L'affectation a lieu après l'expansion de l'alias, donc si some-command est un alias , développez-la d'abord pour find ce qui se passe.

Notez que dans tous les cas, l'affectation est effectuée après l'parsing de la command line, y compris toute substitution de variable sur la command line elle-même. Donc var=a; var=b echo $var var=a; var=b echo $var affiche a , car $var est évalué avant que l'affectation n'ait lieu. Et donc IFS=. printf "%s\n" $var IFS=. printf "%s\n" $var utilise l'ancienne valeur IFS pour split $var .

J'ai couvert tous les types de commands, mais il y a un autre cas: quand il n'y a pas de command à exécuter , c'est-à-dire si la command consiste uniquement en assignations (et éventuellement en redirections). Dans ce cas, la mission rest en place . VAR=VALUE OTHERVAR=OTHERVALUE est équivalent à VAR=VALUE; OTHERVAR=OTHERVALUE VAR=VALUE; OTHERVAR=OTHERVALUE . Donc après IFS=. arr=($var) IFS=. arr=($var) , IFS rest défini sur . . Puisque vous pouvez utiliser $IFS dans l'assignation à arr avec la prévision qu'elle a déjà sa nouvelle valeur, il est logique que la nouvelle valeur de IFS soit utilisée pour l'extension de $var .

En résumé, vous pouvez utiliser IFS uniquement pour la division temporaire des champs:

  • en commençant par un nouveau shell ou un sous-shell (par exemple, third=$(IFS=.; set -f; set -- $var; echo "$3") est une façon compliquée de faire un third=${var#*.*.} sauf qu'ils se comportent différemment lorsque la valeur de var contient less de deux caractères.
  • dans ksh, avec IFS=. some-function IFS=. some-functionsome-function est défini avec la function some-function … syntaxe ksh function some-function … ;
  • dans bash et zsh, avec IFS=. some-function IFS=. some-function tant qu'ils fonctionnent en mode natif par opposition au mode de compatibilité.

La réponse de @Gilles est vraiment géniale, il explique (en détail) un problème complexe.

Cependant, je crois que la réponse à pourquoi cette command:

 $ IFS=. printf "%s\n" $var abc 

fonctionne comme il le fait est l'idée simple que toute la command line est analysée avant qu'il soit exécuté. Et que chaque "mot" est traité une fois par le shell.
Les affectations, comme IFS=. , sont retardés (l'étape 4 est la dernière):

4.- Chaque assignation de variable doit être étendue …

jusqu'au moment où la command est exécutée et que toutes les expansions dans les arguments sont traitées en premier pour build cette ligne exécutable:

 $ IFS=. printf "%s\n" abc ## IFS=. goes to the environment. abc 

La valeur de $var est étendue avec l'ancien IFS à abc avant que la command printf ne reçoive les arguments "%s\n" et abc .

Eval

Un niveau de retard peut être introduit par eval :

 $ IFS=. eval printf "'%s\n'" \$var a b c 

La ligne est analysée (1ère fois) et "IFS =." est défini sur l'environnement comme ceci:

 $ printf '%s\n' $var 

Ensuite, il est analysé à nouveau:

 $ printf '%s\n' abc 

Et exécuté à ceci:

 a b c 

La valeur de $var (abc) est divisée par la valeur de IFS utilisée:. .

Environnement

La partie complexe et délicate est ce qui est valable dans l'environnement quand !!!

C'est très bien expliqué dans la première partie de Gilles.

Avec un détail supplémentaire.

Lorsque cette command est exécutée:

 $ IFS=. arr=($var) 

La valeur de l'IFS est conservée dans l'environnement actuel, oui:

 $ printf '<%s> ' "${arr[@]}" "$IFS" <a> <b> <c> <.> 

IFS pour une seule déclaration.

Mais cela pourrait être évité: Définir IFS pour une seule instruction

 $ IFS=. command eval arr\=\(\$var\) $ printf '<%s> ' "${arr[@]}" "$IFS" <a> <b> <c> < > 

Votre question concernant

 var=abc IFS=. printf "%s\n" $var 

est un cas de coin.

Cela est dû au fait que l' macro expansion la macro expansion dans la command se produit avant la variable shell IFS=. est réglé.

En d'autres termes: lorsque $var est développé, la valeur IFS précédente est active, alors IFS est défini sur '.' .