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?

closed as off-topic by Michael Homer, Anthon, jasonwryan, jimmij, Networker Mar 18 '15 at 0:39

This question appears to be off-topic. The users who voted to close gave this specific reason:

  • "Questions describing a problem that can't be reproduced and seemingly went away on its own (or went away when a typo was fixed) are off-topic as they are unlikely to help future readers." – Michael Homer, Anthon, jasonwryan, jimmij, Networker
If this question can be reworded to fit the rules in the help center, please edit the question.

  • 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 '15 at 6:26
  • The code you posted works for me in Bash. I think it's even POSIX-compliant. Are you sure it's exactly what you ran? What does bash --version claim? – Michael Homer Mar 17 '15 at 6:26
  • 1
    In particular, the error you post is exactly what you'd get if you'd quoted "$FIELDS", which the code you posted doesn't — does your real code do that? – Michael Homer Mar 17 '15 at 6:33
  • 1
    I have that exact version to hand here. Copying the code in your second block in literally works as expected. Something else is up. – Michael Homer Mar 17 '15 at 8:51
  • 1
    What is $IFS set to? – Stephen Kitt Mar 17 '15 at 9:09
3

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 an array as suggested by GMaster works in this case:

IFS=,
FIELDS=(NAME PRETTY_NAME DESC)
while read ${FIELDS[@]}; do
   echo $NAME
done < some.csv
  • I marked you as correct Stephen, although I actually worked out (with your and Michael's help). – Jeremy Davis Mar 17 '15 at 11:26
3

Use bash arrays

FIELDS=(NAME PRETTY_NAME DESC)
while read ${FIELDS[@]}; do
  echo $NAME
done < some.csv
  • 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! :) – Jeremy Davis Mar 17 '15 at 11:24
1

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 or ask your own question.