What is the difference between PS1 and PROMPT_COMMAND? - bash

While taking a look at this awesome thread I noticed that some examples use
PS1="Blah Blah Blah"
and some use
PROMPT_COMMAND="Blah Blah Blah"
(and some use both) when setting the prompt in a Bash shell. What is the difference between the two? A Stack Overflow search and even a bit of broader Google searching aren't getting me results, so even a link to the right place to look for the answer would be appreciated.

PROMPT_COMMAND can contain ordinary Bash statements whereas the PS1 variable can also contain the special characters, such as '\h' for hostname, in the variable.
For example, here is my Bash prompt that uses both PROMPT_COMMAND and PS1. The Bash code in PROMPT_COMMAND works out what Git branch you might be in and displays that at the prompt, along with the exit status of the last run process, hostname and basename of the pwd.
The variable RET stores the return value of the last executed program. This is convenient to see if there was an error and the error code of the last program I ran in the terminal. Note the outer ' surrounding the entire PROMPT_COMMAND expression. It includes PS1 so that this variable is reevaluated each time the PROMPT_COMMAND variable is evaluated.
PROMPT_COMMAND='RET=$?;\
BRANCH="";\
ERRMSG="";\
if [[ $RET != 0 ]]; then\
ERRMSG=" $RET";\
fi;\
if git branch &>/dev/null; then\
BRANCH=$(git branch 2>/dev/null | grep \* | cut -d " " -f 2);\
fi;
PS1="$GREEN\u#\h $BLUE\W $CYAN$BRANCH$RED$ERRMSG \$ $LIGHT_GRAY";'
Example output looks like this in a non-Git directory:
sashan#dhcp-au-122 Documents $ false
sashan#dhcp-au-122 Documents 1 $
And in a Git directory you see the branch name:
sashan#dhcp-au-122 rework mybranch $
Update
After reading the comments and Bob's answer, I think that writing it as he describes is better. It's more maintainable than what I originally wrote above, where the PS1 variable is set inside the PROMPT_COMMAND, which itself is a super complicated string that is evaluated at runtime by Bash.
It works, but it's more complicated than it needs to be. To be fair, I wrote that PROMPT_COMMAND for myself about 10 years ago and it worked and didn't think too much about it.
For those curious as to how I've amended my things, I've basically put the code for the PROMPT_COMMAND in a separate file (as Bob described) and then echo the string that I intend to be PS1:
GREEN="\[\033[0;32m\]"
CYAN="\[\033[0;36m\]"
RED="\[\033[0;31m\]"
PURPLE="\[\033[0;35m\]"
BROWN="\[\033[0;33m\]"
LIGHT_GRAY="\[\033[0;37m\]"
LIGHT_BLUE="\[\033[1;34m\]"
LIGHT_GREEN="\[\033[1;32m\]"
LIGHT_CYAN="\[\033[1;36m\]"
LIGHT_RED="\[\033[1;31m\]"
LIGHT_PURPLE="\[\033[1;35m\]"
YELLOW="\[\033[1;33m\]"
WHITE="\[\033[1;37m\]"
RESTORE="\[\033[0m\]" #0m restores to the terminal's default colour
if [ -z $SCHROOT_CHROOT_NAME ]; then
SCHROOT_CHROOT_NAME=" "
fi
BRANCH=""
ERRMSG=""
RET=$1
if [[ $RET != 0 ]]; then
ERRMSG=" $RET"
fi
if which git &>/dev/null; then
BRANCH=$(git branch 2>/dev/null | grep \* | cut -d " " -f 2)
else
BRANCH="(git not installed)"
fi
echo "${GREEN}\u#\h${SCHROOT_CHROOT_NAME}${BLUE}\w \
${CYAN}${BRANCH}${RED}${ERRMSG} \$ $RESTORE"
And in my .bashrc file:
function prompt_command {
RET=$?
export PS1=$(~/.bash_prompt_command $RET)
}
PROMPT_DIRTRIM=3
export PROMPT_COMMAND=prompt_command

From the GNU Bash documentation page (Bash Reference Manual):
PROMPT_COMMAND
If set, the value is interpreted as a command to execute before
the printing of each primary prompt ($PS1).
I never used it, but I could have used this back when I only had sh.

