Passing parameters to a Bash function
Solution 1
There are two typical ways of declaring a function. I prefer the second approach.
function function_name {
command...
}
or
function_name () {
command...
}
To call a function with arguments:
function_name "$arg1" "$arg2"
The function refers to passed arguments by their position (not by name), that is $1
, $2
, and so forth. $0
is the name of the script itself.
Example:
function_name () {
echo "Parameter #1 is $1"
}
Also, you need to call your function after it is declared.
#!/usr/bin/env sh
foo 1 # this will fail because foo has not been declared yet.
foo() {
echo "Parameter #1 is $1"
}
foo 2 # this will work.
Output:
./myScript.sh: line 2: foo: command not found
Parameter #1 is 2
Reference: Advanced Bash-Scripting Guide.
Solution 2
Knowledge of high level programming languages (C/C++, Java, PHP, Python, Perl, etc.) would suggest to the layman that Bourne Again Shell (Bash) functions should work like they do in those other languages.
Instead, Bash functions work like shell commands and expect arguments to be passed to them in the same way one might pass an option to a shell command (e.g. ls -l
). In effect, function arguments in Bash are treated as positional parameters ($1, $2..$9, ${10}, ${11}
, and so on). This is no surprise considering how getopts
works. Do not use parentheses to call a function in Bash.
(Note: I happen to be working on OpenSolaris at the moment.)
# Bash style declaration for all you PHP/JavaScript junkies. :-)
# $1 is the directory to archive
# $2 is the name of the tar and zipped file when all is done.
function backupWebRoot ()
{
tar -cvf - "$1" | zip -n .jpg:.gif:.png "$2" - 2>> $errorlog &&
echo -e "\nTarball created!\n"
}
# sh style declaration for the purist in you. ;-)
# $1 is the directory to archive
# $2 is the name of the tar and zipped file when all is done.
backupWebRoot ()
{
tar -cvf - "$1" | zip -n .jpg:.gif:.png "$2" - 2>> $errorlog &&
echo -e "\nTarball created!\n"
}
# In the actual shell script
# $0 $1 $2
backupWebRoot ~/public/www/ webSite.tar.zip
Want to use names for variables? Just do something this.
local filename=$1 # The keyword declare can be used, but local is semantically more specific.
Be careful, though. If an argument to a function has a space in it, you may want to do this instead! Otherwise, $1
might not be what you think it is.
local filename="$1" # Just to be on the safe side. Although, if $1 was an integer, then what? Is that even possible? Humm.
Want to pass an array to a function by value?
callingSomeFunction "${someArray[@]}" # Expands to all array elements.
Inside the function, handle the arguments like this.
function callingSomeFunction ()
{
for value in "$@" # You want to use "$@" here, not "$*" !!!!!
do
:
done
}
Need to pass a value and an array, but still use "$@" inside the function?
function linearSearch ()
{
local myVar="$1"
shift 1 # Removes $1 from the parameter list
for value in "$@" # Represents the remaining parameters.
do
if [[ $value == $myVar ]]
then
echo -e "Found it!\t... after a while."
return 0
fi
done
return 1
}
linearSearch $someStringValue "${someArray[@]}"
In Bash 4.3 and above, you can pass an array to a function by reference by defining the parameter of a function with the -n
option.
function callingSomeFunction ()
{
local -n someArray=$1 # also ${1:?} to make the parameter mandatory.
for value in "${someArray[@]}" # Nice!
do
:
done
}
callingSomeFunction myArray # No $ in front of the argument. You pass by name, not expansion / value.
Solution 3
If you prefer named parameters, it's possible (with a few tricks) to actually pass named parameters to functions (also makes it possible to pass arrays and references).
The method I developed allows you to define named parameters passed to a function like this:
function example { args : string firstName , string lastName , integer age } {
echo "My name is ${firstName} ${lastName} and I am ${age} years old."
}
You can also annotate arguments as @required or @readonly, create ...rest arguments, create arrays from sequential arguments (using e.g. string[4]
) and optionally list the arguments in multiple lines:
function example {
args
: @required string firstName
: string lastName
: integer age
: string[] ...favoriteHobbies
echo "My name is ${firstName} ${lastName} and I am ${age} years old."
echo "My favorite hobbies include: ${favoriteHobbies[*]}"
}
In other words, not only you can call your parameters by their names (which makes up for a more readable core), you can actually pass arrays (and references to variables - this feature works only in Bash 4.3 though)! Plus, the mapped variables are all in the local scope, just as $1
(and others).
The code that makes this work is pretty light and works both in Bash 3 and Bash 4 (these are the only versions I've tested it with). If you're interested in more tricks like this that make developing with bash much nicer and easier, you can take a look at my Bash Infinity Framework, the code below is available as one of its functionalities.
shopt -s expand_aliases
function assignTrap {
local evalString
local -i paramIndex=${__paramIndex-0}
local initialCommand="${1-}"
if [[ "$initialCommand" != ":" ]]
then
echo "trap - DEBUG; eval \"${__previousTrap}\"; unset __previousTrap; unset __paramIndex;"
return
fi
while [[ "${1-}" == "," || "${1-}" == "${initialCommand}" ]] || [[ "${#@}" -gt 0 && "$paramIndex" -eq 0 ]]
do
shift # First colon ":" or next parameter's comma ","
paramIndex+=1
local -a decorators=()
while [[ "${1-}" == "@"* ]]
do
decorators+=( "$1" )
shift
done
local declaration=
local wrapLeft='"'
local wrapRight='"'
local nextType="$1"
local length=1
case ${nextType} in
string | boolean) declaration="local " ;;
integer) declaration="local -i" ;;
reference) declaration="local -n" ;;
arrayDeclaration) declaration="local -a"; wrapLeft= ; wrapRight= ;;
assocDeclaration) declaration="local -A"; wrapLeft= ; wrapRight= ;;
"string["*"]") declaration="local -a"; length="${nextType//[a-z\[\]]}" ;;
"integer["*"]") declaration="local -ai"; length="${nextType//[a-z\[\]]}" ;;
esac
if [[ "${declaration}" != "" ]]
then
shift
local nextName="$1"
for decorator in "${decorators[@]}"
do
case ${decorator} in
@readonly) declaration+="r" ;;
@required) evalString+="[[ ! -z \$${paramIndex} ]] || echo \"Parameter '$nextName' ($nextType) is marked as required by '${FUNCNAME[1]}' function.\"; " >&2 ;;
@global) declaration+="g" ;;
esac
done
local paramRange="$paramIndex"
if [[ -z "$length" ]]
then
# ...rest
paramRange="{@:$paramIndex}"
# trim leading ...
nextName="${nextName//\./}"
if [[ "${#@}" -gt 1 ]]
then
echo "Unexpected arguments after a rest array ($nextName) in '${FUNCNAME[1]}' function." >&2
fi
elif [[ "$length" -gt 1 ]]
then
paramRange="{@:$paramIndex:$length}"
paramIndex+=$((length - 1))
fi
evalString+="${declaration} ${nextName}=${wrapLeft}\$${paramRange}${wrapRight}; "
# Continue to the next parameter:
shift
fi
done
echo "${evalString} local -i __paramIndex=${paramIndex};"
}
alias args='local __previousTrap=$(trap -p DEBUG); trap "eval \"\$(assignTrap \$BASH_COMMAND)\";" DEBUG;'
Solution 4
Drop the parentheses and commas:
myBackupFunction ".." "..." "xx"
And the function should look like this:
function myBackupFunction() {
# Here $1 is the first parameter, $2 the second, etc.
}
Solution 5
A simple example that will clear both during executing script or inside script while calling a function.
#!/bin/bash
echo "parameterized function example"
function print_param_value(){
value1="${1}" # $1 represent first argument
value2="${2}" # $2 represent second argument
echo "param 1 is ${value1}" # As string
echo "param 2 is ${value2}"
sum=$(($value1+$value2)) # Process them as number
echo "The sum of two value is ${sum}"
}
print_param_value "6" "4" # Space-separated value
# You can also pass parameters during executing the script
print_param_value "$1" "$2" # Parameter $1 and $2 during execution
# Suppose our script name is "param_example".
# Call it like this:
#
# ./param_example 5 5
#
# Now the parameters will be $1=5 and $2=5
stivlo
I'm a software developer, living in London. I'm interested in programming and system administration, in particular in the following areas: Java, JSP, JSTL, Ant, Maven TDD, JUnit, Mockito, Hibernate, JPA Spring Framework, Apache Lucene, iText PDF Amazon AWS: EC2, SimpleDB, SES, S3 Javascript, ExtJS, jQuery, Perl, PHP Linux, Postfix, Apache, Tomcat Currently learning Natural Language Processing with Gate My blog about programming & sysadm & travel: http://www.stefanolocati.it/ My github repos: https://github.com/stivlo Why I like stackoverflow: when I was in the University's Unix lab, I could ask questions to the gurus passing their days there and have wonderful inputs to solve practical problems and to improve my skills; being in stackoverflow, is a bit like hanging in the lab again. No, wait, actually it's much better, because of the wide skillset and quantity of geeks.
Updated on November 26, 2021Comments
-
stivlo over 2 years
I am trying to search how to pass parameters in a Bash function, but what comes up is always how to pass parameter from the command line.
I would like to pass parameters within my script. I tried:
myBackupFunction("..", "...", "xx") function myBackupFunction($directory, $options, $rootPassword) { ... }
But the syntax is not correct. How can I pass a parameter to my function?
-
Wil over 6 years"...but what comes up is always how to pass parameter from the command line" - Yes! That's because Bash scripts are basically sequences of command lines - invoke a function in a Bash script exactly as if it was a command on the command line! :-) Your call would be myBackupFunction ".." "..." "xx"; no parenthesis, no commas.
-
MSalters almost 6 yearsThe counterpart to this question: return value from a bash function
-
Gabriel Staples over 2 yearsSee also: Passing arrays as parameters in bash
-
-
CMCDragonkai over 10 yearsWhen I write
function name(){}
I get an error about the '('. But when I writename(){}
it works. Any ideas? -
Leandro over 10 yearsYou have forgotten the spaces, try
function name() {}
. Maybe with a 'enter' before{}
-
Terry Gardner over 10 yearsGood answer. My 2 cents: in shell constructs that reside in a file that is sourced (dotted) when needed, I prefer to use the
function
keyword and the()
. My goal (in a file, not command line) is to increase clarity, not reduce the number of characters typed, viz,function myBackupFunction() compound-statement
. -
Charles Duffy about 9 years@CMCDragonkai, the
function
keyword version is an extension; the other form works in all POSIX-compliant shells. -
Charles Duffy about 9 years@TerryGardner, consider that your attempts to increase clarity are reducing compatibility.
-
Ron Burk about 8 yearsOf course, if you REALLY restrict yourself, you can make your script compatible with MS-DOS COMMAND.EXE. Perhaps compatibility is only situationally relevant.
-
GypsyCosmonaut almost 7 yearsWhat are the
@var
,@reference
,@params
variables ? What should I look up on internet to learn more about this? -
Wil over 6 yearsPassing by name in that manner only works for integers passed into the numeric operator (( )), and it only works because the numeric operator recursively resolves strings to values. If you'd like to test what I mean, try entering '5' for x and then 'x' for y and you'll see that it adds (x + y ) = ( 5 + x ) = ( 5 + 5 ) = 10. For all other use cases your example will fail. Instead you should use 'add "$x" "$y"' for generic code.
-
Charles Duffy over 6 years@RonBurk, perhaps -- but even if we consider only clarity, the
function
keyword had guarantees in the old ksh-family shells that introduced it that modern bash don't honor (in such shells,function
made variables local-by-default; in bash, it does not). As such, its use decreases clarity to anyone who knows, and might expect, the ksh behavior. See wiki.bash-hackers.org/scripting/obsolete -
David A. French almost 6 yearsHi @niieani when I try to create a bash function in the form you use in your answer it tells me I need to install ucommon utils from apt. Is this how your bash script works? Am I doing this correctly? If I understand you or someone else basically built the ucommon util program to allow for an extension of Bash, correct?
-
niieani almost 6 years@DavidA.French no, this shouldn't happen. There is no relation between
ucommon
and my code. It's possible you have some tool installed which causes the issue you mentioned, no idea what could it be. -
Charles Duffy almost 6 years@dogbane, would you be willing to consider changing the reference link to a different source -- be that the official manual, the bash-hackers' wiki, or the Wooledge wiki -- or to accept an edit which does so? The ABS has quite a history of teaching people bad practices through examples that aren't carefully chosen to showcase good behavior.
-
Jacktose over 5 years“pipe in”. I see what you did there!
-
almaceleste over 4 yearsalso, I want to add, that you could use
$#
to get the count of the arguments, passed to the function. -
Anthony Rutledge over 4 yearsFar too involved, given the question.Things like
local filename=$1
work well enough for most. More over, in bash, one has the option to use declare-A
to create associative arrays. You can already pass arrays as a list!callingSomeFunction "${someArray[@]}"
-
Jaraws over 3 yearsWhat if first argument is a string array?
-
iomv over 2 yearsThe last example posted does not work as far as I can tell. I tried to run it on bash v5+ and it is just returning me the full array in the loop as opposed to each item
-
iomv over 2 yearsafter testing again, I found that it was my error as I was declaring the array in line instead of declaring it before
-
Anthony Rutledge over 2 years@iomv Nonetheless, do be careful of the "circular variable reference" problem. Whatever name you declare the array as inside of the function, DO NOT name your array argument in the calling context / client code the same name. Notice how I changed the last example to help people avoid the "circular name reference" problem. Good call, even though you made an error on your own. :-)
-
Aaron Franke about 2 yearsHow do I make the function be recognized by
xargs
? It seems that the function works when I call it myself likemyfunc hi
but if I use| xargs myfunc
it doesn't work.