1

Given the following file.txt:

this is line 1
# this is line 2
this is line 3

I would like to use sed to replace the lines with # at the beginning with \e[31m${lineContent}\e[0m. This will color that particular line. Additionally, I need the color \e[31m to be in a variable, color. (The desired output of this example would be having the second line colored). I have the following:

function colorLine() {
    cat file.txt | sed ''/"$1"/s//"$(printf \e[31m $1 \e[0m)"/g''
}
colorLine "#.*"

The variable color is not included in what I have so far, as I am not sure how to go about that part. The output of this is (with the second line being red):

this is line 1
#.*
this is line 3

It is apparently interpreting the replace string literally. My question is how do I use the matched line to generate the replace string?

I understand that I could do something much easier like appending \e[31m to the beginning of all lines that start with #, but it is important to use sed with the regexes.

k-a-v
  • 326
  • 5
  • 22
  • The consecutive single quotes might as well not be there. The first starts a single quoted string; the second stops it; neither is passed to the executed command. – Jonathan Leffler Aug 30 '19 at 22:49
  • This might help: [Using sed to color the output from a command on solaris](https://unix.stackexchange.com/q/45924/74329) – Cyrus Aug 30 '19 at 22:49
  • Perhaps it's a quoting issue in the shell script? – IronMan Aug 30 '19 at 22:50

4 Answers4

4
colorLine() {
    sed "/$1/s//"$'\e[31m&\e[0m/' file.txt
}
colorLine "#.*"

Multiple fixes, but it uses $1 to identify the pattern from the arguments to the function, and then uses ANSI-C quoting to encode the escape sequences — and fixes the color reset sequence which was (originally) missing the [ after the escape sequence. It also avoids the charge of "UUoC — Useless Use of cat".

The fixed file name is not exactly desirable, but fixing it is left as an exercise for the reader.


What if I needed \e[31m to be a variable, $color. How do I change the quoting?

I have a colour-diff script which contains (in Perl notation — I've translated it to Bash notation using ANSI C quoting as before):

reset=$'\e[0m' 
black=$'\e[30;1m'      # 30 = Black,   1 = bold
red=$'\e[31;1m'        # 31 = Red,     1 = bold
green=$'\e[32;1m'      # 32 = Green,   1 = bold
yellow=$'\e[33;1m'     # 33 = Yellow,  1 = bold
blue=$'\e[34;1m'       # 34 = Blue,    1 = bold
magenta=$'\e[35;1m'    # 35 = Magenta, 1 = bold
cyan=$'\e[36;1m'       # 36 = Cyan,    1 = bold
white=$'\e[37;1m'      # 37 = White,   1 = bold

With those variables around, you can create your function as you wish:

colorLine() {
    sed "/$1/s//$blue&$reset/“ file.txt
}

Where you set those variables depends on where you define your function. For myself, I'd probably make a script rather than a function, with full-scale argument parsing, and go from there. YMMV

Take a look at List of ANSI color escape sequences to get a more comprehensive list of colours (and other effects — including background and foreground colours) and the escape sequence used to generate it.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • This works. What if I needed `\e[31m` to be a variable, `$color`. How do I change the quoting? – k-a-v Aug 30 '19 at 23:01
  • It is so much easier if you ask the whole question all at once, rather than adding extras in dribs and drabs as answers come in. – Jonathan Leffler Aug 30 '19 at 23:04
  • Sorry, this was something I hadn't considered but now realize is somewhat important. I'll edit the question. – k-a-v Aug 30 '19 at 23:05
  • A small bug. This `sed "/$1/s//$blue&$reset/'` should be `sed "/$1/s//$blue&$reset/2"` (double quote at the final) – Jotne Aug 31 '19 at 06:47
1

With GNU sed and Kubuntu 16.04.

foo="#.*"
sed 's/'"$foo"'/\x1b[31m&\x1b[0m/' file
Cyrus
  • 84,225
  • 14
  • 89
  • 153
0

I'd trick grep to do it for me this way:

function colorLine() {
    GREP_COLORS="mt=31" grep --color=always --context=$(wc -l <file.txt) --no-filename "$1" file.txt
}

Split-out of the trick:

  • GREP_COLORS="mt=31": SGR substring for matching non-empty text in any matching line. Here will generate \e[31m red before matched string, and reset to default color after matched string.

  • --color=always: always colorise even in non interactive shell

  • context=$(wc -l <file.txt): output as much context lines as number of lines in the file (so all lines).

  • --no-filename: do not print the file name

Léa Gris
  • 17,497
  • 4
  • 32
  • 41
0

An awk version

black='\033[30;1m'
red='\033[31;1m'
green='\033[32;1m'
yellow='\033[33;1m'
blue='\033[34;1m'
magenta='\033[35;1m'
cyan='\033[36;1m'
white='\033[37;1m'

color=$cyan
colorLine() { awk -v s="$1" -v c=$color '$0~s {$0=c$0"\033[0m"}1' file.txt; }
colorLine "#.*"

You can add file as a variable as vell:

color=$cyan
file="file.txt"
colorLine() { awk -v s="$1" -v c=$color '$0~s {$0=c$0"\033[0m"}1' $file; }
colorLine "#.*"

In awk \e is printed as \033


A more dynamic version:

colorLine() { 
    temp=$2;
    col=${!temp};
    awk -v s="$1" -v c=$col '$0~s {$0=c$0"\033[0m"}1' $3; }
colorLine "#.*" green file.txt

Then you have colorLine pattern color file

Jotne
  • 40,548
  • 12
  • 51
  • 55