Shell – Preserving color output with cut

colorscommand linelspipeshell

Using bash interactively I am trying to use cut to prevent the lines from going longer than my terminal width. But when using a command with colorized output all the color gets removed by cut. For example: ls -lG | cut -c 1-$COLUMNS.

Is there anyway to get cut to preserve the color formatting?

Best Answer

You've got two problems here

  1. ls -G stops outputting in colour when the output doesn't go to a terminal (here to a pipe instead). GNU ls needs to be passed a --color=always option, and for BSD ls, you need to set the environment CLICOLOR_FORCE to a non-empty value to tell it to always output in color.
  2. Colors are achieved by outputting escape sequences that are a sequence of characters like <ESC>[31m for foreground red. That doesn't have any width when displayed, but as far as cut is concerned, that's 5 characters which will count up to $COLUMNS.

    So you can't use cut here as you need to ignore those escape sequences in the calculation. Instead, you could do something like:

    esc=$'\e'
    CLICOLOR_FORCE=1 ls -l | sed "s/\(\(\($esc\[[0-9;]*m\)\{0,1\}.\{0,1\}\)\{$COLUMNS\}\).*/\1${esc}[m/"
    

    There, sed does the counting and adds a \e[m to revert the color to default in case it has been cut in the process.

Alternatively, you could tell your terminal not to wrap and do the cutting itself with:

tput rmam

(tput smam to restore)

You could define a function like:

nowrap() {
  [ -t 1 ] && tput rmam
  "$@"; local ret="$?"
  [ -t 1 ] && tput smam
  return "$ret"
}
alias nowrap='nowrap '

(the alias part to force alias expansion after nowrap), to be called as:

nowrap ls -l ...