arraysbashcsvfor-loopif-statement

Including more commands if CSV column is not empty


Let's say I have the following CSV file (sample.csv)

filepath1
filepath1,filepath2
filepath1,filepath2,filepath3
filepath1,filepath2,filepath3,filepath4
filepath1,filepath2

And the program I need to use uses the following syntax:

program_im_using --file filepath1

But if multiple files are present, the syntax for input is:

program_im_using --file filepath1 --file filepath2
or
program_im_using --file filepath1 --file filepath2 --file filepath3

What I would do to paste the values from the CSV file would be something like this:

filepath_first=( $(awk -F "\"*,\"*" '{print $1}' $sample_csv) )
filepath_second=( $(awk -F "\"*,\"*" '{print $2}' $sample_csv) )

for i in "${!filenames[@]}"; do
    program_im_using --file "${filepath_first[i]}" --file "${filepath_second[i]}"
done

My example of what I'm doing only works if I have exactly 2 columns of values in every row. How can I generalize in a way such that if I have one column, I get:

program_im_using --file filepath1

But if I have two

program_im_using --file filepath1 --file filepath2

etc?


Solution

  • For a variable number of args one common approach is to store the args in an array, eg:

    $ args=(--file filepath1 --file filepath2)
    $ typeset -p args
    declare -a args=([0]="--file" [1]="filepath1" [2]="--file" [3]="filepath2")
    

    A sample script to list input args:

    $ cat program_im_using
    #!/bin/bash
    n=1
    for i in "$@"; do echo "arg #$n:$i:";((n++));  done
    

    We can then feed these args to the program like such:

    $ ./program_im_using "${args[@]}"
    arg #1 :--file:
    arg #2 :filepath1:
    arg #3 :--file:
    arg #4 :filepath2:
    

    Sample input file:

    $ cat sample.csv
    filepath1
    filepath1,filepath2
    filepath1,filepath2,filepath3
    /a/b/c.txt,/d/e/f/g.txt,/h.txt
    

    Using sed to reformat our csv lines:

    $ sed 's/^/--file /; s/,/ --file /g' sample.csv
    --file filepath1
    --file filepath1 --file filepath2
    --file filepath1 --file filepath2 --file filepath3
    --file /a/b/c.txt --file /d/e/f/g.txt --file /h.txt
    

    Feeding the sed results to a bash/while-read loop:

    while read -ra args
    do
        printf "\n####### ${args[*]}\n"
        ./program_im_using "${args[@]}"
    done < <(sed 's/^/--file /; s/,/ --file /g' sample.csv)
    

    NOTES:

    This generates:

    ####### --file filepath1
    arg #1 :--file:
    arg #2 :filepath1:
    
    ####### --file filepath1 --file filepath2
    arg #1 :--file:
    arg #2 :filepath1:
    arg #3 :--file:
    arg #4 :filepath2:
    
    ####### --file filepath1 --file filepath2 --file filepath3
    arg #1 :--file:
    arg #2 :filepath1:
    arg #3 :--file:
    arg #4 :filepath2:
    arg #5 :--file:
    arg #6 :filepath3:
    
    ####### --file /a/b/c.txt --file /d/e/f/g.txt --file /h.txt
    arg #1 :--file:
    arg #2 :/a/b/c.txt:
    arg #3 :--file:
    arg #4 :/d/e/f/g.txt:
    arg #5 :--file:
    arg #6 :/h.txt: