5

Why doesn't this print all the passed arguments, in bash?

function abc() {
    echo "$1" #prints the correct argument

    for x in `seq 1 $#`; do
        echo "$x" #doesn't print the 1st, 2nd, etc arguments, but instead 1, 2, ..
    done
}

It is printing

1
2
3
4
...

instead.

6 Answers 6

9

I'll just add a couple more options to what everyone else has given. The closest to the way you're trying to write this is to use bash indirect expansion:

function abc() {
    for x in `seq 1 $#`; do
        echo "${!x}"    # the ! adds a level of indirection
    done
}

...another option if you want to operate on only some of the arguments, is to use array slicing with $@:

function def() {
    for arg in "${@:2:3}"; do  # arguments 2 through 4 (i.e. 3 args starting at number 2)
        echo "$arg"
    done
}

similarly, "${@:2}" will give you all arguments starting at number 2, "${@:$start:$((end-start+1))}" will give you arguments $start through $end (the $(( expression calculates how many arguments there are between $start and $end), etc...

Sign up to request clarification or add additional context in comments.

Comments

4

Actually there is a special short-hand for this case:

function abc() {
  for arg ; do
    echo "$arg"
  done
}

That is, if the in ... part is omitted, arg loops over the function's argument $@.

Incidentally if you have for arg ; ... outside of a function, it will iterate over the arguments given on the command line.

Comments

2

The seq command returns all numbers from start to stop. What you are calling here is seq 1 <number_of_arguments_to_abc>. For example, if you call abc alpha beta gamma, then the arguments would be seq 1 3, thus you get the numbers 1, 2 and 3.

If you want the arguments to abc instead, the expression is for x in "$@".

4 Comments

Yes. But assuming I'd want to use the current seq instruction, how would I have to do with echo? I say this because I might not want to go through all the elements. Sometime I will want to start on the 2nd, for example.
@devoured elysium: I'm sorry, I don't really understand your goal. Please give examples how you want to call abc, and what it should output in those cases.
arguments a b c and i only want b and c
function abc() { echo "$1"; shift; for x in "$@" ; do echo "$x" ; done ; } The magic is the shift command, which throws away the (currently) first argument.
2

If you want to print all arguments try this

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

1 Comment

I really recommend to add quotation marks around $@, as in "$@". If you don't you get unexpected results if you for example call abc "String with spaces".
1

The variable X holds the literal numbers. You're trying to do indirection - substitute $1 where there's a $x. Indirection warps the brain. $@ provides a simpler mechanism for looping over the arguments - without any adverse effects on your psyche.

for x in "$@"; do
  echo $x
done

See the bash man page for more details on $@.

1 Comment

Yes, but as explained to DarkDust, I need better granularity. I might just want to iterate over part of the arguments' elements, so I need to know if it possible doing it without $@
1

You should use the for arg form that others have shown. However, to address some things in your question and comments, see the following:

In Bash, it's not necessary to use seq. You can use C-style for loops:

for ((i = 2; i <= $#; i++))
do
    echo "${@:i:1}"
done

Which demonstrates array slicing which is another technique you can use in addition to direct iteration (for arg) or using shift.

An advantage of using either version of for is that the argument array is left intact, while shift modifies it. Also, with the C-style form with array slicing, you could skip any arguments you like. This is usually not done to the extent shown below, because it would rely on the arguments following a strict pattern.

for ((i = 2; i < $# - 2; i+=2))

That bit of craziness would start at the second argument, process every other one and stop before the last two or three (depending on whether $# is odd or even).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.