2

I've tried to create a awk script or script in other method.

I want a given line from the log file that contains date and time variables (but contains a given word) to be underlined with a specific color.

I created something like this in awk, but it only underlines a certain phrase, without a date and time, would it be possible to underline the date and time additionally or the entire line containing that words?

awk $'{ gsub(" DEBUG StateMachine\|entr \'NTP:nextGetTimeTimeoutState'", "\033[1;41m&\033[0m");
print }' LOG.log

this line from LOG.log looks something like this:

2021-08-17 10:16:35,445 DEBUG StateMachine|exit 'NTP:nextGetTimeTimeoutState'
2021-08-17 10:16:35,445 DEBUG StateMachine|entr 'NTP:nextIteratorState'
2021-08-17 10:16:35,445 INFO StateMachine|task 'NTP:nextIteratorState'
2021-08-17 10:16:35,449 DEBUG StateMachine|exit 'NTP:nextIteratorState'
2021-08-17 10:16:35,449 DEBUG StateMachine|entr 'NTP:nextGetTimeTimeoutState'
2021-08-17 10:16:35,449 INFO StateMachine|wait 60000 NTP:nextGetTimeTimeoutState
1
  • 1
    Please edit your question and explain the pattern. Do you want to colorize lines that contain the literal string DEBUG StateMachine|entr 'NTP:nextGetTimeTimeoutState'? Or is the | meant as the OR operator between two separate patterns DEBUG StateMachine and entr 'NTP:nextGetTimeTimeoutState' Please copy&paste exactly the code you tried, not "something like this".
    – Bodo
    Sep 15 at 11:54
9

Any time you find yourself using $'{...}' around an awk script you are doing something wrong and should ask for help. Never do that as it's never required in a well-written script and causes your script to become fragile as it's inviting the shell to interpret some parts of it before awk even sees it.

Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems. :-)

You're escaping regexp metachars to make your regexp act like it's a string. Don't do that - just use string instead of regexp operators when you want to match a string:

awk 'index($0,"DEBUG StateMachine|entr \047NTP:nextGetTimeTimeoutState\047") {
    $0 = "\033[1;41m" $0 "\033[0m"
}
1' LOG.log

The \047s instead of 's are because you can't escape a ' in a '-delimited string (including scripts) in shell. See http://awk.freeshell.org/PrintASingleQuote.

To highlight 2 different lines with the same color you could use:

awk '
    index($0,"DEBUG StateMachine|entr \047NTP:nextGetTimeTimeoutState\047") ||
    index($0,"DEBUG StateMachine|exit \047NTP:nextGetTimeTimeoutState\047") {
        $0 = "\033[1;41m" $0 "\033[0m"
    }
1' LOG.log

and to highlight 2 lines with 2 different colors:

awk '
    index($0,"DEBUG StateMachine|entr \047NTP:nextGetTimeTimeoutState\047") {
        $0 = "\033[1;42m" $0 "\033[0m"
    }
    index($0,"DEBUG StateMachine|exit \047NTP:nextGetTimeTimeoutState\047") {
        $0 = "\033[1;41m" $0 "\033[0m"
    }
1' LOG.log

Having said that, since you apparently are trying to use different colors based on different parts of the input, now it would be appropriate to use a regexp with capture groups to isolate the relevant parts of the input and then just look at those parts to determine the color to use for each line and here's how I'd implement that using GNU awk for the 3rd arg to match() for capture groups:

$ cat tst.awk
BEGIN {
    red    = "\033[1;41m"
    green  = "\033[1;42m"
    yellow = "\033[1;43m"
    blue   = "\033[1;44m"
    purple = "\033[1;45m"
    reset  = "\033[0m"

    map["nextGetTimeTimeoutState","entr"] = green
    map["nextGetTimeTimeoutState","exit"] = red
    map["nextIteratorState","entr"]       = yellow
    map["nextIteratorState","task"]       = blue
    map["nextIteratorState","exit"]       = purple
}
match($0,/(DEBUG|INFO) StateMachine\|(\S+)\s+\047NTP:([^\047]+)\047/,a) {
    key = a[3] SUBSEP a[2]
    if ( key in map ) {
        $0 = map[key] $0 reset
    }
}
{ print }

or using any POSIX awk:

