1

This is a bash question (hence posting here in Unix&Linux)

I have a while loop that reads from a csv; It looks something like this:

while read NAME PRETTY_NAME DESC; do
     echo $NAME
done < some.csv

And that all works lovely; but what I'd really love to do is define the field names in a variable; like this:

FIELDS="NAME PRETTY_NAME DESC"
while read $FIELDS; do
     echo $NAME
done < some.csv

but unfortunately it seems that is not acceptable:

-bash: read: `NAME PRETTY_NAME DESC': not a valid identifier

:( Any ideas if (or preferably how) this is possible?

3
  • Mind to explain what you want to gain with that separation or indirection of names? And, BTW, your read command won't work with CSV files in general.
    – Janis
    Mar 17, 2015 at 6:26
  • @Janis - I do some processing using info from the CSV file then write it out to an updated file. I could do it manually; but as things are still in flux ATM I wanted to have a single place I could add fields that would propagate throughout the script. And what do you mean that it won't work for CSV files in general? Mar 17, 2015 at 8:27
  • @Jeremy; to answer the question in your above comment. A CSV file may have [quoted or escaped] characters inside the data fields. So whether you define your IFS correctly (which wasn't shown in your question) or not, it wouldn't help if you have four fields like A,B,"C,D",E.
    – Janis
    Mar 17, 2015 at 16:27

3 Answers 3

4

I'm guessing you've set IFS to , to parse the CSV. At least when I do that I get the same error message you're getting:

IFS=,
FIELDS="NAME PRETTY_NAME DESC"
while read $FIELDS; do
    echo $NAME
done < some.csv

That's because the shell parses read $FIELDS using $IFS, so it looks for , to separate the arguments; thus NAME PRETTY_NAME DESC ends up being interpreted as a single argument instead of three, and the result is an invalid identifier name.

Using commas instead of spaces in FIELDS works:

IFS=,
FIELDS="NAME,PRETTY_NAME,DESC"
while read $FIELDS; do
    echo $NAME
done < some.csv

Alternatively, you can use an array as suggested by GMaster:

IFS=,
FIELDS=(NAME PRETTY_NAME DESC)
while read ${FIELDS[*]}; do
   echo $NAME
done < some.csv
0
3

Use bash arrays

FIELDS=(NAME PRETTY_NAME DESC)
while read ${FIELDS[@]}; do
  echo $NAME
done < some.csv
1
  • Thanks for alerting me to bash arrays. TBH I was under the impression that there wasn't even such a thing... Got some reading to do! :) Mar 17, 2015 at 11:24
2

The best practice is to set the IFS in the while loop, thus once the loop is finished IFS returns to the IFS set by the shell.

FIELDS="NAME PRETTY_NAME DESC"
while IFS="," read -r $FIELDS; do
     echo $NAME
done < some.csv

This is POSIX compliant code.

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