Functions and Reusable Scripts

Defining a function

A function is a named block of commands you can call again and again. Inside it, $1, $2 and $@ are the function's own arguments, just like in a script:

greet() {
  echo "Hello, $1!"
}
greet Ada
greet Linus

In the terminal

student@vulntech:~$ greet() {
>   echo "Hello, $1!"
> }
student@vulntech:~$ greet Ada
Hello, Ada!
student@vulntech:~$ greet Linus
Hello, Linus!

Return values

A function reports success or failure with return and an exit code, which you test with if exactly as for any command:

is_even() {
  if [ $(( $1 % 2 )) -eq 0 ]; then return 0; else return 1; fi
}
if is_even 10; then echo "10 is even"; fi
if is_even 7; then echo yes; else echo "7 is odd"; fi

In the terminal

student@vulntech:~$ is_even() {
>   if [ $(( $1 % 2 )) -eq 0 ]; then return 0; else return 1; fi
> }
student@vulntech:~$ if is_even 10; then echo "10 is even"; fi
10 is even
student@vulntech:~$ if is_even 7; then echo yes; else echo "7 is odd"; fi
7 is odd

To hand back a piece of text instead, echo it and catch it with $( ):

double() {
  echo $(( $1 * 2 ))
}
result=$(double 21)
echo "double 21 is $result"

In the terminal

student@vulntech:~$ double() {
>   echo $(( $1 * 2 ))
> }
student@vulntech:~$ result=$(double 21)
student@vulntech:~$ echo "double 21 is $result"
double 21 is 42

local variables

Mark a function's own variables local so they don't clobber ones outside it. Without local, every variable is shared across the whole script, which causes surprising bugs:

count=99
add() {
  local count
  count=$(( $1 + $2 ))
  echo "inside: $count"
}
add 2 3
echo "outside: $count"

In the terminal

student@vulntech:~$ count=99
student@vulntech:~$ add() {
>   local count
>   count=$(( $1 + $2 ))
>   echo "inside: $count"
> }
student@vulntech:~$ add 2 3
inside: 5
student@vulntech:~$ echo "outside: $count"
outside: 99

A small tool, put together

Here's everything so far in one script: a function, a loop, an if and arguments. It counts how many of the files you name actually exist:

cat <<'EOF' > count-files.sh
#!/bin/bash
exists() { [ -e "$1" ]; }
found=0
for path in "$@"; do
  if exists "$path"; then
    echo "found: $path"
    found=$(( found + 1 ))
  else
    echo "missing: $path"
  fi
done
echo "$found of $# exist"
EOF
chmod +x count-files.sh
./count-files.sh /etc/hostname /etc/nope /bin/bash

In the terminal

student@vulntech:~$ cat <<'EOF' > count-files.sh
> #!/bin/bash
> exists() { [ -e "$1" ]; }
> found=0
> for path in "$@"; do
>   if exists "$path"; then
>     echo "found: $path"
>     found=$(( found + 1 ))
>   else
>     echo "missing: $path"
>   fi
> done
> echo "$found of $# exist"
> EOF
student@vulntech:~$ chmod +x count-files.sh
student@vulntech:~$ ./count-files.sh /etc/hostname /etc/nope /bin/bash
found: /etc/hostname
missing: /etc/nope
found: /bin/bash
2 of 3 exist

Remember: name() { ...; } defines a function; call it like a command. $1 is its first argument, return N its exit code, and local keeps a variable to itself.

HOME LEARN COMMUNITY DASHBOARD
Practice terminal Starting…