How to colorize head, tail and less, same as I’ve done with cat

colorsfunctionheadtail

I've got 'color cat' working nicely, thanks to others
(see How can i colorize cat output including unknown filetypes in b&w?).

In my .bashrc:

cdc() {
  for fn in "$@"; do
  source-highlight --out-format=esc -o STDOUT -i $fn 2>/dev/null || /bin/cat $fn;
  done
}
alias cat='cdc' # To be next to the cdc definition above.

I'd like to be able to use this technique for other functions like head, tail and less.

How could I do that for all four functions? Any way to generalize the answer?

I have an option for gd doing git diff using

gd() { 
   git diff -r --color=always "$@"
}

Best Answer

Something like this should do what you want:

for cmd in cat head tail; do
  cmdLoc=$(type $cmd | awk '{print $3}')
  eval "
    $cmd() { 
      for fn in \"\$@\"; do 
        source-highlight --failsafe --out-format=esc -o STDOUT -i \"\$fn\" | 
            $cmdLoc - 
      done 
    }
  "
done

You can condense it like this:

for cmd in cat head tail; do
    cmdLoc=$(type $cmd |& awk '{print $3}')
    eval "$cmd() { for fn in \"\$@\"; do source-highlight --failsafe --out-format=esc -o STDOUT -i \"\$fn\" | $cmdLoc - ; done }"
done

Example

With the above in a shell script, called tst_ccmds.bash.

#!/bin/bash

for cmd in cat head tail; do
    cmdLoc=$(type $cmd |& awk '{print $3}')
  eval "$cmd() { for fn in \"\$@\"; do source-highlight --failsafe --out-format=esc -o STDOUT -i \"\$fn\" | $cmdLoc - ; done }"
done

type cat
type head
type tail

When I run this, I get the functions set as you'd asked for:

$ ./tst_ccmds.bash
cat () 
{ 
    for fn in "$@";
    do
        source-highlight --failsafe --out-format=esc -o STDOUT -i "$fn" 2> /dev/null | /bin/cat - ;
    done
}
head is a function
head () 
{ 
    for fn in "$@";
    do
        source-highlight --failsafe --out-format=esc -o STDOUT -i "$fn" 2> /dev/null | /usr/bin/head - ;
    done
}
tail is a function
tail () 
{ 
    for fn in "$@";
    do
        source-highlight --failsafe --out-format=esc -o STDOUT -i "$fn" 2> /dev/null | /usr/bin/tail -;
    done
}

In action

When I use these functions in my shell (source ./tst_ccmds.bash) they work as follows:

cat

cat ss

head

head ss

tail

tail ss

plain text

txt ss

What's the trick?

The biggest trick, and I would call it more of a hack, is the use of a dash (-) as an argument to cat, head, and tail through a pipe which forces them to output the content that came from source-highlight through STDIN of the pipe. This bit:

...STDOUT -i "$fn" | /usr/bin/head - ....

The other trick is using the --failsafe option of source-highlight:

   --failsafe
          if no language definition is found for the input, it  is  simply
          copied to the output

This means that if a language definition is not found, it acts like cat, simply copying its input to the standard output.

Note about aliases

This function will fail if any of head,tail or cat are aliases because the result of the type call will not point to the executable. If you need to use this function with an alias (for example, if you want to use less which requires the -R flag to colorize) you will have to delete the alias and add the aliased command separately:

less(){
     for fn in "$@"; do 
       source-highlight --failsafe --out-format=esc -o STDOUT -i "$fn" |
          /usr/bin/less -R || /usr/bin/less -R "$fn"; done
}
Related Question