In most shells, $VAR
and $var
and $Var
are three different variables because the shell (all that >> I << am aware of) is case sensitive.
In zsh
on MacOS 13.01:
[Start a fresh copy of zsh]
% s=tst
% S="something else"
% echo "\$s=$s"
$s=tst
% echo "\$S=$S"
$S=something else
% [[ "$s" == "$S" ]] || echo "Not equal"
Not equal
However, in zsh
on MacOS, examine $PATH
and $path
:
% echo $PATH
/Users/dawg/perl5/bin:/usr/local/opt/ruby/bin:/usr/local/opt/[email protected]/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin
% echo $path
/Users/dawg/perl5/bin /usr/local/opt/ruby/bin /usr/local/opt/[email protected]/bin /usr/local/bin /usr/bin /bin /usr/sbin /sbin /Library/Apple/usr/bin
It appears that $path
is a space delimited version of $PATH
.
Now change PATH
in the typical way:
% export PATH=/some/new/folder:$PATH
% echo $PATH
/some/new/folder:/Users/dawg/perl5/bin:/usr/local/opt/ruby/bin:/usr/local/opt/[email protected]/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin
That is what I expect, but it also changes $path
:
% echo $path
/some/new/folder /Users/dawg/perl5/bin /usr/local/opt/ruby/bin /usr/local/opt/[email protected]/bin /usr/local/bin /usr/bin /bin /usr/sbin /sbin /Library/Apple/usr/bin
WORSE, changing $path
also changes $PATH
!
This is not the same in a brew
install of Bash which has no secret $path
waiting to bite.
It bit me (with an hour of head scratching) with a loop like this on zsh on MacOS:
for path in **/*.txt; do
# do some things with sys utilities with $path...
# sys utilities like awk in the PATH were not found
done
Questions:
- Is the
$PATH
/$path
link documented somewhere? - What is the purpose?
- How do I turn this off??
- Why would Apple do this???
CodePudding user response:
path
is an array that is "tied" to PATH
. This is a general feature provided by zsh
via the typeset -T
command.
$ typeset -T FOO foo ' '
$ FOO=a b c
$ print -l $FOO
a b c
$ print -l $foo
a
b
c
Here, FOO
is the scalar and foo
is the array. (Pairs of names that are identical except for case is a convention, not a requirement).
is the separator between elements in the scalar that determines the values of the corresponding array. Modifying one variable affects the other.
The purpose is to make it easier to modify PATH
, by adding or removing directories to the array and letting the shell handle updating the scalar with necessary separators.
path
and PATH
behave as if defined with
typeset -T PATH path :
or
typeset -T PATH path
(:
is the default separator, as the feature is intended to provide array equivalent of variables like PATH
, MANPATH
, etc.)
There is no way to "untie" such scalar/array pairs; I would just accept that path
is effectively reserved by zsh
.
Both the scalar and the array may be manipulated as normal. If one is unset, the other will automatically be unset too. There is no way of untying the variables without unsetting them, nor of converting the type of one of them with another typeset command; T does not work, assigning an array to scalar is an error, and assigning a scalar to array sets it to be a single-element array.
You can see what other pairs are defined using typeset -T
and no other arguments. It will list all variables in lexicographic order (which means all uppercase names, likely scalars, followed by all lowercase names, likely arrays).