tags:

views:

54

answers:

6

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.

+2  A: 

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 "$@".

DarkDust
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
@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.
DarkDust
arguments a b c and i only want b and c
devoured elysium
`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.
DarkDust
+2  A: 

If you want to print all arguments try this

function abc() {
   for arg in $@; do
      echo "$arg"
   done
}
jcubic
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"`.
DarkDust
A: 

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 $@.

Robert Wohlfarth
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 $@
devoured elysium
A: 

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

function abc() {
  for arg ; do
    echo "$arg"
  done
}
Chen Levy
+1  A: 

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).

Dennis Williamson
+2  A: 

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...

Gordon Davisson