$ cat tst.awk
BEGIN {
    red    = "\033[1;41m"
    green  = "\033[1;42m"
    yellow = "\033[1;43m"
    blue   = "\033[1;44m"
    purple = "\033[1;45m"
    reset  = "\033[0m"

    map["nextGetTimeTimeoutState","entr"] = green
    map["nextGetTimeTimeoutState","exit"] = red
    map["nextIteratorState","entr"]       = yellow
    map["nextIteratorState","task"]       = blue
    map["nextIteratorState","exit"]       = purple
}
match($0,/(DEBUG|INFO) StateMachine\|[^[:space:]]+[[:space:]]+\047NTP:[^\047]+\047/) {
    split($0,a,/[|[:space:]:\047]+/)
    key = a[9] SUBSEP a[7]
    if ( key in map ) {
        $0 = map[key] $0 reset
    }
}
{ print }

Whichever one you use the output will be:

enter image description here

You don't need the intermediate variables red, green, etc. as you could just do:

    map["nextGetTimeTimeoutState"]["entr"] = "\033[1;42m"
    map["nextGetTimeTimeoutState"]["exit"] = "\033[1;41m"

but I find it helps clarity and ease of future maintenance/updates to have them.

3
  • 1
    Thank you for help i have a little question is this possible to add a second line to underline ? i trying rewrote to this: ``` awk 'index($0,"DEBUG StateMachine|entr \047NTP:nextGetTimeTimeoutState\047"); index($1,"DEBUG StateMachine|exit \047NTP:nextGetTimeTimeoutState\047"); { $0 = "\033[1;42m" $0 "\033[0m" $1 = "\033[1;41m" $0 "\033[0m" } 1' LOG.log ``` But i've got a error like this: awk: 1: unexpected character '.' awk: 5: unexpected character '.' Sep 16 at 6:39
  • You're welcome. Yes. That's not really close to valid awk syntax, get the book Effective AWK Programming, 5th Edition, by Arnold Robbins to learn how to use awk. I updated my answer to show a way to color multiple lines using the same approach as for 1 line but if you're trying to create a script to color multiple lines in different colors based on the entr/exit and NTP parts there's a better way to do that than just comparing 1 line at a time - as a kew question if that's the case and be sure to include truly represnetative sample input/output with multiple lines needing various colors.
    – Ed Morton
    Sep 16 at 13:15
  • Oh, what the heck - I went ahead and added a better solution to the more general problem of highlighting multiple lines using multiple colors based on the content in specific positions in those lines.
    – Ed Morton
    Sep 16 at 13:35
3

If I understand you correctly you can use regex and octal representation of single quotes (\047) like this:

awk '{ sub(/^.*DEBUG StateMachine\|entr \047NTP:nextGetTimeTimeoutState\047/, "\033[1;41m&\033[0m/); print }' LOG.log

enter image description here

0
3

Since this is a straight forward "make an edit on a number of matching lines", you don't need the power of awk. As others have noted, much of your problem is getting the quoting correct.

Using sed, in one line

sed "/DEBUG StateMachine|entr 'NTP:nextGetTimeTimeoutState'/s/.*/"$'\e[[1;41m&\e[0m'/

or in 3

PAT=" DEBUG StateMachine|entr 'NTP:nextGetTimeTimeoutState'"
TO=$'\e[[1;41m&\e[0m'
sed "/$PAT/s/.*/$TO/"
2

You may just substitute the beginning and end of the whole line whenever your match is hit:

awk '
/DEBUG StateMachine|entr \047NTP:nextGetTimeTimeoutState\047/ {
  gsub(/^/,"\033[1;41m",$0)
  gsub(/$/,"\033[0m")
}
1 ' LOG.log

Note that \| is the OR operator in regex and just | a literal pipe.

1
  • 2
    Use sub() instead of gsub() if you only want a regex to match once (as you do) but your script will run faster if you just do a simple string assignment of $0 = "\033[1;41m" $0 "\033[0m" instead of 2 subs() which will each be slower than an assignment individually and also cause awk to re-split $0 twice since you're modifying it twice instead of just once.
    – Ed Morton
    Sep 15 at 12:34
0

As for other methods, there exist packages like colorize or ccze

where you can pipe some text into them and they will pretty-up your output. Most use a generic colour-profile, but it is possible to write your own.

Try with the defaults first and see if you like it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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