Functions: Parameters

Naming your parameters

Convention goes a long way; like “a destination is always first and source is the second argument”. But you can improve readability quite a lot, by immediately assigning the positional parameters to local variables.

copy_file_if_present()
{
   local dst="$1"
   local src="$2"

   if [ -f "${src}" ]
   then
      cp "${dst}" "${src}"
   fi
}

I would like to write more about this, since its very much key to readable shell scripts. But it’s just as easy as that.

Passing more than 9 arguments

Parameter of a shell function are accessed like $1, $2 .. $9. But the caller can pass more arguments. To extract them, you shift used-up parameters away. Then what used to be in $2 is now in $1 and so forth. $# holds the number of parameters still available to the function:

foo()
{
   while [ $# -ne 0 ]
   do
      printf "%s\n" "$1"
      shift
   done
}

foo 1 2 3 4 5 6 7 8 9 10
1
2
3
4
5
6
7
8
9
10

Combined with shift you can write a function to have more than nine named parameters:

foo()
{
   local a1="$1"; shift
   local a2="$1"; shift
   local a3="$1"; shift
   local a4="$1"; shift
   local a5="$1"; shift
   local a6="$1"; shift
   local a7="$1"; shift
   local a8="$1"; shift
   local a9="$1"; shift
   local a10="$1"; shift

   printf "%s\n" "${a1}"
   printf "%s\n" "${a2}"
   printf "%s\n" "${a3}"
   printf "%s\n" "${a4}"
   printf "%s\n" "${a5}"
   printf "%s\n" "${a6}"
   printf "%s\n" "${a7}"
   printf "%s\n" "${a8}"
   printf "%s\n" "${a9}"
   printf "%s\n" "${a10}"
}

Note

In the hated dash, you can not shift, if there are no parameters left. So you need to test for a parameters presence explicitly.

local x="$1"; [ $# -ne 0 ] && shift

Often its nicer and probably faster as well, to do a multiple shift instead.

   local a1="$1"
   local a2="$2"
   local a3="$3"
   local a4="$4"
   local a5="$5"
   local a6="$6"
   local a7="$7"
   local a8="$8"
   local a9="$9"
   shift 9
   local a10="$1"; shift
...

Forwarding parameters

When you write a wrapper function, you often only need to consider some of the parameters and can pass the rest of the parameters “as is”. Here "$@" comes in handy, which is a shortcut for “all remaining parameters”.

foo()
{
   local cc="$1" ; shift

   case "${cc}" in
      'gcc')
         gcc-10 "$@"
      ;;

      'clang')
         clang-11 "$@"
      ;;
   esac
}

Note

Do not confuse "$@" with $*, which means all remaining parameters as a string. The string would then be reparsed by the shell, which may lead to quoting misery.