Learn how to write fish shell scripts by example.

How to set variables

Here’s a simple example of assigning a value to a variable.

set MY_VAR "some value"

Here’s an example of appending values to a variable. By default fish variables are lists.

set MY_VAR $MY_VAR "another value"

This is how you can create lists.

set MY_LIST "value1" "value2" "value3"

Here’s an example of storing value returned from the execution of a command to a variable.

set OUR_VAR (math 1+2)
set OUR_VAR (date +%s)
set OUR_VAR (math $OUR_VAR / 60)

Since all fish variables are lists, you can access individual elements using [n] operator, where n=1 for the first element (not 0 index). Here’s an example. And negative numbers access elements from the end.

set LIST one two three
echo $LIST[1]  # one
echo $LIST[2]  # two
echo $LIST[3]  # three
echo $LIST[-1] # This is the same element as above

You can also use ranges from the variable / list, continuing the example above.

set LIST one two three
echo $LIST[1..2]  # one two
echo $LIST[2..3]  # two three
echo $LIST[-1..2] # three two

How to write for loops

Since variables contain lists by default, it is very easy to iterate thru them. Here’s an example.

set FOLDERS bin
set FOLDERS $FOLDERS .atom
set FOLDERS $FOLDERS github
for FOLDER in $FOLDERS
  echo "item: $FOLDER"
end

How to write if statements

The key to writing if statements is using the test command to evaluate some expression to a boolean. This can be string comparisons or even testing the existence of files and folders. Here are some examples.

String comparison in variable.

if test $hostname = "mymachine"
  echo "hostname is mymachine"
end

Checking for file existence.

if test -e "somefile"
  echo "somefile exists"
end

How to execute strings

The safest way to execute strings that are generated in the script is to use the following pattern.

echo "ls \
  -la" | sh

This not only makes it easier to debug, but also avoids strange errors when doing multi-line breaks using \.

How to write functions

A fish function is just a list of commands that may optionally take arguments. These arguments are just passed in as a list (since all variables in fish are lists).

Here’s an example.

function say_hi
  echo "Hi $argv"
end
say_hi
say_hi everbody!
say_hi you and you and you

How to use sed

This is useful for removing fragments of files that are not needed, especially when xargs is used to pipe the result of find.

Here’s an example that removes ./ from the start of each file that’s found.

echo "./.Android" | sed 's/.\///g'

Here’s a more complex example of using sed, find, and xargs together.

set folder .Android*
find ~ -maxdepth 1 -name $folder | sed 's/.\///g' | \
  xargs -I % echo "cleaned up name: %"

How to use xargs

This is useful for piping the output of some commands as arguments for more commands.

Here’s a simple example: ls | xargs echo "folders: ".

  • Which produces this: folders: idea-http-proxy-settings images tmp.
  • Note how the arguments are concatenated in the output.

Here’s a slightly different example using -I % which allows arguments to be placed anywhere (not just at the end).

ls | xargs -I % echo "folder: %"

Which produces this output:

folder: idea-http-proxy-settings
folder: images
folder: tmp

Note how the arguments are each in a separate line.

How to use cut to split strings

Let’s say you have a string "token1:token2" and you want to split the string and only keep the first part of it. This can be done using the following cut command.

echo "token1:token2" | cut -d ':' -f 1
  • -d ':' - this splits the string by the : delimiter
  • -f 1 - this keeps the first field in the tokenized string

Here’s a real example of finding all the HTML files in ~/github/developerlife.com with the string "fonts.googleapis" in it and then opening them up in subl.

cd ~/github/developerlife.com
echo \
"find . -name '*html' | \
 xargs grep fonts.googleapis | \
 cut -d ':' -f 1 | \
 xargs subl" \
 | sh

How to calculate how long the script took to run

set START_TS (date +%s)

# This is where your code would go.
sleep 5

set END_TS (date +%s)
set RUNTIME (math $END_TS - $START_TS)
set RUNTIME (math $RUNTIME / 60)
echo "⏲ Total runtime: $RUNTIME min ⏲"

Related Posts