I am writing a script that must be sourced and that I use daily in the terminal (zsh), and the solution to one of a problem in the script is to unlock a flock lock, I would do it in the trap for RETURN.
But, it seems the trap <code> RETURN syntax does not work in Zsh as it does in Bash.
I have this simple script here, test.sh:
#!/bin/bash
trap 'echo returned' RETURN
echo "Hello, World!"
Here what happens when I source it with Zsh:
[urpagin:~/sandbox/tmp_sbox/F0MPlT]$ echo "$0"
/usr/bin/zsh
[urpagin:~/sandbox/tmp_sbox/F0MPlT]$ source test.sh
test.sh:trap:3: undefined signal: RETURN
Hello, World!
Here what happens when I source it with BASH:
[urpagin@ikari F0MPlT]$ echo "$0"
bash
[urpagin@ikari F0MPlT]$ source test.sh
Hello, World!
returned
And with another style that seems to be from Zsh, we define test2.sh:
#!/bin/zsh
TRAPEXIT() {
echo "Exited"
}
TRAPRETURN() {
echo "Returned"
}
echo "Hello, World!"
We can see that when run like a normal script using zsh, the TRAPEXIT() function gets called, whereas the TRAPRETURN() function does not.
[urpagin:~/sandbox/tmp_sbox/F0MPlT]$ echo "$0"
/usr/bin/zsh
[urpagin:~/sandbox/tmp_sbox/F0MPlT]$ zsh test2.sh
Hello, World!
Exited
[urpagin:~/sandbox/tmp_sbox/F0MPlT]$ source test2.sh
Hello, World!
Exited
We also observe that the TRAPRETURN() function is never called, even when we source the file. The expected behavior would be that the function gets called.
So it seems:
trap RETURN is Bash-specific and not recognized in Zsh.TRAPEXIT, but TRAPRETURN does not seem to exist.So then, is it even possible to execute code when a sourced script returns?
Zsh doesn't have a trap return construct. Instead, you can simulate it using a custom hook. There's a tiny plugin that helps you define your own hooks: https://github.com/zsh-hooks/zsh-hooks. You don't have to use this, but it helps make defining a custom hook much simpler.
Back to your example, lets say you have a script foo.sh that you want to work in both Bash and Zsh. It would have the following contents:
# foo.sh
trap_return() {
echo "returned..."
}
if [[ -n "$ZSH_VERSION" ]]; then
hooks-add-hook post_source trap_return
else
trap 'trap_return' RETURN
fi
echo "Hello, World!"
Next, you need to wire up the Zsh script you're sourcing foo.sh from. Let's call it bar.zsh
#!/bin/zsh
# bar.zsh
source /path/to/zsh-hooks.plugin.zsh
# Define a new way to source that wires up and uses the hook.
# You could even mask source itself by calling this func `source`.
source_with_hook() {
# Define a post_source hook
hooks-define-hook post_source
# Source the file
builtin source "$@"
# Run the post_source hook, and remove it for the next call
hooks-run-hook post_source
unset post_source
}
# Now, source foo.sh
source_with_hook /path/to/foo.sh
Now, let's show that it works in both Bash and Zsh:
$ bash
$ echo $BASH_VERSION
5.3.3(1)-release
$ source foo.sh
Hello, World!
returned...
$ zsh
% echo $ZSH_VERSION
5.9
% # remember, bar.zsh has our wrapper, and sources foo.sh with our hook
% source bar.zsh
Hello, World!
returned...