The difference is that PS1 is the actual prompt string used, and PROMPT_COMMAND is a command that is executed just before the prompt. If you want the simplest, most flexible way of building a prompt, try this:
Put this in your .bashrc file:
function prompt_command {
export PS1=$(~/bin/bash_prompt)
}
export PROMPT_COMMAND=prompt_command
Then write a script (Bash, Perl, or Ruby: your choice), and place it in file ~/bin/bash_prompt.
The script can use any information it likes to construct a prompt. This is much simpler, IMO, because you don't have to learn the somewhat baroque substitution language that was developed just for the PS1 variable.
You might think that you could do the same by simply setting PROMPT_COMMAND directly to ~/bin/bash_prompt, and setting PS1 to the empty string.
This at first appears to work, but you soon discover that the readline code expects PS1 to be set to the actual prompt, and when you scroll backwards in history, things get messed up as a result.
This workaround causes PS1 to always reflect the latest prompt (since the function sets the actual PS1 variable used by the invoking instance of the shell), and this makes readline and command history work fine.

From man bash:
PROMPT_COMMAND
If set, the value is executed as a command prior to issuing each primary prompt.
PS1
The value of this parameter is expanded (see PROMPTING below) and used as the primary prompt string. The default value is ''\s-\v\$ ''.
If you simply want to set the prompt string, using PS1 alone is enough:
PS1='user \u on host \h$ '
If you want to do something else just before printing the prompt, use PROMPT_COMMAND. For example, if you want to sync cached writes to disk, you can write:
PROMPT_COMMAND='sync'

Yeah, so to try to really nail this down:
PROMPT_COMMAND is a handy Bash convenience variable/function, but there is, strictly speaking, nothing that cannot also be done using PS1 alone, correct?
I mean, if one wants to set another variable with scope outside the prompt: depending on the shell, that variable would probably need to be declared first outside $PS1 or (worst case) one might have to get fancy with something waiting on a FIFO prior to calling $PS1 (and armed again at the end of $PS1); the \u \h might cause some trouble, particularly if you're using some fancy regex; but otherwise: one can accomplish anything PROMPT_COMMAND can by using command substitution within $PS1 (and, maybe in corner cases, explicit subshells)?
Right?

The difference is that
if you output an incomplete line from PROMPT_COMMAND, it will screw your Bash prompt
PS1 substitutes \H and friends
PROMPT_COMMAND runs its contents, and PS1 uses its contents as the prompt.
PS1 does variable expansion and command substitution at each prompt. There isn't any need to use PROMPT_COMMAND to assign a value to PS1 or to run arbitrary code. You can easily do export PS1='$(uuidgen) $RANDOM' once in file .bash_profile. Just use single quotes.

