1

I have the following CSV file:

Alabama,Alaska,Arizona,Arkansas,California,Colorado,Connecticut,Delaware,Florida,Georgia,Hawaii,Idaho,Illinois,Indiana,Iowa
1000,"1 0 0 1",1002,1002,1003,1004,1005,"1 0 0 6",1007,1008,1009,1010,1011,1012,1013
100,101,102,102,103,104,105,"1 0 6 2",107,108,109,110,111,112,113
10001,10011,10021,10021,10031,10041,10051,10061,10071,10081,10091,10101,10111,10121,10131
.
.
.
.

My target is to set the CSV parameters ( all states in CSV ) with their values in my bash script

for example

#!/bin/bash

Alabama=1000
.
.
.
Iowa=1013

so in my bash script I can read each parameter

example

 echo $Alabama
 1000

First I just tried to write the following (wrong) code, in order to set the parameters with their values:

#!/bin/bash

counter=1

for CSV_COLUMN in Alabama  Alaska  Arizona Arkansas  California  Colorado  Connecticut Delaware Florida  Georgia  Hawaii  Idaho  Illinois  Indiana  Iowa 
do
  export $CSV_COLUMN=` echo $CSV_LINE | cut -d',' -f$counter `
  counter=$counter+1
done

The test should be (from the bash script)

echo $Alabama
1000

How should I change my code in order to implement my idea?

  • Please read this page for instructions on formatting your posts on Unix.SE. Are the extra spaces part of your input file? Why do you have many more values than states? – terdon Jul 15 '14 at 13:39
  • about you last question - because the defintion of each line is diff , but the parameters are the same for each line – maihabunash Jul 15 '14 at 13:42
  • 1
    I'm sorry, that makes no sense to me. What definition? What parameters? Do you want to go through the file and assign the values to the variables for each line? – terdon Jul 15 '14 at 13:49
  • 1
    If you are using bash version 4+, have you considered using a single associative array instead of defining a whole bunch of individual named variables? – steeldriver Jul 15 '14 at 15:23
  • 1
    Could you tell us what you're actually trying to do? Having a variable named $ALABAMA is not very useful since you will never see that name written, it would only be available to the program internally. I think what you're after will be solved by using arrays but please explain what kind of processing your script will be doing. – terdon Jul 15 '14 at 16:55
2

The command

awk -F, 'NR==1 { for (i=1; i<=NF; i++) sn[i]=$i }
         NR==2 { for (i=1; i<=NF; i++) print sn[i] "=" $i; exit }' states

where states is your file, will output

Alabama=1000
Alaska="1 0 0 1"
Arizona=1002
Arkansas=1002
California=1003
Colorado=1004
Connecticut=1005
Delaware="1 0 0 6"
Florida=1007
Georgia=1008
Hawaii=1009
Idaho=1010
Illinois=1011
Indiana=1012
Iowa=1013
  • -F, sets awk’s field separator to ,.
  • NR==1 means “do the following on the first record (line) only”; NR==2 means the second line.
  • The (first) for loop looks at each field in the first row and assigns the values (state names) to the sn array: sn[1]=Alabama, sn[2]=Alaska, …).
  • The second for loop looks at each (comma-delimited) field in the second row and pairs it with the corresponding state name (from the first line) and an =, as shown above.
  • Then we exit, so awk doesn’t need to read the rest of the file.

So

eval $(awk -F, 'NR==1 { for (i=1; i<=NF; i++) sn[i]=$i }
                NR==2 { for (i=1; i<=NF; i++) print sn[i] "=" $i; exit }' states)

will capture that output and interpret it as a series of commands.  Done.


If you’re determined to use a shell loop and the cut command, consider

values=$(sed -n '2p;2q' states)
counter=1
for state_name in $(sed 's/,/ /g;1q' states)
do
    eval $state_name=$(echo "$values" | cut -d, -f$counter)
    counter=$((counter+1))
done

Or you could list the state names explicitly, as you did in your example/attempt (for var in Alabama Alaska Arizona Arkansas …).  But why would you want to duplicate your data in your code when it’s already in the file?  And you could eliminate the values variable and do

eval $state_name=$(sed -n '2p;2q' states | cut -d, -f$counter)

in the loop, but that would require reading the states file 50 times instead of once.  (Or 51 times instead of twice, if you count the for state_name in $(sed 's/,/ /g;1q' states) statement.)

  • 1
    You are, of course, free to have whatever opinion you want, but I don’t understand what you’re talking about. Search the question and the other answer for 103 and 10001 – the only place they appear is in the question, in the display of the input file (3rd and 4th lines). The OP never said anything about doing anything with those values, and neither did @chaos. – Scott Jul 15 '14 at 20:11
1

Use this line:

read `sed -e 's/,/ /g' -e '1q;d' file` < <(sed -e 's/\ /\\\ /g' -e 's/"//g' -e 's/,/ /g' -e '2q;d' file)

Explanation:

  • The first sed command causes that only the first line of the file is printed and commas (,) are replaced with spaces: Alabama Alaska Arizona Arkansas California ...

  • The second sed command does the same with the second line (it seems in your question that you only want the values of the second line): 1000 1001 1002 1002 ....

  • read -r assignes the first list with the second.

After you can test the values with echo:

echo $Alabama
1000
echo $Alaska
1 0 0 1
echo $Georgia
1008
  • I want to add something else , some times I have CSV as 1000, "10 11 22 33" , 1001 , 1002 , how we can support the strings between the " " – maihabunash Jul 15 '14 at 14:58
  • see my update question – maihabunash Jul 15 '14 at 15:07
  • @maihabunash I changed the line a bit, see my updated answer. – chaos Jul 15 '14 at 18:16
  • need to add counter to cover all lines – user58412 Jul 15 '14 at 19:59
  • 1
    I don't understand what you mean. What's with the other lines, you can have only one value. Or else I completely misunderstood your question. – chaos Jul 15 '14 at 20:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.