bash functions - ghdrako/doc_snipets GitHub Wiki
- https://www.gnu.org/software/bash/manual/bash.html#Shell-Functions
- https://www.linuxjournal.com/content/return-values-bash-functions
- https://www.tldp.org/LDP/abs/html/functions.html
- http://mywiki.wooledge.org/BashGuide/Functions
As with a bash keyword or builtin, with function the shell doesn’t need to create a separate process to run the function. That makes a function **more efficient ** than calling a separate command binary or shell script.
Function definition must occur before it is called.
function_name () {
commands
}
function_name () { commands; }
function function_name {
commands
}
function function_name { commands; }
hello_world () {
echo 'hello, world'
}
hello_world
#!/bin/bash
open() {
case "$1" in
*.mp3|*.ogg|*.wav|*.flac|*.wma) xmms "$1";;
*.jpg|*.gif|*.png|*.bmp) display "$1";;
*.avi|*.mpg|*.mp4|*.wmv) mplayer "$1";;
esac
}
for file; do
open "$file"
done
Then, a for loop iterates over all of the script's positional parameters. (Remember, for file is equivalent to for file in "$@"
and both of them iterate over the full set of positional parameters.)
Variables Scope
Functions may also have local variables, declared with the local
or declare
keywords. This lets you do work without potentially overwriting important variables from the caller's namespace.
#!/bin/bash
count() {
local i
for ((i=1; i<=$1; i++)); do echo $i; done
echo 'Ah, ah, ah!'
}
for ((i=1; i<=3; i++)); do count $i; done
The local variable i inside the function is stored differently from the variable i in the outer script. This allows the two loops to operate without interfering with each other's counters.
var1='A'
var2='B'
my_function () {
local var1='C'
var2='D'
echo "Inside function: var1: $var1, var2: $var2"
}
echo "Before executing function: var1: $var1, var2: $var2"
my_function
echo "After executing function: var1: $var1, var2: $var2"
#Output:
#Before executing function: var1: A, var2: B
#Inside function: var1: C, var2: D
#After executing function: var1: A, var2: D
Return Values
Bash functions, unlike functions in most programming languages do not allow you to return a value to the caller. When a bash function ends its return value is its status: zero for success, non-zero for failure. To return values, you can set a global variable with the result, or use command substitution, or you can pass in the name of a variable to use as the result variable.
The return value from bash functions is really just an exit status, the value that you can check using $?
after calling the function.
It actually returns the exit status of the last command executed in the function.
There are two typical approaches to return value from function
-
function output be the return values. You might pipe that output into the next part of your script that needs the results from the function, or you might capture that output using
$()
notation. The $() will run the function in a subshell and you can assign the result (the function’s output) to a variable, and then use that variable on another command line. -
use global variables
function Summer {
#local i
local -i i # or declare -i i to declare i as an integer value, avoiding conversions to/from strin
SUM=0 # SUM is global because that is how the result of the function call is being returned to the caller
for((i=0; i<$1; i++)) {
let SUM+=i;
}
}
Return value as global variable
my_function () {
echo "some result"
return 55
}
my_function
echo $?
#Output:
#some result
#55
my_function () {
func_result="some result"
}
my_function
echo $func_result
#Output:
#some result
#### use local variables in your functions
* with use command substitution
my_function () {
local func_result="some result"
echo "$func_result"
}
func_result="$(my_function)"
echo func_result
#Output:
#some result
function that accepts a variable name as part of its command line and then set that variable to the result of the function
function myfunc()
{
local __resultvar=$1
local myresult='some value'
eval $__resultvar="'$myresult'"
}
myfunc result
echo $result
Since we have the name of the variable to set stored in a variable, we can't set the variable directly, we have to use eval to actually do the setting. The eval statement basically tells bash to interpret the line twice, the first interpretation above results in the string result='some value' which is then interpreted once more and ends up setting the caller's variable.
When you store the name of the variable passed on the command line, make sure you store it in a local variable with a name that won't be (unlikely to be) used by the caller (which is why I used __resultvar rather than just resultvar).
Returning Text Values
While usually, functions communicate using a numerical return code (return [number]), they can also send back text:
function get_system_info() {
os_name=$(uname -s)
kernel_version=$(uname -r)
output="Operating System: $os_name Kernel: $kernel_version"
echo "$output" # Return the text output
}
info=$(get_system_info) # Capture the entire output in a variable
echo "$info"
If you don't, and the caller happens to choose the same name for their result variable as you use for storing the name, the result variable will not get set. For example, the following does not work:
# doesn't work !!! poniewaz zmienna w funkcji ma taka sama nazwe jak nazwa zmiennej w jej wywolaniu
function myfunc()
{
local result=$1
local myresult='some value'
eval $result="'$myresult'"
}
myfunc result
echo $result
The reason it doesn't work is because when eval does the second interpretation and evaluates result='some value', result is now a local variable in the function, and so it gets set rather than setting the caller's result variable.
functions that combine both result variables and command substitution:
function myfunc()
{
local __resultvar=$1
local myresult='some value'
if [ "$__resultvar" ](/ghdrako/doc_snipets/wiki/-"$__resultvar"-); then
eval $__resultvar="'$myresult'"
else
echo "$myresult"
fi
}
myfunc result
echo $result
result2=$(myfunc)
echo $result2
Here, if no variable name is passed to the function, the value is output to the standard output
Dynamic Scoping
If you declare a local variable in a function and then that function calls another function, the second function will see the first function’s local variable and not the global variable of the same name. If you call that second function from the main part of your script,it will see (and use) the global variable.
Passing Arguments to Bash Functions
- The passed parameters are $1, $2, $3 … $n, corresponding to the position of the parameter after the function’s name.
- The $0 variable is reserved for the function’s name.
- The $# variable holds the number of positional parameters/arguments passed to the function.
- The $* or $@ variable holds all positional parameters/arguments passed to the function.
#!/bin/bash
greeting () {
echo "Hello $1"
}
greeting "Joe"
Output
Hello Joe
Arguments as array
function print_items() {
local items_array=("$@") # Access all passed arguments as an array
for item in "${items_array[@]}"; do
echo $item
done
}
my_items=("apple" "banana" "orange")
print_items "${my_items[@]}"
Note: It’s essential to pass arrays with "${array_name[@]}"
syntax
(including quotes and [@]) to preserve any elements containing spaces.
Show all arguments
function Tell_All { echo "You called $FUNCNAME" echo "The $# args supplied are:" for arg in "$@"; do echo "$arg" done }
Validate argument in function
function create_backup() { if [ -z "$1" ]; then # Missing filename? echo "Error: please provide a filename." return 1 fi
...rest of your backup creation logic
}
### Array FUNCNAME
. There is an array variable named ``FUNCNAME`` that holds the call stack of the functions that have been invoked. Element
0 is always the **current function**, so just use ``$FUNCNAME`` (since using the** array name**
**without an index always returns the first element**, i.e., the one at index zero). That’s useful in a debugging prompt
function f1 { echo "You called $FUNCNAME" }
### Functions
Definition:
function foo () { echo "Arguments work just like script arguments: $@" echo "And: $1 $2..." echo "This is a function" return 0 }
foo
with two arguments, arg1 and arg2:
Call the function foo arg1 arg2
=> Arguments work just like script arguments: arg1 arg2
=> And: arg1 arg2...
=> This is a function
or simply
bar () { echo "Another way to declare functions!" return 0 }
bar
with no arguments:
Call the function bar # => Another way to declare functions!
Calling your function
foo "My name is" $Name
### Passing function as argument
function x() { echo "Hello world"; } function around() { echo before; $1; echo after; }
around x
function x() { echo "x(): Passed $1 and $2"; } function around() { echo before; "$@"; echo after; } around x 1st 2nd
-- print before x(): Passed 1st and 2nd after
function x() { echo "Hello world" }
function around() { echo "before" var=$($1) echo "after $var" }
around x
### Recursive function
* Recursive functions are slow under bash.
* Avoid using recursive functions if possible
factorial() { if [ $1 -eq 0 ]; then echo 1 else local subfactorial=$(factorial $(( $1 - 1 ))) echo $(( $1 subfactorial )) fi } result=$(factorial 5) echo "Factorial of 5 is $result"
#!/bin/bash
function f() { local n=$1 if $n -eq 0 ; then echo 1 else echo $((n*$(f $n-1))) fi }
for i in {1..10}; do echo "$i!=$(f $i)" done
#!/bin/bash
fact.sh - Shell script to to find factorial of given command line arg
factorial(){ local i=$1 local f declare -i i declare -i f
factorial() is called until the value of $f is returned and is it is <= 2
This is called the recursion
[ $i -le 2 ] && echo $i || { f=$(( i - 1)); f=$(factorial $f); f=$(( f * i )); echo $f; } }
display usage
[ $# -eq 0 ] && { echo "Usage: $0 number"; exit 1; }
call factorial
factorial $1
### Redirection function definition
You can put an I/O redirection on your function definition. It takes effect when you
call that function. Here’s a common use of that feature to easily redirect the entire
usage message to STDERR (via ``1>&2``):
function Usage_Message { echo "usage: $0 value pathname" echo "where value must be positive" echo "and pathname must be an existing file" echo "for example: $0 25 /tmp/scratch.csv" } 1>&2
This function might be called when the script checks and finds that the correct
number of arguments have not been supplied or that the supplied filename doesn’t
exist