0

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/python@3.10/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/python@3.10/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/python@3.10/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/python@3.10/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???
dawg
  • 98,345
  • 23
  • 131
  • 206
  • Regarding 4, this has nothing to do with Apple. – chepner Dec 04 '22 at 15:48
  • There's no reason to expect `bash` and `zsh` to behave the same; they are two different shells. – chepner Dec 04 '22 at 15:49
  • (1) yes of course, in the zsh documentation (3) why would you want to? It’s a *feature*. (4) as noted, nothing to do with Apple. – Konrad Rudolph Dec 04 '22 at 15:52
  • 1
    I can imagine wanting to turn it off, at least on a case-by-case basis. The general advice in `bash` (and POSIX shells in general) is to avoid all-uppercase names as reserved. This feature causes some all-lowercase names to be effectively reserved as well. – chepner Dec 04 '22 at 15:54
  • The POSIX specification words it as " The name space of environment variable names containing lowercase letters is reserved for applications.", implying that anything *not* containing a lowercase letter (including mixed-case names) *might* be used elsewhere. – chepner Dec 04 '22 at 15:59
  • `zsh` itself is not a POSIX-compliant *shell* (though it can be treated as one by setting the correct options), but it is typically used in POSIX-compliant systems, so things like naming conventions are still applicable. Using `path` in the manner is *technically* compliant; `zsh` is an application and can do what it likes with the "reserved" name `path`. (It is a bit of a gotcha, though, if you aren't expecting or aware of this use.) – chepner Dec 04 '22 at 16:01
  • @chepner … I just realised that I completely misread your initial comment. After rereading it I now fully agree with you. And yes, the fact that non-uppercase names are reserved in zsh can certainly be seen as unfortunate. – Konrad Rudolph Dec 04 '22 at 16:04
  • POSIX states that all utilities covered by the specification use environment variables consisting of uppercase, underscores, and digits. If you want to avoid one of those utilities from stepping on your own variables, you need to ensure at least one lowercase letter is in the name. So "reserved" is probably not the most *technical* way to do describe this, but the *safest* approach is to avoid such names. – chepner Dec 04 '22 at 16:05
  • 1
    The array `path` is documented in _man zshparam_, in the section _PARAMETERS USED BY THE SHELL_. You can't turn off this feature. – user1934428 Dec 05 '22 at 11:58

1 Answers1

3

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).

chepner
  • 497,756
  • 71
  • 530
  • 681
  • As to 'the purpose' it is so you can do `path+=/some/new/path` in zsh as seen [HERE](https://stackoverflow.com/a/18077919/298607) Your answer allowed me to find this but could not before. Thanks! – dawg Dec 04 '22 at 17:16