In my zsh configuration I define a number of aliases. One particular zsh alias maps gl
to my custom Git alias git lg
(see Improve Git Log for details). Since my zsh configuration is portable, I want to define a fallback if my custom Git alias does not exist on the machine.
Short Answer
This is the code I added to my .zshrc
function gl() {
git lg "$@" 2>/dev/null || \
[ $? -eq 141 ] || \
git log --oneline --graph "$@"
}
Note: This could be written as one long command however I’m using \
to break it up over multiple lines for readability. The following is equivalent
function gl() {
git lg "$@" 2>/dev/null || [ $? -eq 141 ] || git log --oneline --graph "$@"
}
Why a Function?
Initially, I tried an alias (e.g. alias gl="git lg || git log --oneline --graph"
), which worked great until I realized it did not support passing in arguments.
e.g. gl develop
would ignore the “develop” part of the command and run just git lg
(instead of git lg develop
)
To fix this, I needed to provide the arguments from the command, which are available in the environment variable $@. Unfortunately, we can not use a variable in an alias
so we’re using a function
instead.
Why 2>/dev/null ?
When you run a command from the command line, two different streams of data are output to the screen (stdout
and stderr
). The stdout
is the output of the command, while stderr
is any error messages.
If you try to run git lg
and the Git alias does not exist, the following is output on the screen through the stderr
stream.
git: 'lg' is not a git command. See 'git --help'.
The most similar command is
log
If we do not include 2>/dev/null
with out git lg
command, when it fails we get the above output before the output of the fallback (git log --oneline --graph
).
By adding 2>/dev/null
, we send the stderr
to /dev/null
, which makes it disappear (preventing the above error message from displaying when git lg
fails).
Why || ?
The ||
is a logical OR, which says if the first command fails then run the next command. If the first command succeeds, then do not run the second.
An Example
You can try running the following from the command line.
echo "one" || echo "two"
This will display one
(the second command does not run because the first was successful)
Another Example
You can try running the following from the command line.
notacommand || echo "two"
This will display an error message and then because the first command failed, it will display two
You can suppress the error message with 2>/dev/null
, now this will only display “two” (the fallback)
notacommand 2>/dev/null || echo "two"
Remember 2/dev/null
only suppresses the error message, so if we do the following we still get just one
in the output
echo "one" 2>/dev/null || echo "two"
What is success?
How does the machine know the command was successful? Whenever a command is run an exit code is returned, if the code is zero (0
), the command is considered successfully run. The exit code for the last command is stored in the environment variable $?
Example of Exit Code
Running the command echo $?
will output the last exit code. e.g.
If you run the command ls
and then run
echo $?
The output will be 0
If you run the command notacommand
(you will see an error message on the screen) and if you then run
echo $?
The output will be 127
(a non-zero exit code, indicating an unsuccessful command).
Why [ $? -eq 141 ]
As mentioned above, a command is considered successful when the exit code is zero (0
), however git log
sometimes returns an exit code of 141
when it runs “successfully” (see git log exit code 141? for more details).
Therefore we add this check to see if the exit code ($?
) is equal (-eq
) to 141
. If it is equal, this check returns an exit code of its own of 0
, which prevents running the fallback command.
Result
The result of this learning journey is this function definition in my zsh configuration.
function gl() {
git lg "$@" 2>/dev/null || \
[ $? -eq 141 ] || \
git log --oneline --graph "$@"
}
Leave a Reply