How would you make a function that gets arguments?

function arg_example {
    arg "c"  # This is imaginary but it would return 'true' if it found the argument -c.
    did_find_arg=$?  # Get the previous function (arg)'s output.

    if [$did_find_arg == 'true']; then
        echo "Yes!"
        echo "Not found."

# Testing
arg_example  # "Not found."
arg_example -c  # "Yes!"
arg_example -k  # "Not found."

Also, how would you find the value of a key-value function, eg --name:

function hello {
    echo "Hello, $--name!"

EDIT: I know how to use $1 and $2, but I'd like to know how to get optional things like -v or --version.

  • 1
    That's where you want to use getopts (remember to reset OPTIND and/or make is local for shells that support local scope) – Stéphane Chazelas Jan 26 '15 at 14:10
  • 1
    Take a look at getopts – PM 2Ring Jan 26 '15 at 14:11
  • I'll take a look. – Aqua the SeaWing Jan 26 '15 at 14:16
  • 1
    Don't forget that you need to have spaces around [ and ] in the if statements. – Barmar Jan 26 '15 at 22:15

Processing the command line of a function may be done with getopts in exactly the same way as processing the command line for a script. The only thing to watch out for is that OPTIND will have to be reset to 1 or made local to the function:


myfunc () {
    local OPTIND
    local do_c=0
    local do_k=0
    local opt

    while getopts 'ck' opt; do
        case $opt in
            c) do_c=1 ;;
            k) do_k=1 ;;
            *) echo 'Error in command line parsing' >&2
               return 1
    shift "$(( OPTIND - 1 ))"

    if [ "$do_c" -eq 1 ]; then
        echo 'Yes!'
        echo 'Not found'

myfunc -c
myfunc -k
myfunc -a

The above script produces:

Not found
Not found
script.sh: illegal option -- a
Error in command line parsing



Very simple named argument extractor for Bash:


  local out=""
  for argname in "$@"; do
    out="${out}local $argname=\$1; shift;"
  printf "%s" "$out"

  eval $(getargs a b c)
  printf "a = %s, b = %s, c = %s\n" "$a" "$b" "$c"

testfun "$@"                                                                                                  

Since we want the arguments to be local variables in the dynamic scope of the function testfun, getargs cannot be a function call. Rather, getargs is a tiny compiler which translates an argument specification like a b c into shell syntax, generating the code which we would otherwise manually add in order to get the positional argument into local variables.

For instance, the output of

getargs a b c

is the source code:

local a=$1; shift;local b=$1; shift;local c=$1; shift

when we eval that, it does what it looks like it does. In the context of the caller, it gets the first three positional arguments into a, b and c, shifting them out of the argument list.

Now we could take this to the next level and add "bells and whistles".

For starters, the generated code could check that there actually are exactly three arguments and diagnose.

We could support optional arguments: getargs a b : c (or whatever) could mean that a and b are required, but c is optional. We could also support trailing arguments: getargs a b : c . d could generate code which gets the first two arguments (which are required) into locals a and b. Then if a third argument is present, it goes into c. Any arguments after that go into d, which is a Bash array.

I will leave that as an exercise.

Too bad that the shell doesn't have a macro-expansion time. The big downside of this is that the code is generated each time the function is called, even though it is completely static.

You could write some sort of preprocessor (e.g. in Awk) to transform some sort of named function syntactic sugar into regular shell code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.