3

Imagine I have this

$ ARGS='"a b" c'
$ for arg in "$ARGS"; do echo "$arg"; done
"a b" c
$ for arg in $ARGS; do echo "$arg"; done
"a
b"
c

The result I'm looking to get is

$ <???>
a b
c

How would I go about this? Thanks.

2 Answers 2

2

This would give the output you ask for:

eval "for arg in $ARGS; do echo \"\$arg\";done"

But eval should only be used as a last resort. The reason your quetsion is hard to answer is that the format of your ARGS variable is badly designed.

A series of words separated by whitespace, with whitespace inside words protected by a quoting mechanism, is the basic format of a shell command. Since you have to learn this format to be able to write a shell script, it becomes tempting to use it for the data structures managed by your shell script, so you just take the shell-like construct "a b" c and stuff it into a shell variable.

This is usually a bad idea. There's no reason for the strings in your shell script to look like shell command lines, and the only way to manipulate a string containing whitespace-separated-maybe-quoted-words is to either build your own parser out of primitive string operations, or ask the shell to do it with eval.

But when you use eval, you don't just get the handy-dandy word splitter and quote remover, you also get all the rest of the shell's parser: command substitution, parameter substitution, input/output redirection, and other things you probably don't want.

Use an array. This array-based script works in zsh, ksh, and bash:

ARGS=("a b" c)
for arg in "${ARGS[@]}"; do
    echo "$arg"
done

It's much cleaner than eval. If you're programming for an ancient system that doesn't have zsh, ksh, or bash, consider learning awk. The minimal POSIX shell just isn't a good language for scripts complicated enough to need arrays.

A little elaboration on part of ormaaj's answer: POSIX sh has a single array variable: $@ which is the array of "positional parameters" (command line arguments). You can access (read, not write) its individual elements as $1, $2, $3, etc. You can replace the entire array with the set command. For your sample script, this functionality is just barely enough. This works in any shell of the Bourne family:

set -- "a b" c
for arg; do
    echo "$arg"
done

I added a bonus feature demonstration there: for arg without an in is equivalent to for arg in "$@" which loops over the $@ array.

0

Since you don't care about the quotes just remove them with sed before you walk through the string:

for arg in `echo $ARGS | sed -e 's/"//g'`; do
  echo $arg
done

Unless... your example is wrong and you want to see something like this:

a b
c

There are probably many ways to do this but I would use awk (assuming GNU awk so we can use field separators longer than one character):

echo $ARGS | \
awk -F'" ' '{ for (field = 1; field <= NF; field++) print $field; }' | \
sed -e 's/^"//' -e 's/"$//'

The last sed removes any opening quotes and an eventual closing quote at the end of $ARGS. Of course, this whole thing will fall apart if you have nested quotes.

1
  • Actually i had typed it wrong, the output i wanted was "a b\nc"
    – Flame
    Jul 28, 2012 at 4:34

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .