1

I have data in a csv file. I wrote a script that cats this file and uses column -s, -t to nicely tabularize it into nice columns:

Foosballs  Barbells  Bazketballs
22         39        14
86         94        37
17         44        28

However, I'd like to display the header row in bold. I can do that by writing the format codes directly to the file.

bold=$(tput bold)
reset=$(tput sgr0)
echo "${bold}Foosballs,Barbells,Bazketballs${reset}" > /path/to/file

This works fine with cat; the format codes are displayed correctly when I cat the file. But they screw up column -t: any colored/bolded row is no longer aligned with the other rows.

Foosballs  Barbells  Bazketballs
22            39        14
86            94        37
17            44        28

Is there some way to get column -t to ignore color codes when lining up data into columns? (Or is there a better way to display csv data in columns?)

UPDATE:

Applying column first and the format codes second will work, as some answers point out. But in many cases I want to apply different formats/colors to individual values in the row, not to the entire row. Here's a simple example:

echo "${underline}foo${reset}  ${underline}bar${reset}"

In general, I might want to use arbitrary formatting logic that's difficult or impossible to apply post-hoc (i.e., after I've already printed the line and called column -t on it). Formatting after tabularizing (as in Charles Duffy's answer) is a great start but may not always work for me (at least, conveniently).

I could always write a utility to do this format-code-transparent tabularization myself, but then I'd have to bring that with me wherever I work. I don't want to have to know column widths in advance; I need something like column -t I can throw on the end of a pipe with arbitrary delimited data. Basically, I need a clever one-liner or a third-party util that's readily available via Homebrew or other package managers.

To sum up: For the bounty, I'm looking for a simple, (reasonably) portable method to tabularize previously-formatted data.

4
  • 1
    You can still apply the color codes after column, right? Commented Jul 10, 2018 at 20:37
  • There is no direct way/standard tool to accomplish what you want. What other options are you looking for?
    – Inian
    Commented Nov 2, 2020 at 15:52
  • Are you using MacOS? I get the output you desire using column from util-linux-2.36 but I can confirm that column used on FreeBSD (which is probably more similar to what you have on Mac) botches the output. Commented Nov 2, 2020 at 19:59
  • This might work formulae.brew.sh/formula/util-linux Commented Nov 2, 2020 at 20:04

2 Answers 2

2

One mechanism to enforce alignment and inject color codes is to use printf:

printf '%s%-20s %-20s %-20s%s\n' "$bold" "Foosballs" "Barbells" "Bazketballs" "$reset"

Note that we're using %s placeholders for the color codes, and strings like %-20s (20 characters, left-aligned) for the other fields. This does mean that your code needs to be responsible for knowing the desired length for each column.


If you don't want to do that, you can postprocess:

generate_data() {
  echo "Foosballs,Barbells,Bazketballs"
  echo 22,39,14
  echo 86,94,28
  echo 17,44,28
}

bold=$(tput bold)
reset=$(tput sgr0)

generate_data | column -s, -t | {
  IFS= read -r header                     # read first line
  printf '%s\n' "${bold}$header${reset}"  # write first line in bold
  cat                                     # pass rest of stream through unmodified
}

Or, to color just one column:

generate_data() { printf '%s\n' "Foosballs,Barbells,Bazketballs" 22,39,14 86,94,28 17,44,28; }

color_column() {
  gawk -v column_nr="$1" -v color_start="$2" -v color_end="$3" '
    BEGIN { FPAT = "([[:space:]]*[^[:space:]]+)"; }
    { $column_nr = color_start $column_nr color_end; print $0 }
  '
}
generate_data | column -s, -t | color_column 2 "$(tput bold)" "$(tput sgr0)"
4
  • I like column primarily because it saves me from having to calculate all the column widths, which are not known in advance. The postprocessing seems like a good solution -- however, it does require the color code be applied to the entire line. It would be great to be able to apply color codes to a single column, for instance, which would take extra work this way. (Editing the original question to mention that.)
    – Sasgorilla
    Commented Jul 11, 2018 at 12:32
  • Still doable in post. A column identified how? By position? By contents? ...? -- and the whole column, or just the header? Commented Jul 11, 2018 at 12:43
  • I was thinking here of the entire column, identified by position. Would be cool to see your thoughts on that as well.
    – Sasgorilla
    Commented Aug 10, 2018 at 12:14
  • See the recent amendment. Commented Aug 10, 2018 at 13:13
1

For this test file:

$ cat file
Foosballs,Barbells,Bazketballs
22,39,14
86,94,28

The simple way:

d='\e[0m'  #default env
r='\e[31m' #red   color

printf "$r"; column -s, -t file; printf "$d"

enter image description here

More complicated with different color for each column:

s=','       #delimiter
d='\\e[0m'  #default env
r='\\e[31m' #red   color
g='\\e[32m' #green color
b='\\e[34m' #blue  color

echo -e "$(
awk  -F $s     \
     -v s="$s" \
     -v d="$d" \
     -v r="$r" \
     -v g="$g" \
     -v b="$b" \
     '{ print  r $1 s g $2 s b $3 d }' file | column -s$s -t
)"

enter image description here

And to make header bold, just add this code \e[1m to the echo command like this:

...
B='\e[1m'

echo -e "$B$(
awk  -F "$s" \
...
)"

enter image description here

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