ZSH: prompt expansion return code greater than - zsh

The problem is (theoretically) simple. All I want is for my zsh prompt to print the return code if it is less than or equal to 128 and the corresponding signal when greater than 128. I cannot find any example of this being done and the zsh docs only specify how to do it
if the exit status of the last command was n
The only version I have got (somewhat) working is the following (which only works for SIGINT):
PROMPT='%130(?.[$(kill -l $?)].$?)> '
I have also tried using precmd but completely failed with that (it appears the return code is interfered with when zsh is executing the function but don't quote me on that).

The solution was indeed simple and just involved creating a different function (to which I passed the return code) rather than using precmd. Below is the final version of my zsh prompt, including the return code / signal behaviour:
code () {
if (( $1 > 128 )); then
echo "SIG$(kill -l $1)"
else
echo $1
fi
}
setopt promptsubst
PROMPT='%F{green}%n%f#%m %F{cyan}%~%f> '
RPROMPT='%(?..%F{red}[$(code $?)]%f'

Related

Avoid variable expansion in zsh

If I use the zsh shell and execute the following command I get
$zsh
$echo '$_GET["test"]'
preexec: bad math expression: operand expected at `"test"'
$echo '$_GET[]'
preexec: invalid subscript
In bash I get what I expect:
$bash
$echo '$_GET["test"]'
$_GET["test"]
I assume that zsh is trying to expand the $_GET variable. How can I avoid this? I always expected this to only happen within double quotes anyhow.
[update]
I found the following three lines in the .zshrc:
# Display last command interminal
echo -en "\e]2;Parrot Terminal\a"
preexec () { print -Pn "\e]0;$1 - Parrot Terminal\a" }
After commenting them out everything seems to work as expected.
What I understand is that preexec is executed after a command in the terminal has been submitted but before it is executed. The $1 is the command that one submitted.
I still do not understand the purpose of the two lines but is it because of the double quotes in the preexec print statement that the variables are expanded?
The combination of print -P together with the expansion of $1 is killing you. With this, you first get a "normal" expansion of $1, yielding something like "\e]0;echo '$_GET["test"]'...". Now -P causes print to do a prompt expansion on this string, which means that it has to expand $_GET["test"] as well. This causes the error.
I suggest to remove the -P, in particular since you don't have any characters in your string which would benefit from prompt expansion.

ZSH ZLE Widget remove accept-line command from history

I would like to add some additional syntax to zsh. I would like //some/path/to/file to be relative to the root of my source code tree (/src/repositories/projects). So running ls //some/path/to/directory should list files in /src/repositories/projects/some/path/to/directory.
The best way I've found to do this is with a ZLE accept-line widget which rewrites //... paths with the full UNIX path /src/repositories/projects/... and then invokes the command. This works quite well, but it expands the //... syntax inline and stores the expansion in history, which can make searches a bit more complicated. I would like to store the original //... syntax in the history while keep the /src/repositories/projects/... syntax out.
I am able to use print -s to add the original //... syntax to history, but I am having trouble keeping the expanded syntax out of history. Currently my code looks like this:
function accept-line-override() {
# Add untranslated command to history
print -s "$BUFFER"
# Translate //... paths to /src/repositories/projects/...
BUFFER=$(fix-paths "$BUFFER")
# Invoke command with new paths
zle .accept-line
}
zle -N accept-line accept-line-override
This gives me two lines of history for every invoked command:
$ echo //test
/src/repositories/projects/test
$ history | tail -n 2
10020 echo //test
10021 echo /src/repositories/projects/test
Is there a way I can prevent zle .accept-line from adding its command to history? I've seen methods involving history -d but those only seem to work for bash. I also saw somewhere to do:
function accept-line-override() {
# Add untranslated command to history
print -s "$BUFFER"
# Translate //... paths to /src/repositories/projects/...
BUFFER=$(fix-paths "$BUFFER")
# Manually invoke line so it is not added to history
echo
eval $BUFFER
echo
# Reset prompt
BUFFER=""
zle .reset-prompt
}
But this leads to a lot of undesired side-effects, from Emacs refusing to open to auto-completions not clearing before the command is executed. I'd very much like to avoid that complexity if at all possible. Any idea on getting around this?

zsh function only runs once

I'm fairly new to zsh (oh-my-zsh) and i'm trying to write a custom theme.
I ran into a problem and reduced it to the following testcase
PROMPT='$RANDOM > '
works as expected, it produces a random number on each command.
But when using a function
PROMPT='$(my_random) > '
function my_random(){
echo $RANDOM
}
it always returns the same number, even after source ~/.zshrc still the same number. only when i close the terminal window and open it again, i get a new number which stays the same for the complete session.
only when i do:
PROMPT='$RANDOM $(my_random) > '
function my_random(){
echo $RANDOM
}
i get two random numbers as expected... any explanation for this behaviour?
btw, i'm using kde's konsole on a fresh arch install.
Edit
fwiw i found using /dev/urandom directly works well. I would still like to know whats going on.
function my_random() {
echo $(cat /dev/urandom | tr -dc '0-9' | head -c5)
}
$()-expansion happens in a subshell, and changes to $RANDOM in a subshell don’t affect the parent. From zshparam(1):
The values of RANDOM form an intentionally-repeatable pseudo-random sequence; subshells that reference RANDOM will result in identical pseudo-random values unless the value of RANDOM is referenced or seeded in the parent shell in between subshell invocations.
You don’t need to turn to setting the prompt to reproduce it:
% echo $(echo $RANDOM)
17454
% echo $(echo $RANDOM)
17454
bash doesn’t share zsh’s behaviour here.
The annoying bit is that prompt expansion also happens in a subshell, so you can’t just fix this by referencing $RANDOM in, say, precmd. The best way I can find is to do it in an empty expansion:
PROMPT='${RANDOM##*}$(my_random) > '
As said in a comment by chepner, you can fix this by putting : $RANDOM; in your precmd. This causes the value of $RANDOM to be taken and a new one to be generated.
e.g.
precmd() {
: $RANDOM;
...
}

Use preexec() to evaluate entered command

I want to use preexec() to modify certain commands before they are run but I need to be able to evaluate the current entered command. Is there a variable that contains the entire command before it is executed? I know !! is the last command but I need the current line before it's saved to history.
An example of what I want to do would probably help
ls -l /root please
And then I want preexec to see I wrote "please" at the end and replace it with
sudo ls -l /root
I think something like
preexec() {
if [[ $CURRENT_LINE =~ please$ ]]; then
$CURRENT_LINE="sudo ${CURRENT_LINE% please}"
fi
Would work but I can't find a variable in zsh that gives me the correct $CURRENT_LINE
For bonus points I also want to be able to enter please on a line by itself and have it run sudo !! but I could probably do that with some form of alias.
I think it might be better to make a please function that I can pipe a command to but I don't think that'll work as well because the command will run and fail (before piping) before it is run again with sudo.
As far as I know that the preexec is not for the right place to modify the command to be executed though. We can not change the commands to be executed from inside of the preexec function…
Although the actual command to be executed are passed as $1, $2 and $3.
preexec
Executed just after a command has been read and is about to be executed. If the history mechanism is active (and the line was not discarded from the history buffer), the string that the user typed is passed as the first argument, otherwise it is an empty string. The actual command that will be executed (including expanded aliases) is passed in two different forms: the second argument is a single-line, size-limited version of the command (with things like function bodies elided); the third argument contains the full text that is being executed.
-- zshmisc(1) 9.3.1 Hook Functions
For example:
alias ls='ls -sF --color=auto'
preexec () {
print ">>>preexec<<<"
print -l ${(qqq)#}
}
If I have above in ~/.zshrc then I will get follows:
% echo test preexec<Esc-Return>
ls<Return>
;# outputs below
>>>preexec<<<
"echo test preexec
ls"
"echo test preexec; ls -sF --color=auto"
"echo test preexec
ls -sF --color=auto"
test preexec
total 1692
...
You could add your own zle widget functions to the zsh line editor for manipulating the line editor buffer. (zshzle(1))
You could add the zle widget function to change the behavior for hitting Enter.
my-accept-line () {
if [[ "$BUFFER" == *" please" ]]; then
BUFFER="sudo ${BUFFER% please}"
fi
zle .accept-line
}
zle -N accept-line my-accept-line
The above snippets changes the functionality for accept-line from the built-in behavior to my-accept-line defined here.
Adding the abbreviations also could help which is described below:
Cloning vim's abbreviation feature
-- “examples:zleiab [ZshWiki]” - http://zshwiki.org/home/examples/zleiab

zsh - iterate over files matched using parameter in the script

I have this code executed fine and correctly in the zsh shell:
for f in ./A*.html; do; echo $f; done
output
./Aliasing.html
./Alternate-Forms-For-Complex-Commands.html
./Alternative-Completion.html
./Arguments.html
./Arithmetic-Evaluation.html
./Arithmetic-Expansion.html
./Array-Parameters.html
./Author.html
./Availability.html
However when I use this code, but sending a matching string (./A*.html) as a parameter in the zsh function, it will display only the first file
script:
displayy() {
for f in $1; do; echo $f; done
}
command:
%displayy ./A*.html
output
./Aliasing.html
I would rather expect the same files are printed out as when executing for loop in shell (first example). Any ideas what I'm doing wrong? Grazie
The problem with displayy ./A*.html command is that * is expanded by zsh before it is passed to the dispayy function. So in fact your command looks like this:
$ displayy ./Aliasing.html ./Alternate-Forms-For-Complex-Commands.html ...
Then in displayy you print just first argument: ./Aliasing.html.
The easiest way to solve this issue is to change one char (1=>#) in you displayy definition:
displayy() {
for f in "$#"; do; echo "$f"; done
}
This way iteration of loop goes through all display arguments. Additionally I recommend to put double quotes around variables as a good habit, even if in this example there are no whitespaces in file names.

Resources