Sunday, May 1, 2011

How can I pass a complete argument list in bash while keeping mulitword arguments together?

I am having some issues with word-splitting in bash variable expansion. I want to be able to store an argument list in a variable and run it, but any quoted multiword arguments aren't evaluating how I expected them to.

I'll explain my problem with an example. Lets say I had a function decho that printed each positional parameter on it's own line:

#!/bin/bash -u
while [ $# -gt 0 ]; do
  echo $1
  shift
done

Ok, if I go decho a b "c d" I get:

[~]$ decho a b "c d"
a
b
c d

Which is what I expect and want. But on the other hand if I get the arguments list from a variable I get this:

[~]$ args='a b "c d"'
[~]$ decho $args
a
b
"c
d"

Which is not what I want. I can go:

[~]$ echo decho $args | bash
a
b
c d

But that seems a little clunky. Is there a better way to make the expansion of $args in decho $args be word-split the way I expected?

From stackoverflow
  • Have you tried:

    for arg in "$@"
    do
            echo "arg $i:$arg:"
            let "i+=1"
    done
    

    Should yield something like:

    arg 1: a
    arg 2: c d
    

    in your case.

    Straight from memory, no guarantee :-)

    David Dean : This function performs exactly the same as decho. The problem is in calling decho, not decho itself.
  • You can use:

    eval decho $args
    
  • hmmm.. eval decho $args works too:

    [~]$ eval decho $args
    a
    b
    c d
    

    And I may be able to do something with bash arrays using "${array[@]}" (which works like "$@"), but then I would have to write code to load the array, which would be a pain.

  • You can move the eval inside the script:

    #!/bin/bash -u
    eval set -- $*
    for i; 
    do 
      echo $i;
    done
    

    Now you can do:

    $ args='a b "c d"'
    $ decho $args
    a
    b
    c d
    

    but you'll have to quote the arguments if you pass them on the CL:

    $ decho 'a b "c d"'
    a
    b
    c d
    

0 comments:

Post a Comment