6

Problem description

In a shell script, I want to iterate over all command line arguments ("$@") from inside a function. However, inside a function, $@ refers to the function arguments, not the command line arguments. I tried passing the arguments to the function using a variable, but that doesn't help, since it breaks arguments with whitespaces.

How can I pass $@ to a function in such a way that it does not break whitespace? I am sorry if this has been asked before, I tried searching for this question and there are a lot similar ones, but I didn't find an answer nevertheless.

Illustration

I made a shell script to illustrate the problem.

print_args.sh source listing

#!/bin/sh
echo 'Main scope'
for arg in "$@"
do
    echo "    $arg"
done

function print_args1() {
    echo 'print_args1()'
    for arg in "$@"
    do
        echo "    $arg"
    done
}

function print_args2() {
    echo 'print_args2()'
    for arg in $ARGS
    do
        echo "    $arg"
    done
}

function print_args3() {
    echo 'print_args3()'
    for arg in "$ARGS"
    do
        echo "    $arg"
    done
}

ARGS="$@"

print_args1
print_args2
print_args3

print_args.sh execution

$ ./print_args.sh foo bar 'foo bar'
Main scope
    foo
    bar
    foo bar
print_args1()
print_args2()
    foo
    bar
    foo
    bar
print_args3()
    foo bar foo bar

As you can see, I can't get the last foo bar to appear as a single argument. I want a function that gives the same output as the main scope.

Community
  • 1
  • 1
jornane
  • 1,397
  • 10
  • 27
  • Quote your variables. That's the answer. Use `func "$@"` and not `func $@` to pass the arguments correctly. You knew you needed to do that in the function itself and in the main body. You, apparently, just forgot to do that when passing them to the function. – Etan Reisner Jul 20 '15 at 14:51
  • 1
    http://ideone.com/QTsGiK – Etan Reisner Jul 20 '15 at 14:56
  • Whoops, sorry for removing my comment, sausage fingers. That does work, and the solution seems pretty portable. In my specific case it would be easier to use script-global variables, but passing as a variable is certainly something I could work with. – jornane Jul 20 '15 at 15:00
  • 1
    Passing `"$@"` as an argument to a function is entirely portable (as far as I know). Arrays are not. Using a string is **not** a solution here. It does not work at all. See [Bash FAQ 050](http://mywiki.wooledge.org/BashFAQ/050) for a similar conversation about full command lines in strings. – Etan Reisner Jul 20 '15 at 15:03
  • plus1 for how the question is structured. – Eugeniu Rosca Jul 23 '15 at 07:50

1 Answers1

6

You can use this BASH function:

#!/bin/bash

echo 'Main scope'
for arg in "$@"
do
    echo "    $arg"
done

function print_args1() {
    echo 'print_args1()'
    for arg in "$@"; do
        echo "    $arg"
    done
}

function print_args3() {
    echo 'print_args3()'
    for arg in "${ARGS[@]}"; do
        echo "    $arg"
    done
}

ARGS=( "$@" )

print_args1 "$@"
print_args3

You can see use of bash shebang at top:

#!/bin/bash

required be able to use BASH arrays.

Output:

bash ./print_args.sh foo bar 'foo bar'
Main scope
    foo
    bar
    foo bar
print_args1()
    foo
    bar
    foo bar
print_args3()
    foo
    bar
    foo bar
anubhava
  • 761,203
  • 64
  • 569
  • 643