Use Exit Status of A Command in A Fish Shell Function

talha131

Talha Mansoor

Posted on March 16, 2020

Use Exit Status of A Command in A Fish Shell Function

Fish shell is a user-friendly and a feature-rich alternative to the Bash shell. Think Zsh, but with saner and user-friendly defaults. Fish shell "just works", requiring minimal customizations. You do not have to waste time scouring the internet for tutorials and example config files.

When writing scripts, a lot of time you run into cases where you have to get the exit status of the last command executed. In this article, I detail all the ways you can use to the exit status of a command.

In the following examples, I am going to use rg, i.e. ripgrep. It’s a modern and faster equivalent of the traditional grep and ack commands. rg also has a sane default configuration that does not require you to enable command switches to do mundane tasks like searching recursively or respecting .gitignore rules.

$status variable

Fish Shell stores the exit status of the last command in the $status variable.

trueecho $status
0
➤ falseecho $status
1
➤ blahblah
fish: Unknown command blahblah
➤ echo $status
127

Use it with if and test in your scripts.

function example -d 'Example Fish function'
    rg $argv[1] > /dev/null
    if test $status -eq 0
        echo "$argv[1] found"
    else
        echo "$argv[1] not found"
    end
end

Notice how I used test to check the value of $status variable.

You can read more about $status variable in fish documentation.

Use and, or Combiners

Fish also supports and and or.

  1. and runs the command if the last command execution was successful.
  2. or runs the command if the previous command execution failed.
➤ rg "blahblah" > /dev/null; and echo "found"; or echo "not found"

We can use newlines instead of ;. Use Alt (⌥Option key on macOS) and ⌤Enter key to insert newlines.

➤ rg "blahblah" > /dev/null
   and echo found
   or echo not found

You can read more about and, or combiners in fish shell documentation.

Use and, or Combiners with begin

We can do complex operations like running more than one command using begin with the combiners.

function example -d 'Example Fish function'
    rg $argv[1] > /dev/null
    and begin
        echo "$argv[1] found"
    end
    or begin
        echo "$argv[1] not found"
    end
end

Between begin and end you can write more commands or even more conditions.

You can read more about begin here.

Use if

You do not have to use the $status variable and test command. You can use if directly.

function example -d 'Example Fish function'
    if rg $argv[1] > /dev/null
        echo "$argv[1] found"
    else
        echo "$argv[1] not found"
    end
end

Store output of a command in a variable and test it

Sometimes reading the exit status of a command is not enough. Take git cherry, for example. This command returns the unpushed git commits.

Whether it finds unpushed git commits push or not, it always exits successfully.

Consider the following script,

➤ git cherry
   and echo commits found
   or echo commits not found

I run it in a repository whose HEAD is one commit ahead of the upstream. The output I get is

+ 12dc1f697b714336b118f1361fa49a4ef71c44b7commits found

Then I run it in a repository whose HEAD is in sync with the upstream. The output is,

commits found

Even though git cherry couldn’t find any unpushed git commits. I cannot employ previously suggested methods that rely on the exit status of the command.

In this case, we have to test the output of the command.

Test Output As String

Consider the following script,

function example -d 'Example Fish function'
    if test -n "(git cherry)"
        echo commits found
    else
        echo commits not found
    end
end

Do you notice the quotes around (git cherry)? It is crucial.

See, git cherry can return more than one line in the output. With quotes, the Fish shell treats the command output as one argument, even if it has newlines.

If we remove the quotes, then if newlines are present, the output becomes a list or an array.

test -n returns true if the length of the string is non-zero.

You can read about test command here.

Count Number Of Lines In Output

It is not necessary to use quotes. We can refactor the above script to handle arrays and then count the number of indices in the array.

function example -d 'Example Fish function'
    if test (count (git cherry)) -ne 0
        echo commits found
    else
        echo commits not found
    end
end

count command returns the number of elements in the list. So with count (git cherry), we read the number of lines returned and compare it to 0.

test -ne returns true if the first number is not equal to the second number.

count documentation is available here.

Example

You can see the above discussion in action in a script that I wrote to periodically push out git commits to the upstream, every 10 seconds.

Source Code of tm_git_push script


Cover image attribution: David Clode

💖 💪 🙅 🚩
talha131
Talha Mansoor

Posted on March 16, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related