contrapositive
contrapositive

Reputation: 1531

Bash - Print CSV File

I'm trying to print a CSV file using certain column widths. I think there's an off by one error with i that's causing the first column not to print. As you will see, I'm new to bash and desperately trying to make it act like C.

CSV='./test.csv'
column_width=(20 6 5 10 10 10 10 8 30)

IFS=","
        while read LINE
        do
                set -- $LINE
                arg=($@)
                for (( i = 0 ; i < ${#arg[@]} ; i++))
                do
                        case $i in
                                1) printf "%-20s"   ${arg[$i]} ;;
                                2) printf "%-6s"    ${arg[$i]} ;;
                                3) printf "%-5s"    ${arg[$i]} ;;
                                4) printf "%-10s"   ${arg[$i]} ;;
                                5) printf "%-10s"   ${arg[$i]} ;;
                                6) printf "%-10s"   ${arg[$i]} ;;
                                7) printf "%-10s"   ${arg[$i]} ;;
                                8) printf "%-8s"    ${arg[$i]} ;;
                                9) printf "%-30s\n" ${arg[$i]} ;;
                        esac
                done
        done < $CSV
        unset IFS

I'm also having trouble turning the case statement into a loop. To no avail, I tried replacing the entire C-style for-loop with:

for i in "${arg[@]}"; do
        printf "%-${column_width[$i]}s" ${arg[$i]}
done

I'm sure there's a better way to accomplish this. I'm trying to learn about sed/awk but I'd like to know how to do it without them for now.

Upvotes: 2

Views: 2539

Answers (3)

Jo So
Jo So

Reputation: 26501

I think the script is very elegant. You will have a hard time doing this more concisely in any language (although this uses bashisms which I don't like :->)

As I'm more on the avoid-script-logic-and-use-your-editor's-macro-functionalities side of things, here would be my version.

#!/bin/sh

CSV='./test.csv'

while IFS=, read one two three four five six seven eight nine
do
    test "$one"   && printf %s-20s "$one"
    test "$two"   && printf %s-6s  "$two"
    test "$three" && printf %s-5s  "$three"
    test "$four"  && printf %s-10s "$four"
    test "$five"  && printf %s-10s "$five"
    test "$six"   && printf %s-10s "$six"
    test "$seven" && printf %s-10s "$seven"
    test "$eight" && printf %s-8s  "$eight"
    test "$nine"  && printf %s-30s "$nine"
    printf \\n
done < "$CSV"   # mind the quoting

I personally think it is a little more pleasant to the eye (and no bashisms!), but YMMV. I'd also avoid the extensive tests but simply print if possible.

Upvotes: 2

ormaaj
ormaaj

Reputation: 6577

#!/usr/bin/env bash

csv=./test.csv
column_width=(20 6 5 10 10 10 10 8 30)

while n=0; IFS=, read -ra x; do
    printf '%-*s' {,,,,,,,,}{"${column_width[n]}","${x[n++]}"} 1 $'\n'
done <"$csv"

Upvotes: 2

bsravanin
bsravanin

Reputation: 1873

Good job, Contrapositive.

The off-by-one error in your script is because array index even in Bash starts with 0, whereas your switch case doesn't. The smallest change I can think of is to "shift" the array arg. Try using arg=(0 $@). You could use any other value at the array's 0th index. The non-existent 0 case will be taken care of.

Upvotes: 1

Related Questions