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.

3
  • 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. Commented Aug 30, 2019 at 22:49
  • This might help: Using sed to color the output from a command on solaris Commented Aug 30, 2019 at 22:49
  • Perhaps it's a quoting issue in the shell script? Commented Aug 30, 2019 at 22:50

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

4 Comments

This works. What if I needed \e[31m to be a variable, $color. How do I change the quoting?
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.
Sorry, this was something I hadn't considered but now realize is somewhat important. I'll edit the question.
A small bug. This sed "/$1/s//$blue&$reset/' should be sed "/$1/s//$blue&$reset/2" (double quote at the final)
1

With GNU sed and Kubuntu 16.04.

foo="#.*"
sed 's/'"$foo"'/\x1b[31m&\x1b[0m/' file

Comments

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

Comments

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.