Setting IFS for a single statement
In some shells (including bash
):
IFS=: command eval 'p=($PATH)'
(with bash
, you can omit the command
if not in sh/POSIX emulation). But beware that when using unquoted variables, you also generally need to set -f
, and there's no local scope for that in most shells.
With zsh, you can do:
(){ local IFS=:; p=($=PATH); }
$=PATH
is to force word splitting which is not done by default in zsh
(globbing upon variable expansion is not done either so you don't need set -f
unless in sh emulation).
(){...}
(or function {...}
) are called anonymous functions and are typically used to set a local scope. with other shells that support local scope in functions, you could do something similar with:
e() { eval "$@"; }
e 'local IFS=:; p=($PATH)'
To implement a local scope for variables and options in POSIX shells, you can also use the functions provided at https://github.com/stephane-chazelas/misc-scripts/blob/master/locvar.sh. Then you can use it as:
. /path/to/locvar.sh
var=3,2,2
call eval 'locvar IFS; locopt -f; IFS=,; set -- $var; a=$1 b=$2 c=$3'
(by the way, it's invalid to split $PATH
that way above except in zsh
as in other shells, IFS is field delimiter, not field separator).
IFS=$'\n' a=($str)
Is just two assignments, one after the other just like a=1 b=2
.
A note of explanation on var=value cmd
:
In:
var=value cmd arg
The shell executes /path/to/cmd
in a new process and passes cmd
and arg
in argv[]
and var=value
in envp[]
. That's not really a variable assignment, but more passing environment variables to the executed command. In the Bourne or Korn shell, with set -k
, you can even write it cmd var=value arg
.
Now, that doesn't apply to builtins or functions which are not executed. In the Bourne shell, in var=value some-builtin
, var
ends up being set afterwards, just like with var=value
alone. That means for instance that the behaviour of var=value echo foo
(which is not useful) varies depending on whether echo
is builtin or not.
POSIX and/or ksh
changed that in that that Bourne behaviour only happens for a category of builtins called special builtins. eval
is a special builtin, read
is not. For non special builtin, var=value builtin
sets var
only for the execution of the builtin which makes it behave similarly to when an external command is being run.
The command
command can be used to remove the special attribute of those special builtins. What POSIX overlooked though is that for the eval
and .
builtins, that would mean that shells would have to implement a variable stack (even though it doesn't specify the local
or typeset
scope limiting commands), because you could do:
a=0; a=1 command eval 'a=2 command eval echo \$a; echo $a'; echo $a
Or even:
a=1 command eval myfunction
with myfunction
being a function using or setting $a
and potentially calling command eval
.
That was really an overlook because ksh
(which the spec is mostly based on) didn't implement it (and AT&T ksh
and zsh
still don't), but nowadays, except those two, most shells implement it. Behaviour varies among shells though in things like:
a=0; a=1 command eval a=2; echo "$a"
though. Using local
on shells that support it is a more reliable way to implement local scope.
Standard save-and-restore taken from "The Unix Programming Environment" by Kernighan and Pike:
#!/bin/sh
old_IFS=$IFS
IFS="something_new"
some_program_or_builtin
IFS=${old_IFS}
Put your script into a function and invoke that function passing the commandline arguments to it. As IFS is defined local, changes to it don't affect the global IFS.
main() {
local IFS='/'
# the rest goes here
}
main "$@"