I spent so much time on this I just wanted to share what worked for me. I looked at a lot of the SO posts about PROMPT_COMMAND and PS1 and tried many combinations of single quotes, double quotes, calling functions... I could not get the prompt to update each time without printing control characters or the literal expanded but not processed prompt string, or without just setting PS1 in PROMPT_COMMAND as we are advised not to do. My problem was setting variables (colors) that contained control characters; these had to be hard-coded after the variable name in PS1. PROMPT_COMMAND is set to a function that sets variables and they are used (escaped) in a double-quoted PS1 string. This is for a powerline-style prompt that changes colors with each command.
icon1=#unicode powerline char like
#these: https://github.com/ryanoasis/powerline-extra-symbols#glyphs
icon2=#same
#array of ANSI colors. 2 for rgb mode then the rgb values
#then 'm' without '\]' control character. these are from
#the solarized theme https://ethanschoonover.com/solarized/
declare -a colors=(
"2;220;50;47m"
"2;203;75;22m"
"2;181;137;0m"
"2;133;153;0m"
"2;42;161;152m"
"2;38;139;210m"
"2;108;113;196m"
"2;211;54;130m"
"2;0;43;54m"
"2;7;54;66m"
"2;88;110;117m"
"2;101;123;131m"
"2;131;148;150m"
"2;147;161;161m"
)
#outside of vars set in PROMPT_COMMAND it's ok to have control chars
LEN=${#colors[#]}
BG="\[\e[48;"#set bg color
FG="\[\e[38;"#set fg color
TRANSP="1m\]"#transparency
BASE2="2;238;232;213m\]"#fg (text) color
myfunc(){
RAND=$(($RANDOM % $LEN))
COLOR1=${colors[$RAND]}
COLOR2=${colors[($RAND + 1) % $LEN]}
COLOR3=${colors[($RAND + 2) % $LEN]}
}
PROMPT_COMMAND=myfunc
#note double quotes and escaped COLOR vars followed by hard-coded '\]' control chars
PS1="$BG$TRANSP$FG\$COLOR1\]$icon1$BG\$COLOR1\]$FG$TRANSP$BG\$COLOR1\]$FG$BASE2
[username hard-coded in unicode] $BG\$COLOR2\]$FG\$COLOR1\]$icon2$BG\$COLOR2\]$FG$BASE2
\w $BG\$COLOR3\]$FG\$COLOR2\]$icon2$BG\$COLOR3\]$FG$BASE2 [more unicode]
\[\e[0m\]$FG\$COLOR3\]$icon2\[\e[0m\] "
That ought to get you going!

Related

Adding to $PS1 variable on Git Bash for Windows using ~/.bash_profile causes an issue with __git_ps1

So I am trying to customize the PS1 value to add a check mark or x to the prompt depending on the result of the previous command. Surprisingly I got that part to work fine.
However, it has broken the part of the prompt that shows the Git branch when viewing a Git repository.
Here is the previous PS1 value:
user#PC MINGW64 ~/Git
$ echo $PS1
\[\033]0;$TITLEPREFIX:$PWD\007\]\n\[\033[32m\]\u#\h \[\033[35m\]$MSYSTEM \[\033[33m\]\w\[\033[36m\]`__git_ps1`\[\033[0m\]\n$
and here is the new ~/.bash_profile script that breaks it:
function nonzero_return() {
RETVAL=$?
if [[ $RETVAL -ne 0 ]]
then
echo "❌ ($RETVAL)"
else
echo "✅"
fi
}
export PS1="\[\e[31m\]\`nonzero_return\`\[\e[m\]\[\033]0;$TITLEPREFIX:$PWD\007\]\n\[\033[32m\]\u#\h \[\033[35m\]$MSYSTEM \[\033[33m\]\w\[\033[36m\]`__git_ps1`\[\033[0m\]\n$"
Here is an example of the old prompt versus the new one:
user#PC MINGW64 ~/Git/docker-brew-ubuntu-core (dist-amd64)
$
✅
user#PC MINGW64 ~/Git/docker-brew-ubuntu-core
$
❌ (127)
user#PC MINGW64 ~/Git/docker-brew-ubuntu-core
$
SOLUTION
Thanks to #davlet, #joanis, and #torek for helping me resolve this issue. I also used some other suggestions from them to edit my PS1 value even further into a place that I really liked. If anyone is curious, here is the new ~/.bash_profile script:
function nonzero_return() {
local RETVAL=$?
if [[ $RETVAL -ne 0 ]]
then
echo "❌($RETVAL)"
else
echo "✅"
fi
}
PS1='\[\e]0;Git Bash: $MSYSTEM\007\]\n\[\e[31m\]`nonzero_return`\[\e[32m\] \u#\h \[\e[35m\]\w\[\e[36m\]`__git_ps1`\e[0m\n$'
I think you want to use single quotes when assigning to PS1, and loose the backslashes before backticks. Also PS1 shouldn’t be exported:
PS1='\[\e[31m\]`nonzero_return`[...]'
Also, make RETVAL local in your helper function, otherwise it may interfere with other scripts, ie:
local RETVAL=$?
I'm going to steal that prompt, I really like it!
You just need to escape the backticks around __git_ps1:
export PS1="\[\e[31m\]\`nonzero_return\`\[\e[m\]\[\033]0;$TITLEPREFIX:$PWD\007\]\n\[\033[32m\]\u#\h \[\033[35m\]$MSYSTEM \[\033[33m\]\w\[\033[36m\]\`__git_ps1\`\[\033[0m\]\n$"
The difference is
...\`__git_ps1\`...
instead of
...`__git_ps1`...
in the export statement.
A bit of an explanation
The way the PS1 variable works is that it gets evaluated each time the prompt is printed. That's why you can have functions in it like __git_ps1 or your nonzero_return do something different each time the prompt is printed.
When you use double quotes to define PS1, e.g.:
PS1="...`my_function`...$MYVAR..." # won't work
the shell applies normal double quote expansion right when PS1 is defined, which includes expanding that backtick'd function call, and filling in the current value of $MYVAR, storing only the results in PS1, which is not what you want.
If you escape the backticks, or, better yet, use single quotes as #davlet said, PS1 now contains those backticks and variables themselves, which get evaluation each time the prompt is printed.
You can give it a try: if you use single quotes and then change the value of $MSYSTEM, your prompt will reflect that change right away. The get the same effect with double quotes, you'd have to escape the $.
Getting rid of that newline
In the comments, you ask how to remove the newline after the checkmark or X. That newline comes from the \n you have in the middle of your PS1 definition. But I personally like that newline, because it means my prompt is preceded by a blank line, separating it more nicely from the output of the previous command, especially if that didn't output its own newline.
Here's how I would tweak your prompt:
PS1='\[\033]0;$TITLEPREFIX:$PWD\007\]\n\[\e[31m\]`nonzero_return`\[\e[m\]\[\033[32m\]\u#\h \[\033[35m\]$MSYSTEM \[\033[33m\]\w\[\033[36m\]`__git_ps1`\[\033[0m\]\n$'
Details:
\[\033]0;$TITLEPREFIX:$PWD\007\] sets the Window's title bar
\n separates the prompt from the previous command's output
[\e[31m\]nonzero_return\[\e[m\] your return code function in bold red (note that the \[\e[m|] is superfluous here: it says reset the colour, but your next thing sets the colour again)
\[\033[32m\]\u#\h user#host in green
\[\033[35m\]$MSYSTEM the value of $MSYSTEM in purple
\[\033[33m\]\w current working directory in yellow
\[\033[36m\]`__git_ps1` is the Git status in Cyan
\[\033[0m\] resets the font
\n is the newline
$ is the "superuser indicator" which turns to # when you are the superuser.
More notes:
As #torek points out, \[ and \] are unnecessary before the final \n
You mix \e and \033: those are both escape sequences inserting an actual escape character in there. I prefer to use \e everywhere because I find it more legible.
This is a useful reference on bash prompts: https://wiki.archlinux.org/title/Bash/Prompt_customization

Update variable in bash function and use it in prompt (PS1)

What I want to do is to call a function in PS1 to update a variable inside the function. Then I like to use that variable to add another line to PS1. Like bellow:
my_func(){
var="_RED_"
echo "hello in red"
}
PS1="\[\033]0;\w\007\]"
PS1+='$(my_func)'
if [ $var = "_RED_" ]; then # here I want to use that var
PS1+="\[$(tput setaf 124)\] red"
fi
The reason for doing this is to bring non-printable characters \[ and \] out of the function to prevent from overlapping long lines that is caused by \[ \]
You can absolutely update global variables inside a shell function -- all assignments inside functions modify global variables unless local or declare variables were used to create a new scope.
The problem here, however, is that you aren't running your function in the same shell process as the code that later tries to read var (though whether it is in fact "later" or not is a separate issue)! When you use command substitution -- the $() in $(my_func) -- you're creating a new forked-off subprocess to run that function. When that subprocess exits, all changes to variable values it's made are lost with it.
However, you can work around that by not using command substitution at all. Consider the below, which uses the PROMPT_COMMAND hook to assign PS1:
# code to run before each time a prompt is printed
PROMPT_COMMAND='build_ps1_func'
# rewrite your function to write to a named variable, not stdout
my_func(){
local outvar=$1; shift # named variable to write stdout to
var="_RED_" # hardcoded global to update, per question
printf -v "$outvar" '%s' "hello in red"
}
build_ps1_func() {
local your_str # define your_str as a local variable
my_func your_str # call my_func, telling it to write output to your_str
PS1="\[\033]0;\w\007\]"
PS1+="$your_str"
if [ $var = "_RED_" ]; then # using that variable here
PS1+="\[$(tput setaf 124)\] red"
fi
}
What I want to do is to call a function in PS1 to update a variable inside the function. Then I like to use that variable to add another line to PS1.
From your sample code, I suppose you mean that you want to perform a command expansion involving a shell function in the process of assigning a value to PS1. [update:] Since you've tagged the question [bash], we'll presume that you are specifically interested in the behavior of GNU Bash, which differs from that of a fully-conforming POSIX shell in this area. It is important in that case to recognize that that the command expansion will be performed once, at the time the value of PS1 is set / modified, not each time PS1 is displayed. Your wording and specific syntax make me suspect that you have a different expectation.
Consider this part of your code:
PS1="\[\033]0;\w\007\]"
PS1+='$(my_func)'
Because it appears in single quotes, the $(my_func) is not subject to command expansion or any other expansion at the time that it is appended to your prompt string. Although the single quotes are removed before the value is appended to PS1, that does not mean it will be subject to expansion later. Unlike a fully-conforming POSIX shell, however, Bash will perform command substitution on the prompt string before printing it.
Now, because the function body is a curly-braced compound command, if it is executed then it will indeed set the value of var in the current shell, and that effect will be visible after the function returns. However,
(update, per #CharlesDuffy:) the command in a command substitution is run in a subshell. Although a variable update in that subshell would persist past the function return, its scope is limited to the subshell, which exits almost immediately thereafter.
Again, nothing in the code you presented results in your function ever being called, and
even if it were called, and it did set var in the current shell, you test for a value of $var different from the one your function would set.
In contrast, consider this other fragment of your code:
PS1+="\[$(tput setaf 124)\] red"
Because the whole string is double-quoted in this case, the contents are unequivocally subject to command expansion. If this were executed, the $(tput setaf 124) would be replaced with the output from running tput setaf 124. That would happen at the time that PS1's value is modified, not every time its value is displayed.
Although you can generate a prompt that contains ANSI escape sequences, you cannot do it quite the way you're trying to do. Inasmuch as your specific needs are unclear, I hesitate to suggest a particular alternative.

Bash: How to set changing variable in PS1, updating every prompt

I've searched for quite a while, but haven't been able to find a post or any information whatsoever about adding a variable that is, well, variable (i.e. changing) within PS1 in bash, that will update every time a new prompt occurs. In concreto, I would like the width of the prompt to span across the whole terminal window, e.g.:
7zS2::awesome| --------------------------------------------------------- ~/.config/awesome
This is what I have so far, omitting color codes for legibility:
mytest=$PWD
mynext="$(basename $PWD)"
mylength=$((${#mytest}+${#mynext}))
length=$(($mylength+6))
PS1='7zS2::\W| $(printf "\\u2500%.0s" $(seq $length $(tput cols))) \w\n\$'
Which works perfectly whenever I
exec bash
to reset the prompt.
However, I would like it to work without me having to reload bash. Surely there must be a way of doing this, as \w, \W and of the likes are unique and updated every prompt as well. Any way of easily doing this?
Thanks!
7zS2
Try this. Place your code into a file, I'm calling it gash.sh in my home directory, with a small modification: replace the PS1 assignment with echo, and replace the single quotes with double.
mytest=$PWD
mynext="$(basename $PWD)"
mylength=$((${#mytest}+${#mynext}))
length=$(($mylength+6))
echo "7zS2::\W| $(printf \\u2500%.0s $(seq $length $(tput cols))) \w\n\$"
then in your startup file set PS1:
PS1='$(~/gash.sh)'

emacs terminal bash (PS1) prompt duplicated

This is a bit of a convoluted question, but here goes nothing!
I've recently updated my bash prompt to the nice version appearing in the last post of this thread: Bash: custom PS1 with nice working directory path.
The relevant bash code (from that thread post) is copied here:
# define the awk script using heredoc notation for easy modification
MYPSDIR_AWK=$(cat << 'EOF'
BEGIN { FS = OFS = "/" }
{
if (length($0) > 16 && NF > 4)
print $1,$2,".." NF-4 "..",$(NF-1),$NF
else
print $0
}
EOF
)
# my replacement for \w prompt expansion
export MYPSDIR='$(echo -n "${PWD/#$HOME/~}" | awk "$MYPSDIR_AWK")'
# the fancy colorized prompt: [0 user#host ~]%
# return code is in green, user#host is in bold/white
export PS1='[\[\033[1;32m\]$?\[\033[0;0m\] \[\033[0;1m\]\u#\h\[\033[0;0m\] $(eval "echo ${MYPSDIR}")]% '
# set x/ssh window title as well
export PROMPT_COMMAND='echo -ne "\033]0;${USER}#${HOSTNAME%%.*} $(eval "echo ${MYPSDIR}")\007"'
This prompt looks roughly like so (in non-emacs terminals):
[0 user#host ~/my_dir]%
Where the "0" above is green and the "user#host" is bold.
(Note that the "0" can be all sorts of numbers, and represents the return value of the last command.)
The issue I'm experiencing is specific to shells running within emacs (and it occurs for most variants of terminal-interaction within emacs: 'term', 'ansi-term', 'shell', and 'eshell').
The prompt appears twice (and slightly broken) in emacs terminals, like so:
0;user#host ~/my_dir[0 user#host ~/my_dir]%
The 'second' version of the prompt, starting from and including the "[" looks just fine.
It's the preceding text, which appears without any styling (i.e. no green and no bold).
So, emacs must be interpreting some portion of the prompt as input, and my guess is the color or bold escaped indicators attached to the "0" and "user#host" portions of the prompt?
Might anyone know how to tell emacs to interpret the escapes correctly?
Or, alternatively, how to modify the prompt-setting commands such that both emacs will not hate it and it'll still work in non-emacs terminals?
And maybe even another alternative: how to add a test for the terminal type ('eterm-color' within emacs) with a modified string that is emacs-friendly?
The error comes from the export PROMPT_COMMAND=... statement.
You can avoid this being read in your configuration, by checking whether you have a shell running inside emacs or not. Here the environment variable INSIDE_EMACS becomes handy. From the Emacs manual (Sect. 32.7):
Emacs sets the environment variable INSIDE_EMACS in the subshell to ‘version,comint’, where version is the Emacs version (e.g., ‘24.1’). Programs can check this variable to determine whether they are running inside an Emacs subshell
In your example, you want
export PROMPT_COMMAND='echo -ne "\033]0;${USER}#${HOSTNAME%%.*} $(eval "echo ${MYPSDIR}")\007" only being executed when you are not in emacs, otherwise you get this nasty "double prompt". The following conditional statement in your code will help.
if [ -z "$INSIDE_EMACS" ];
then
export PROMPT_COMMAND='echo -ne "\033]0;${USER}#${HOSTNAME%%.*} $(eval "echo ${MYPSDIR}")\007"'
else
export PROMPT_COMMAND=''
fi
It checks whether you are not inside emacs, and only then the PROMPT_COMMAND variable is set to your desired value.
The extra display is coming from the PROMPT_COMMAND variable's contents. emacs appears not to understand the OSC 0 title setting xterm escape sequence and so prints out the output.

ZSH script and prompt profiling?

This answer, "How to profile a bash shell script?", seems to nearly perfectly cover what I'm trying to accomplish here. I currently have some zsh scripts that modify the prompt, however I think some updates to oh-my-zsh have evoked some issues that I need to hunt down. The sluggishness from time to time is unbearable.
To this end, how would you adapt the prompt sections in this example answer to work with zsh vs bash?
Presently I have modified /etc/zshenv such that it has the initial suggested code from the example:
PS4='+ $(date "+%s.%N")\011 '
exec 3>&2 2>/tmp/bashstart.$$.log
set -x
And my ~/.zshrc has the following appended to it's tail:
set +x
exec 2>&3 3>&-
Of course these are not valid for ZSH shell customization. My prompt rendering code utilizes oh-my-zsh customizations. I could prepend the appropriate code to the prompt I suppose or I'm open to other suggestions.
Calling date for each command will fork and exec, which adds overhead which may interfere with your measurements.
Instead, you could use
PS4=$'+ %D{%s.%6.}\011 '
to log timestamps with lower overhead (up to millisecond precision).
For some notes on processing the resulting logs, see http://blog.xebia.com/profiling-zsh-shell-scripts/
You may need to do
setopt prompt_subst
if it's not already.
Also, in order to interpret the octal escape for tab, use $'':
PS4=$'+ $(date "+%s.%N")\011 '
You may also find some of these escapes to be useful:
%? The return status of the last command executed just before the prompt.
%_ The status of the parser, i.e. the shell constructs (like `if' and `for') that have been started on the command
line. If given an integer number that many strings will be printed; zero or negative or no integer means print as
many as there are. This is most useful in prompts PS2 for continuation lines and PS4 for debugging with the
XTRACE option; in the latter case it will also work non-interactively.
%i The line number currently being executed in the script, sourced file, or shell function given by %N. This is most
useful for debugging as part of $PS4.
%I The line number currently being executed in the file %x. This is similar to %i, but the line number is always a
line number in the file where the code was defined, even if the code is a shell function.
%L The current value of $SHLVL.
%N The name of the script, sourced file, or shell function that zsh is currently executing, whichever was started
most recently. If there is none, this is equivalent to the parameter $0. An integer may follow the `%' to spec‐
ify a number of trailing path components to show; zero means the full path. A negative integer specifies leading
components.
%x The name of the file containing the source code currently being executed. This behaves as %N except that function
and eval command names are not shown, instead the file where they were defined.

Resources