Share

What you need to know about bash functions

Bash, as a scripting language, can be a bit painful sometimes. It doesn't have good distinction between variable types, it has strange syntax for functions and the syntax for statements (such as if/while/case) is unintuitive and inconsistent. Having said that, you can rely on shell script being available on any Unix machine, and bash is almost as prevalent. Therefore, it's often the best choice for scripting.

You can get by with knowing the basic syntax, script parameters, variable assignment and command execution. However, knowing how to use functions will really boost your bash skills by a few levels. Here's everything you need to know to start using them straight away.

Syntax

Bash functions follow this format:

function speak {
  echo "Hello, I'm a function"
}

speak

# => "Hello, I'm a function"

Or you can use the alternative syntax, which doesn't use the function keyword, and includes parentheses:

speak() {
  echo "Hello, I'm a function"
}

speak

# => "Hello, I'm a function"

Arguments/parameters

Functions act like miniature scripts. There's no way of declaring arguments in the function signature; they use the same $ prefixed variables that you use when collecting arguments to a script.

function say {
  local word=$1
  echo "You wanted me to say $word"
}

say "cheese"

# => "You wanted me to say cheese"

And a more advanced example:

function can-haz-args {
  echo "You gave me $# arguments: $@"
}

can-haz-args "one" 2 etc

# => "You gave me 3 arguments: one 2 etc"

Local variables

Thanks to Will Morton for suggesting this section.

You may have noticed that I used the keyword local in one of the functions in one of the previous examples. This is because standard bash variables are global, as demonstrated here:

meaning_of_life=42

function be_destructive {
  meaning_of_life=39
}

be_destructive

echo "The meaning of life is $meaning_of_life"

# => The meaning of life is 39

The local keyword changes function variables to only apply to the current scope - global variables won't be overwritten:

meaning_of_life=42

function try_and_be_destructive {
  local meaning_of_life=39
}

try_and_be_destructive

echo "The meaning of life is $meaning_of_life"

# => The meaning of life is 42

Therefore, it's good practice to use the local keyword in functions, to avoid unexpected behaviour.

Returning values

There is a return statement for bash functions, but the return must be a numeric value, to be consistent with all bash commands.

function gimme_a_code {
  return 10
}

gimme_a_code
echo $?

# => 10

Returning a non-numeric value from a bash function is pretty tricky. The best way I've come across is to echo the value in the function, then to evaluate that function in the main script, like so:

function strip {
  echo "$1" | tr -d " "
}

stripped=$(strip " lots of spaces  ")

echo "$stripped"

# => "lotsofspaces"

You can also use backticks (``) to evaluate, instead of$()`, but the latter is preferred.

Chaining

Functions behave like bash commands, which means that you can use &&:

function do_stuff {
  echo "Doing stuff"
}

function do_something_else {
  echo "Doing something else"
}

do_stuff && do_something_else

# => "Doing stuff"
# => "Doing something else"

And you can use || to run something else in the case of a failure:

function fail {
  test -f nonexistentfile
}

fail || do_something_else

# => "Doing something else"

Miscellaneous behaviour

Exit statements exit the whole script:

function die {
  exit 1
}

echo "I'm dying!"
die
echo "I'm alive!"

#=> "I'm dying!"

You can therefore use functions as a "execute this or die" wrapper:

function do_or_die {
  $@ || { echo "Command failed: $@" && exit 1; }
}

do_or_die test -f /important/file

echo "Phew, everything's fine"

# => "Command failed: test -f /important/file"

Summary

Hopefully there's something here that you can take away and apply to your bashing. If you have any more useful tips then feel free to leave a comment.

← Previous post: Creating a Cinch plugin part 1: a word game bot for IRC Next post: Creating a Cinch plugin part 2: a word game bot for IRC →
comments powered by Disqus