0

It might seem very easy, but I'm stuck at this.

I have a string that is CSV and the length of items is unknown.

"item1,item2,item3,..."

I want to cut it, and loop over its items.

I tried:

while IFS=, read item;
do
   echo $item
done <<< $csvString

But it just gave me the entire string once.

And I can't use cut because I can't find how to loop over it.

  • Change csvString to $csvString – Edgar Magallon Jan 27 '23 at 04:37
  • @EdgarMagallon, I updated the question. It was a typo. But it does not work. – Saeed Neamati Jan 27 '23 at 04:48
  • 1
    Oh, I see. The problem is that the while loop actually reads line per line but the variable assignments consider the delimiter. For example, if you had: string1,string2,string3 and you se while IFS=, read item item2; do ... the values of item and item2 will be string1 and string2,string3 respectively – Edgar Magallon Jan 27 '23 at 05:25
  • @EdgarMagallon, yea, I know that. I can use item1 item2. But the length of parameters is not known beforehand. I don't know how many itemX there is in the string. – Saeed Neamati Jan 27 '23 at 05:43
  • 1
    If you are using bash use: readarray -d ',' -t vals <<< "$csvString". That will create an array with a N number of items (according to the delimiter: ,). You will be able to loop over the array – Edgar Magallon Jan 27 '23 at 05:47

2 Answers2

3

You can't loop like you are showing because your input is a single line, and read reads a line at a time. The read operation reads the first and all subsequent fields into the single variable you provide.

If you want to process your input string item by item, you can do so by converting the items into the elements of an array and then iterating over these:

readarray -d , -t csvArray < <( printf '%s' "$csvString" )

for item in "${csvArray[@]}"; do printf '%s\n' "$item" done

I'm using printf with readarray above to avoid adding a newline character at the end of the string (<<<"$csvString" would add a newline).

However, if your input string is a CSV string and not a simple list of comma-delimited substrings, then you can't rely on readarray to split your string correctly since some fields may contain embedded delimiters. You would instead use a CSV-aware tool, such as Miller (mlr), to parse the string and to perform whatever action it is that you want to do.

$ csvString='"1, 2, 3",Hello world,A,B,C'
$ mlr --csv -N put -q 'for (k,v in $*) { emit v }' <<<"$csvString"
"1, 2, 3"
Hello world
A
B
C

The above mlr command iterates over the single header-less CSV input record, outputting the value in each field as a new record. Miller quotes the first emitted record automatically since it contains embedded commas.

Kusalananda
  • 333,661
2

You could also use awk as following (Assuming that $csvString contains strings of a Simple-csv format):

awk -v itmStr="$csvString" '
BEGIN{
       itmNr=split(itmStr, items, /,/)
       for (i=1; i<=itmNr; i++)
           print items[i]
}'
αғsнιη
  • 41,407