Dans certains coquillages (y compris bash
):
IFS=: command eval 'p=($PATH)'
(avec bash
, vous pouvez omettre l’ command
émulation si pas dans sh / POSIX). Mais gardez à l'esprit que lorsque vous utilisez des variables non entre guillemets, vous en avez généralement besoin set -f
, et il n'y a pas de portée locale pour cela dans la plupart des shells.
Avec zsh, vous pouvez faire:
(){ local IFS=:; p=($=PATH); }
$=PATH
consiste à forcer le fractionnement des mots, ce qui n'est pas fait par défaut dans zsh
(la suppression lors du développement de variable n'est pas effectuée non plus, donc vous n'avez pas besoin, set -f
sauf dans l'émulation sh).
(){...}
(ou function {...}
) sont appelées fonctions anonymes et sont généralement utilisées pour définir une étendue locale. avec d'autres shells prenant en charge l'étendue locale dans les fonctions, vous pouvez faire quelque chose de similaire avec:
e() { eval "$@"; }
e 'local IFS=:; p=($PATH)'
Pour implémenter une portée locale pour les variables et les options dans les shells POSIX, vous pouvez également utiliser les fonctions fournies à l' adresse https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh . Ensuite, vous pouvez l'utiliser comme:
. /path/to/locvar.sh
var=3,2,2
call eval 'locvar IFS; locopt -f; IFS=,; set -- $var; a=$1 b=$2 c=$3'
(Soit dit en passant, il est incorrect de diviser de $PATH
cette façon ci-dessus, sauf zsh
que, comme dans les autres shells, IFS est un délimiteur de champ, et non un séparateur de champ).
IFS=$'\n' a=($str)
Est-ce juste deux missions, l'une après l'autre, tout comme a=1 b=2
.
Une note d'explication sur var=value cmd
:
Dans:
var=value cmd arg
Le shell s'exécute /path/to/cmd
dans un nouveau processus et passe cmd
et arg
dans argv[]
et var=value
dans envp[]
. Ce n'est pas vraiment une affectation de variable, mais davantage de variables d'environnement transmises à la commande exécutée . Dans le shell Bourne ou Korn, avec set -k
, vous pouvez même l'écrire cmd var=value arg
.
Cela ne s'applique pas aux fonctions intégrées ou aux fonctions qui ne sont pas exécutées . Dans le shell Bourne, dans var=value some-builtin
, var
finit par être défini, comme avec un var=value
seul. Cela signifie par exemple que le comportement de var=value echo foo
(ce qui n’est pas utile) varie selon qu’il echo
soit intégré ou non.
POSIX et / ou ont ksh
modifié ce comportement en ce sens que le comportement Bourne ne se produit que pour une catégorie de fonctions intégrées appelées fonctions spéciales . eval
est un spécial intégré, read
n'est pas. Pour les commandes intégrées non spéciales, les var=value builtin
ensembles var
ne concernent que l'exécution de la commande intégrée, ce qui lui permet de se comporter de manière similaire à l'exécution d'une commande externe.
La command
commande peut être utilisée pour supprimer l' attribut spécial de ces fonctions spéciales . Ce qui est bien négligé Posix que pour les eval
et .
builtins, cela voudrait dire que des obus aurait à mettre en œuvre une pile variable (même si elle ne précise pas les local
ou la typeset
portée de limitation des commandes), parce que vous pouvez faire:
a=0; a=1 command eval 'a=2 command eval echo \$a; echo $a'; echo $a
Ou même:
a=1 command eval myfunction
avec myfunction
être une fonction à l' aide ou la mise $a
et potentiellement appeler command eval
.
C'était vraiment un oubli parce que ksh
(la spécification est principalement basée sur) ne l'a pas implémentée (et AT & T ksh
et zsh
ne le fait toujours pas), mais de nos jours, à l'exception de ces deux là, la plupart des shells l'implémentent. Le comportement varie selon les coquillages, par exemple:
a=0; a=1 command eval a=2; echo "$a"
bien que. L'utilisation local
sur les shells qui le supportent constitue un moyen plus fiable d'implémenter la portée locale.
IFS=: command eval …
définitIFS
que pour la durée deeval
, comme prescrit par POSIX, en dash, pdksh et bash, mais pas en ksh 93u. Il est inhabituel de voir que ksh est l'intrus-non-conforme-un-out.Sauvegarde et restauration standard extraites de "The Unix Programming Environment" de Kernighan et Pike:
la source
$IFS
correctement s'il était précédemment non défini.$'\t\n'' '
, comme expliqué ici: wiki.bash-hackers.org/syntax/expansion/…$' \t\n'
. L'espace doit être le premier utilisé"$*"
. Notez que c'est la même chose dans tous les obus Bourne-like.Placez votre script dans une fonction et appelez-la en lui transmettant les arguments de la ligne de commande. Comme IFS est défini localement, ses modifications n’affectent pas l’IFS global.
la source
Pour cette commande:
Il existe une solution alternative: donner à la première affectation (
IFS=$'\n'
) une commande à exécuter (une fonction):Cela mettra IFS dans l’environnement à appeler le fractionnement, mais ne sera pas conservé dans l’environnement actuel.
Cela évite également l’utilisation toujours risquée d’eval.
la source
$IFS
fixées à la$'\n'
suite , au besoin par POSIX.La réponse proposée par @helpermethod est certainement une approche intéressante. Mais c’est aussi un piège, car dans BASH, la portée de la variable locale s’étend de l’appelant à la fonction appelée. Par conséquent, si vous définissez IFS dans main (), cette valeur persistera pour les fonctions appelées depuis main (). Voici un exemple:
Et la sortie ...
Si IFS déclaré dans main () n'était pas encore dans la portée de func (), le tableau n'aurait pas été correctement analysé dans func () B. Décommentez la première ligne de func () et vous obtenez ce résultat:
C'est ce que vous devriez obtenir si IFS était hors de portée.
Une solution bien meilleure, à mon humble avis, consiste à renoncer à changer ou à s’appuyer sur IFS au niveau mondial / local. Créez plutôt un nouveau shell et jouez avec IFS. Par exemple, si vous appelez func () dans main () comme suit, transmettez le tableau sous forme de chaîne avec un séparateur de champ de barre oblique inversée:
... ce changement dans IFS ne sera pas reflété dans func (). Le tableau sera passé sous forme de chaîne:
... mais à l'intérieur de func (), l'IFS sera toujours "/" (comme défini dans main ()), sauf si modifié localement dans func ().
Pour plus d'informations sur l'isolation des modifications apportées à IFS, consultez les liens suivants:
Comment convertir une variable de tableau bash en une chaîne délimitée par des nouvelles lignes?
Chaîne Bash à dresser avec IFS
Trucs et astuces pour la programmation de scripts shell en général - Voir "REMARQUE concernant l'utilisation de sous-coques ..."
la source
IFS=$'\n' declare -a astr=(...)
parfait merci!Cet extrait de la question:
est interprété comme deux affectations de variable globales distinctes évaluées de gauche à droite et équivaut à:
ou
Cela explique à la fois pourquoi le global a
IFS
été modifié et pourquoi le découpage des mots$str
en éléments de tableau a été effectué à l'aide de la nouvelle valeur deIFS
.Vous pourriez être tenté d'utiliser un sous-shell pour limiter l'effet de la
IFS
modification comme ceci:mais vous remarquerez rapidement que la modification de
a
est également limitée au sous-shell:Ensuite, vous seriez tenté de sauvegarder / restaurer IFS en utilisant la solution de cette réponse précédente par @msw ou d’essayer d’utiliser
local IFS
une fonction interne suggérée par @helpermethod. Mais très vite, vous remarquerez que vous avez toutes sortes de problèmes, en particulier si vous êtes un auteur de bibliothèque qui doit faire preuve de rigueur face aux appels de scripts incorrects:IFS
était initialement non défini?set -u
(akaset -o nounset
)?IFS
été faite en lecture seule viadeclare -r IFS
?trap
gestionnaire`)?S'il vous plaît ne pas enregistrer / restaurer IFS. Au lieu de cela, tenez-vous aux modifications temporaires:
Pour limiter la modification de variable à une seule commande, à une commande intégrée ou à un appel de fonction, utilisez
IFS="value" command
.Pour lire plusieurs variables en séparant un caractère spécifique (
:
utilisé ci-dessous à titre d'exemple), utilisez:Pour lire dans un tableau, utilisez (faites ceci à la place de
array_var=( $str )
):Limitez les effets de la modification de la variable à un sous-shell.
Pour sortir les éléments d'un tableau séparés par une virgule:
Pour capturer cela dans une chaîne:
la source
La solution la plus simple consiste à prendre une copie de l’original
$IFS
, comme par exemple la réponse de msw. Cependant, cette solution ne fait pas la distinction entre un non définiIFS
et unIFS
ensemble égal à la chaîne vide, ce qui est important pour de nombreuses applications. Voici une solution plus générale qui capture cette distinction:la source