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

19

En bash, disons que vous avez var=a.b.c., puis:

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

Cependant, une telle utilisation de IFSprend 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 tableaux ou le fractionnement de mots dans la documentation de Bash ne donne aucune indication dans les deux cas. Une recherche IFSdans la documentation d'une seule page ne fournit pas non plus d'indices sur cet effet.

Je ne sais pas quand je peux faire de manière fiable:

IFS=x do something

Et attendez-vous à ce que IFScela affecte le fractionnement du champ.

muru
la source

Réponses:

28

L'idée de base est que les VAR=VALUE some-commandjeux VARà VALUEl'exécution de some-commandquand some-commandest une commande externe, et il ne reçoit pas plus de fantaisie que cela. Si vous combinez cette intuition avec une certaine connaissance du fonctionnement d'un shell, vous devriez trouver la bonne réponse dans la plupart des cas. La référence POSIX est «Simple Commands» dans le chapitre «Shell Command Language» .

Si some-commandest une commande externe , VAR=VALUE some-commandéquivaut à env VAR=VALUE some-command. VARest exporté dans l'environnement de some-command, et sa valeur (ou son absence) dans le shell ne change pas.

Si some-commandest une fonction , alors VAR=VALUE some-commandest équivalent à VAR=VALUE; some-command, c'est- à -dire que l'affectation reste en place après le retour de la fonction et que la variable n'est pas exportée dans l'environnement. La raison en est liée à la conception du shell Bourne (et par la suite à la compatibilité descendante): il n'avait pas la possibilité de sauvegarder et de restaurer les valeurs des variables autour de l'exécution d'une fonction. Ne pas exporter la variable est logique car une fonction s'exécute dans le shell lui-même. Cependant, ksh (y compris ATT ksh93 et ​​pdksh / mksh), bash et zsh implémentent le comportement le plus utile où VARest défini uniquement pendant l'exécution de la fonction (il est également exporté). Dans ksh , cela se fait si la fonction est définie avec la syntaxe kshfunction NAME …, pas s'il est défini 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 , cela se fait si l' posix_builtinsoption n'est pas définie; cette option n'est pas définie par défaut mais est activée par emulate shou emulate ksh.

Si some-commandest une fonction intégrée, le comportement dépend du type de fonction intégrée. Les éditions spéciales se comportent comme des fonctions. Les fonctions intégrées spéciales sont celles qui doivent être implémentées à l'intérieur du shell car elles affectent le shell d'état (par exemple, breakaffectent le flux de contrôle, cdaffectent le répertoire courant, setaffectent les paramètres et options de position…). D'autres fonctions intégrées sont intégrées uniquement pour des raisons de performances et de commodité (principalement - par exemple, la fonction bash printf -vne peut être implémentée que par une fonction intégrée), et elles se comportent comme une commande externe.

L'affectation a lieu après l'expansion de l'alias, donc s'il some-commands'agit d'un alias , développez-le d'abord pour trouver ce qui se passe.

Notez que dans tous les cas, l'affectation est effectuée après l'analyse de la ligne de commande, y compris toute substitution de variable sur la ligne de commande elle-même. var=a; var=b echo $varImprime donc a, car $varest évalué avant que l'affectation n'ait lieu. Et IFS=. printf "%s\n" $varutilise donc l'ancienne IFSvaleur pour se diviser $var.

J'ai couvert tous les types de commandes, mais il y a un autre cas: quand il n'y a pas de commande à exécuter , c'est-à-dire si la commande se compose uniquement d'affectations (et éventuellement de redirections). Dans ce cas, l'affectation reste en place . VAR=VALUE OTHERVAR=OTHERVALUEest équivalent à VAR=VALUE; OTHERVAR=OTHERVALUE. Donc, après IFS=. arr=($var), IFSreste réglé sur .. Étant donné que vous pouvez utiliser $IFSdans l'affectation à arren supposant qu'il a déjà sa nouvelle valeur, il est logique que la nouvelle valeur de IFSsoit utilisée pour l'expansion de $var.

En résumé, vous pouvez utiliser uniquement IFSpour le fractionnement temporaire de champs:

  • en lançant un nouveau shell ou un sous-shell (par exemple, third=$(IFS=.; set -f; set -- $var; echo "$3")c'est une façon compliquée de faire third=${var#*.*.}sauf qu'ils se comportent différemment lorsque la valeur de varcontient moins de deux .caractères);
  • en ksh, IFS=. some-functionsome-functionest défini avec la syntaxe ksh function some-function …;
  • en bash et zsh, avec IFS=. some-functiontant qu'ils fonctionnent en mode natif par opposition au mode de compatibilité.
Gilles 'SO- arrête d'être méchant'
la source
" IFSreste réglé sur ." Eek. Après avoir lu la première partie, cela a du sens, mais avant de poster ce Q, je ne m'y attendais pas.
muru
1
Ceci est une réponse à une question différente
schily
Quelques explications supplémentaires dans cette réponse d'il y a quelques années .
Ti Strga
6

La réponse de @Gilles est vraiment formidable, explique-t-il (en détail) une question complexe.

Cependant, je crois que la réponse à la raison pour laquelle cette commande:

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

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

4.- Chaque affectation de variable doit être étendue ...

jusqu'à ce que la commande soit exécutée et que toutes les extensions des arguments soient traitées en premier pour créer cette ligne exécutable:

$ IFS=. printf "%s\n" a.b.c           ## IFS=. goes to the environment.
a.b.c

La valeur de $varest développée avec l '"ancien" IFS a.b.cavant que la commande printfne reçoive les arguments "%s\n"et a.b.c.

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 suit:

$ printf '%s\n' $var

Ensuite, il est à nouveau analysé comme suit:

$ printf '%s\n' a b c

Et exécuté à ceci:

a
b
c

La valeur de $var (abc) est divisé par la valeur de l' IFS en cours d' utilisation: ..

Environnement

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

Cela s'explique très bien dans la première partie de la réponse de Gilles.

Avec un détail supplémentaire.

Lorsque cette commande est exécutée:

$ IFS=. arr=($var)

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

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

IFS pour une seule instruction.

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

$ IFS=. command eval arr\=\(\$var\)

$  printf '<%s>  ' "${arr[@]}" "$IFS"
<a>  <b>  <c>  < 
> 
Communauté
la source
2

Votre question concernant

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

est un cas d'angle.

En effet, la macro expansioncommande in se produit avant que la variable shell ne IFS=.soit définie.

En d'autres termes: lorsque $varest développé, la IFSvaleur précédente est active, puis IFSest définie sur '.'.

schily
la source