The simplest approach to emulate bash's $PROMPT_COMMAND
which comes to my mind is to use the precmd
hook, as you already figured out. Define it as
precmd() { eval "$PROMPT_COMMAND" }
and you can do something like that:
$ PROMPT_COMMAND='echo Hello, it is now $(date)'
Hello, it is now Mon, Mar 31, 2014 7:08:00 PM
$ whoami
user
Hello, it is now Mon, Mar 31, 2014 7:08:21 PM
$
Please note the single quotes in that example, otherwise $(date)
will get expanded too early, i.e. already when defining $PROMPT_COMMAND
and not when called before the prompt.
If you want to preserve (and don't want to alter) the existing definition, you can use that approach:
$ prmptcmd() { eval "$PROMPT_COMMAND" }
$ precmd_functions=(prmptcmd)
With that the prmptcmd
functions is executed after the existing precmd()
function.
Finally, here is a way which is suitable for use in a program package, which neither should modify user or system files nor can enter the commands interactive.
An example to spawn a bash session could be
PROMPT_COMMAND="echo foo" bash
To spawn zsh you can use
ZDOTDIR=/program/dir zsh
which causes /program/dir/.zshrc
to be sourced. In this file the precmd()
hook can be defined as explained above. If you want the user's settings in addition include source $HOME/.zshrc
etc. in the program's .zshrc, too.
This setup is maintainable, as no files outside the program directory are modified.
As a last addition, here is a proof of concept how to keep the newuser welcome, too. Use the following code in your /program/dir/.zshenv
rc config file:
echo define precmd, traps, etc.
autoload -Uz zsh-newuser-install
if [[ ! -e "$HOME/.zshrc" ]]; then
zsh-newuser-install -f
mv $ZDOTDIR/.zshrc $HOME/.zshrc
else
builtin source $HOME/.zshrc
fi
Your first guess was right, but you used a bad example.
When the shell reads the command
foo=bar echo $foo
the first thing it does (or, at least, one of the first things)
is to look for variable references (like $foo
) and evaluate them.
Then it processes what’s left of the line.
(This may be a bit of an oversimplification;
see bash(1)
or the Bash Reference Manual for details.)
So you have the command
echo (nothing)
running with foo=bar
; but it’s too late; there’s nothing left to look at the value of $foo
.
You can demonstrate this with the following sequence:
$ foo=oldvalue
$ foo=bar echo "$foo"
oldvalue
But
foo=bar command
is the way to go. A couple of examples that work are:
foo=bar sh -c 'echo "$foo"'
and
foo=bar env
(You may want to pipe that through grep foo
.)
I just now noticed the sentence that you quoted,
“When those assignment statements precede a command that is built into the shell, for instance, …”,
suggesting that built-in commands (such as echo
) represent a special case.
It is intuitive (to me, at least) that this “command scope” you’re looking for
would have to work by setting an environment variable in a child process,
and it’s not clear how it would work for a built-in command, which doesn’t run in a child process.
Not many built-in commands access the environment directly,
and their interaction with shell variables is sometimes arcane.
But I came up with a couple more examples that might be more what you’re looking for:
foo=bar eval 'echo $foo'
will display “bar”, because the evaluation of $foo
is deferred.
It is handled by the eval
(built-in) command, rather than the shell’s initial parsing pass.
Or, create a text file called (for instance) showme.sh
.
Put an echo $foo
(or echo "$foo"
) command into it, and say
foo=bar . showme.sh
This is probably what your book is talking about when it says,
“… the shell has to keep track of the correct order in which to resolve variable references ….”
Somehow the shell runs the commands in showme.sh
with foo
equal to bar
,
and then reverts to the previous value of foo
(if any)
when it gets back to its primary input (the terminal).
Best Answer
The unset built-in command takes an option,
-f
, to delete functions:Form the unset entry in the bash manpage:
Note:
-f
is only really necessary if a variable with the same name exists. If you do not also have a variable namedfoo
, thenunset foo
will delete the function.