Home > Back-end >  MacOS path is a space delimited version of PATH and they are linked in zsh
MacOS path is a space delimited version of PATH and they are linked in zsh

Time:12-05

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:

  1. Is the $PATH / $path link documented somewhere?
  2. What is the purpose?
  3. How do I turn this off??
  4. 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).

  • Related