37

Inside a function, $1 ... $n are the parameters passed to that function. Outside a function $1 ... $n are the parameters passed to the script.

Can I somehow access the parameters passed to the script inside a function?

nbro
  • 15,395
  • 32
  • 113
  • 196
zedoo
  • 10,562
  • 12
  • 44
  • 55

6 Answers6

27

Usually you just pass them as parameters to the function at call time.

The (uglier) alternative is to put them in global variables.

Jean
  • 10,545
  • 2
  • 31
  • 31
  • To use the global variable approach, if your function is exported with export -f, you'll just need to export MY_VAR=$1 – AndrewHarvey Apr 04 '18 at 10:03
22

(I know this is an old post, but none of the answers actually answered the question.)

Use the BASH_ARGV array. It contains the arguments passed to the invoking script in reverse order (i.e., it's a stack with the top at index 0). You may have to turn on extended debugging in the shebang (e.g., #!/bin/bash -O extdebug) or with shopt (e.g., shopt -s extdebug), but it works for me in bash 4.2_p37 without it turned on.

From man bash:

An array variable containing all of the parameters in the current bash execution call stack. The final parameter of the last subroutine call is at the top of the stack; the first parameter of the initial call is at the bottom. When a subroutine is executed, the parameters supplied are pushed onto BASH_ARGV. The shell sets BASH_ARGV only when in extended debugging mode….

Here's a function I use to print all arguments in order on a single line:

# Print the arguments of the calling script, in order.
function get_script_args
{
    # Get the number of arguments passed to this script.
    # (The BASH_ARGV array does not include $0.)
    local n=${#BASH_ARGV[@]}

    if (( $n > 0 ))
    then
        # Get the last index of the args in BASH_ARGV.
        local n_index=$(( $n - 1 ))

        # Loop through the indexes from largest to smallest.
        for i in $(seq ${n_index} -1 0)
        do
            # Print a space if necessary.
            if (( $i < $n_index ))
            then
                echo -n ' '
            fi

            # Print the actual argument.
            echo -n "${BASH_ARGV[$i]}"
        done

        # Print a newline.
        echo
    fi
}
Dean Hall
  • 627
  • 6
  • 6
14

As Benoit stated, the simplest solution is to pass the commandline arguments to the function as function arguments with $@, then you can reference them in exactly the same way as outside the function. You'll actually be referencing the values passed to the function that just happen to have the same value as the commandline arguments, keep that in mind.

Note that this pretty much precludes you from passing any other arguments to the function, unless you know exactly how many arguments will be passed at the command line (unlikely as that is up to the user and isn't bound by your constraints)

i.e.

    function fname {
    # do something with $1 $2 $3...$n #
    }

    # $@ represents all the arguments passed at the command line #
    fname $@

A better way is to pass only the arguments you know you will be using, that way you can use them in the function AND also pass other parameters from within your code if you wish

i.e.

    function fname {
    # do something with $1 $count $2 and $3 #
    }

    count=1
    fname $1 $count $2 $3
CaffeineConnoisseur
  • 3,635
  • 4
  • 19
  • 16
  • 4
    You can simply pass fixed number of arguments to the function at the beginning of the argument list and then after processing them in the function use `shift` to skip them. **Function call:** `fname farg1 farg2 farg3 "$@"` **In the function after processing the three arguments:** `shift 3` – pabouk - Ukraine stay strong Aug 12 '13 at 12:18
  • 3
    Also you could call fname telling it how many user arguments are there using $# allowing you to not only specify extra arguments before, but also after like so: `fname beforearg $# "$@" afterarg` – Bodo Thiesen Feb 18 '14 at 04:47
10

You can store all of your script arguments in a global array:

args=("$@")

and then access them in a function:

f(){
    echo ${args[0]} ${args[1]}
}
dogbane
  • 266,786
  • 75
  • 396
  • 414
  • 2
    I think that `function f(){` is not valid. You should use `f() {` or `function f {` instead. – Benoit Oct 19 '10 at 07:28
  • 1
    I have corrected my answer, but `function f(){}` did work for me. – dogbane Oct 19 '10 at 07:41
  • The problem with that is that it isn't portable. But there is really no reason to prefer the `function` keyword even in cases where it happens to work. – tripleee Jul 27 '23 at 15:48
5

You should probably use "$@" and pass that at the end of your function's argument list. Inside the function, shift after parsing your arguments and use $1 to $n as normally.

Benoit
  • 76,634
  • 23
  • 210
  • 236
1

Thanks for the tips - they inspired me to write a callstack function. I used the 'column' command for esthetics.

callstack() {
    local j=0 k prog=$(basename $0)
    for ((i=1; ((i<${#BASH_ARGC[*]})); i++))
    do
        echo -n "${FUNCNAME[$i]/main/$prog} " # function name
        args=""
        for ((k=0; ((k<${BASH_ARGC[$i]})); k++))
        do
            args="${BASH_ARGV[$j]} $args" # arguments
            let j++
        done
        echo -e "$args\t|${BASH_LINENO[$i]}" $(sed -n ${BASH_LINENO[$i]}p "$0" 2>/dev/null) # line calling the function
    done | column -t -s $'\t' -o ' ' | sed 1d # delete callstack entry
}
compareTemplates brother_001270_1.jpg     |163  compareTemplates "$f" # process the rest
processPdf brother_001270.pdf             |233  filetype "${f%[*}" pdf && processPdf "$f"
process brother_001270.pdf                |371  --process) shift; process "$@"; exit ;; # process jpg or pdf
sm --quiet --process brother_001270.pdf   |0