>Here are some examples of the three possibilities described above.
>
> # current value of PAGER environment variable:
> % printenv | grep PAGER
> PAGER=/usr/local/bin/less
>
> # inline assignment of PAGER is "seen" by printenv:
> % PAGER=/bin/cat printenv | grep PAGER
> PAGER=/bin/cat
The assignment modifies the environment of the printenv command; it prints
its environment.
>
> # ...and by set:
> % PAGER=/bin/cat set | grep -a PAGER
> PAGER=/bin/cat
Just like printenv.
>
> # ...and by perl:
> % PAGER=/bin/cat perl -e 'print "$ENV{PAGER}\n"'
> /bin/cat
Mostly like the previous cases.
>
> # ...but neither by printf nor echo nor print:
> % PAGER=/bin/cat printf "$PAGER\n"
> /usr/local/bin/less
This is different. You aren't asking printf to output the value of an
environment variable. You can't ask it to do that. It doesn't know how. It
only prints its arguments.
You're constructing an argument for the printf command by expanding the
current value of $PAGER. Then you're modifying the environment, which has no
effect because the command doesn't use the environment.
If you postpone the evaluation of $PAGER until a later stage...
% PAGER=/bin/cat eval 'printf "$PAGER\n"'
/bin/cat
> % PAGER=/bin/cat echo $PAGER
> /usr/local/bin/less
> % PAGER=/bin/cat print $PAGER
> /usr/local/bin/less
These are the same. Expanding the $PAGER happens before the modification of
the environment. You can see that in all the commands which behave this way,
you have a "$PAGER" being expanded by the shell passed to a command that
doesn't care about the environment, where in the others you have a command
that looks at the environment and/or shell variables itself (including perl,
where you used $ENV{PAGER} and the perl code is quoted with apostrophes so
the shell won't try to expand it).
>
> # a situation where inline assignments are downright forbidden:
> % IFS=$'\n' for i ( $( grep -wl foo * ) ) echo $i
> zsh: parse error near `for'
That syntax only works on simple statements. Maybe a future zsh will make it
work on compound statements too.
But there is a fairly brief way of line-splitting command substitution:
for i in ${(f)"$(cmd)"} ...
>
>Sometimes the (seemingly) inconsistent behavior surrounding these
>inline assignments is downright bewildering. E.g.:
>
> # Inline assignment to shell variable SHELLVAR is seen by set:
> % SHELLVAR=42
> % set | grep -a SHELLVAR
> SHELLVAR=42
> % SHELLVAR=24 set | grep -a SHELLVAR
> SHELLVAR=24
>
> # ...but inline assignment to shell variable USERNAME isn't:
> % set | grep -a USERNAME
> USERNAME=kjones
> % USERNAME=senojk set | grep -a USERNAME
> USERNAME=kjones
USERNAME has its own little section in zshparam(1). assigning to it is
magical: it actually tries to change your process's uid! A shell running as
root can drop privileges by assigning a new value to USERNAME, or spawn a
child process with different privileges with the assignment-before-command
syntax. If you assign an invalid username or you don't have sufficient
privileges, the assignment fails.
Your inability to change USERNAME is unrelated to your user of the
assignment-before-command syntax. Simple assignments behave the same way:
% echo $USERNAME
pacman
% USERNAME=root
zsh: failed to change group ID: operation not permitted
% echo $USERNAME
pacman
% USERNAME=asdfghjk
% echo $USERNAME
pacman
As you can see, the "username doesn't exist" case fails silently, while the
"insufficient privilege" case tells you what went wrong. From that I can
conclude that you didn't actually create an account called senojk...
--
Alan Curry