Bash Functions: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
Line 129: Line 129:
   501  <b><font color=red>4058</font></b>  3941  0  9:07PM ttys004    0:00.01 /bin/bash ./main.sh
   501  <b><font color=red>4058</font></b>  3941  0  9:07PM ttys004    0:00.01 /bin/bash ./main.sh
   501  4059  <b><font color=red>4058</font></b>  0  9:07PM ttys004    0:00.01 /bin/bash ./main.sh
   501  4059  <b><font color=red>4058</font></b>  0  9:07PM ttys004    0:00.01 /bin/bash ./main.sh
From this moment, those shells are executed independently, until both of them exit.
If the sub-shell is still executing while the parent shell exits or it is killed, it will keep going, while [[Linux Process Management Concepts#The_init_Process|the init process]] (process ID 1) becomes its parent. The controller terminal of the parent will keep getting sub-shell's stdout and stderr. If the controlling terminal is stopped, the sub-shell will still keep going, but in this case the output will be discarded.





Revision as of 01:27, 16 July 2017

Internal

Defintion

Syntax

[function] function-name() {
    ...
}

The "function" keyword is optional.

If the function declaration is specified on a single line, then the final command of the function must be followed by a semicolon:

function one-liner() { echo "something"; echo "something else"; }

The function definition must precede the first call to it. Function bodies may not be empty.

Arguments

The function does not declare its arguments in the signature. They are available in the function's body as $1, $2, etc.

Important: For more than 9 arguments, always refer to arguments using ${}: ${10} not $10. If you use $10, you'll actually get the first argument ($1) with a "0" appended to it.

The number of arguments, including the empty strings specified as "" when the function was invoked, may be obtained with $#.

The shift keyword "shifts" to the left the argument list, discarding $1 and assigning to $1 the value that was previously assigned to $2, to $2 the value that was previously assigned to $3, and so on. shift's exit code is 0 if there were still arguments to shift, 1 otherwise.

The correct way to iterate through a function's arguments, including the empty strings, is:

function my-function() {
    while [ $# -gt 0 ]; do
        [ "$1" = "" ] && echo "got empty string" || echo "got $1"
        shift
    done
}

Exit Status

A bash function does not return a value, it only allows to set an exit status, which is a numerical value. 0 indicates success and a non-zero value indicates failure. The exit status is declared with the "return" keyword:

 function f() {
    ...
    return 0
 }

If there is no explicit "return" keyword, the function's exit status is the exit status of last executed statement.

The function's caller can retrieve the exist status with $?.

Returning Values

As mentioned above, functions do not return values. However, we may send content to stdout or stderr from the body of the function, and that content can be captured by the caller as follows:

 function callee() {
    
     echo "we send this to stdout"
     echo "we send this to stderr" 1>&2
 }
 
 function caller() {
     
     local content
     content=$(callee)
     echo "${content}"
 }

Command substitution demonstrated above will capture the content that is being sent to stdout into the local variable "content". The content sent to stderr will be sent to the stderr of the executing shell.

Function Info

The FUNCNAME Array

A function has a "FUNCNAME" built-in array, which is an array variable containing the names of all shell functions currently in the execution call stack. The element with index 0 is the name of any currently-executing shell function. The bottom-most element is "main". This variable exists only when a shell function is executing. Assignments to FUNCNAME have no effect and return an error status. If FUNCNAME is unset, it loses its special properties, even if it is subsequently reset.

The Name of the Function

The name of the function may be obtained inside the function with:

${FUNCNAME[0]}

Recommended Style

# Function documentation 
# Returns to stdout: ...
# Returns to stderr: ...
function some-function() {

    local arg1=$1  # arg1 documentation
    local arg2=$2  # arg1 documentation
    ...
}

Executing a Function in Background

Functions can be executed in background with & as follows:

 function callee() {
    ...
 }

 function caller() {

     callee &

 }

When a function is invoked in background within the context of a shell, a completely new sub-shell is started and begins to execute the code of function. Both processes share the same name in the process table, but the shell that invoked the function in background is the parent of the newly created sub-shell:

 501  4058  3941   0  9:07PM ttys004    0:00.01 /bin/bash ./main.sh
 501  4059  4058   0  9:07PM ttys004    0:00.01 /bin/bash ./main.sh

From this moment, those shells are executed independently, until both of them exit.

If the sub-shell is still executing while the parent shell exits or it is killed, it will keep going, while the init process (process ID 1) becomes its parent. The controller terminal of the parent will keep getting sub-shell's stdout and stderr. If the controlling terminal is stopped, the sub-shell will still keep going, but in this case the output will be discarded.


The sub-shell's stdout and stderr are directed to the parent shell's stdout and stderr.

When the function exits:


Bash Process in Background