Bash shell functions

Functions provide the ability to define and invoke a complex series of
commands using a keyword.


functions
  • Runs in current shell.
  • When invoked, can take arguments from command line. using positional parameters, $0, $1, $*, etc.
  • Can contain complex conditional statements
  • Can use iteration - loops
  • Can invoke other functions as long as they are already declared.
  • Can invoke itself, so be careful.
  • Exportable using export -f
  • Or put definition in .bashrc if you want it always available.
  • Can be redefined at any time.
  • Can be marked readonly - can't be redefined.
  • Can have same name as alias. Alias invoked first. But if function invoked in alias, it will run.
  • Avoid using real command names, unless you plan on using full path for actual command when needed.
  • Use unset fun-name to delete function definition in current shell/script.
  • A function is part of current shell/script.
  • Variables defined in calling shell/script are available in function.
  • Variables defined in function are available to calling shell/script.
  • Changes to variables in function are seen in shell/script.
  • Keyword local will hide variable changes from shell/script. local var="new value"
  • Do NOT use path to invoke function, internal command.
    Defining a function function fun-name { action; action action } function fun-name { action; action action } fun-name () { action; action action } function fun-name () { action; action action } * trailing white-space may cause sytax problems.
  • function keyword to define a function unless () used.
  • fun-name - any name, alpha, numeric, and _ First character cannot be number. Avoid actual command names.
  • () - parentheses required after name if function keyword not specified. Must be on same line as name.
  • {} - braces encapsulate the command sequence definitions. Open brace may appear right after () or on next line. Closed brace completes definition. On its own line or preceded by a delimiter. When defining on command line, the PS2 prompt appears when recording function body until closing brace.
    Invoking function fun-name argument list
  • invokes the define function
  • passing in arguments from the command line as positional parameters.
  • positional parameters only available in current invocation.
    function rsorter ()
    {
      #rsorter takes 1 or 2 arguments.
      # $1 is file to be sorted
      # $2 is file to store sorted file in
    
      # if insufficient arguments 
      if [ $# -lt 1 ] 
      then
        echo "arg count $# insufficient"; 
        return 1; 
      fi
    
      #test that 2nd argument not null
      if [ -n "$2" ]
      then
        # If $2 exists use it for output
        sort -r $1 -o $2
      else
        # else use original filename for output
        sort -r $1 -o $1
      fi
    }
    
    rsorter file1
    
    rsorter file2 file3
    

    Variables Variables defined in shell/script are available to function. Unless marked local in function, changes to variable in function are visible to calling shell/script. Positional variables $0, $1, etc. are local to function. Arguments passed in from command line and assigned when function invoked.
    return and exit Use return followed by an unsigned value between 0-255 to set the status code upon termination of the function. Use exit followed by an unsigned value between 0-255 to set the status code if you want to terminate the program, not just the the function.
    set set can be used to initialize positional parameters dynamically. set one "and a" two echo $# 3 echo $1 and a
    Final advice
  • In general, don't define function inside function. This can be done but normal command line expansion has already occurred, so what can be done is limited. And the inner function will be defined every time the outter function is called.
  • Avoid recurssion unless very careful about coding a break out.
  • Avoid use names of existing commands. If you do so, you will have to specify full path to run 'real' command.
    function lsset ()
    {
      if [ $1 == "l" ]
      then
        function ls ()
        {
          /bin/ls -l
        }
      else
        function ls ()
        {
          /bin/ls
        }
      fi
    } 
    
    
    Note that the full path to ls had to be stated in definition to avoid recursion. If you want to try this, enter the lsset function definition. Run lsset with l as an argument, then run ls Run lsset again without the l argument, then run ls Or copy and paste the commands above into the terminal windows. Output should look something like :
    > lsset l
    > ls
    -rw-r--r--  1 berezin berezin      4665 Dec 31  2013 win8.1.txt
    drwx------  3 berezin berezin      4096 Jul 28 10:39 work
    
    > lsset
    > ls
    win8.1.txt
    work
    
  • Don't define function inside loop. This is just a waste of CPU resources.
  • Use care if redefining a function. Program flow of control may affect which function definition is actually in use.
  • Use returns to return back to calling script/shell.
  • Use exit to exit function and calling script/shell.
  • Remember the command line arguments are those passed to the function, not those passed to the calling script.
  • However, you can pass the shell script's command line arguments to the function :
    #!/bin/bash
    #  fun.sh
    
    function fun2 ()
    {
      for var in "$@"
      do
        echo "$var"
      done
    }
    
    fun2 "$@"
    
    ./fun.sh one "and two" one and two