2

Suppose the file log.txt contains below content

[12] 03/31/21 08:33:30.080851 T(12581) _DBG message x 1
[12] 03/31/21 08:33:30.080851 T(34897) _DBG message y 1
[12] 03/31/21 08:33:31.241167 T(12344) _DBG message z 1
[12] 03/31/21 08:33:31.457612 T(34897) _DBG message y 2
                        test message line 2
                        test message line 3 
                        test message line 4 
[12] 03/31/21 08:33:31.78912 T(12344) _DBG message z 2
[12] 03/31/21 08:33:32.56341 T(34897) _DBG message y 3
[12] 03/31/21 08:33:33.12789 T(12581) _DBG message x 2
                        test message for x
[12] 03/31/21 08:33:33.78123 T(34897) _DBG message y 3
                        test message line 2
[12] 03/31/21 08:33:34.12342 T(12581) _DBG message x 3
[12] 03/31/21 08:33:34.56712 T(34897) _DBG message y 4

Desired output should be

[12] 03/31/21 08:33:30.080851 T(34897) _DBG message y 1
[12] 03/31/21 08:33:31.457612 T(34897) _DBG message y 2
                        test message line 2
                        test message line 3 
                        test message line 4 
[12] 03/31/21 08:33:32.56341 T(34897) _DBG message y 3
[12] 03/31/21 08:33:33.78123 T(34897) _DBG message y 3
                        test message line 2
[12] 03/31/21 08:33:34.56712 T(34897) _DBG message y 4

Given a thread ID, it should print the line + next few lines belong to that msg. Note that in the desired output, all other thread messages are removed.

I tried below sed command but it prints the next line also (which is different thread message)

sed -n -e '/T(34897)/,/_DBG/ p' log.txt 

I tried other grep/awk/regex commands but couldn't get this done. Please help

3 Answers 3

3
$ awk -v t=34897 '/^\[/{f=($4=="T("t")")} f' file
[12] 03/31/21 08:33:30.080851 T(34897) _DBG message y 1
[12] 03/31/21 08:33:31.457612 T(34897) _DBG message y 2
                        test message line 2
                        test message line 3
                        test message line 4
[12] 03/31/21 08:33:32.56341 T(34897) _DBG message y 3
[12] 03/31/21 08:33:33.78123 T(34897) _DBG message y 3
                        test message line 2
[12] 03/31/21 08:33:34.56712 T(34897) _DBG message y 4

The above sets a "found" flag f every time it sees a line starting with [. If the 4th field on that line is T(<target value>) then f is set to true (1), otherwise false (0). When every line is read if f is true at that time then that current line is printed.

With any awk script if you'd like to see it with different formatting and less reliance on defaults to make it a bit less brief and more clear, you can pretty-print it using GNU awk (note: it must be gawk, not some other awk variant) as awk -o- ...:

$ awk -o- -v t=34897 '/^\[/{f=($4=="T("t")")} f' file
/^\[/ {
        f = ($4 == "T(" t ")")
}

f {
        print
}
6
  • I see a pattern and an action followed by a boolean. And I see that it works. But where is the boolean documented? I can't find it in the gawk manual. Apr 1, 2021 at 4:41
  • While that one-liner is indeed very nicely Code Golfed, it is also very abstruse and its methodologies are rather opaque in comparison to my answer. It is, though, at its core, the same solution in a more concise form.
    – DopeGhoti
    Apr 1, 2021 at 13:48
  • @DopeGhoti that's why I added the explanatory paragraph below it.
    – Ed Morton
    Apr 1, 2021 at 15:36
  • @KenJackson are you referring to f=($4=="T("t")") or something else?
    – Ed Morton
    Apr 1, 2021 at 15:39
  • 1
    @KenJackson correct. I updated my answer to show how you can pretty-print awk scripts to get a heavier focus on clarity and what the output of that is for my script. Hope that helps.
    – Ed Morton
    Apr 1, 2021 at 17:27
2

With this awk script:

BEGIN {
  doprint = 0
  marker = "T("thread")"
}

$1 ~ /\[[0-9]+\]/ {
   if( $4 == marker ) {
      doprint = 1
   } else {
      doprint = 0
   }
}

doprint==1 { print }

invoked thusly:

$ awk -v thread="34897" -f 642963.awk input.txt
[12] 03/31/21 08:33:30.080851 T(34897) _DBG message y 1
[12] 03/31/21 08:33:31.457612 T(34897) _DBG message y 2
                        test message line 2
                        test message line 3
                        test message line 4
[12] 03/31/21 08:33:32.56341 T(34897) _DBG message y 3
[12] 03/31/21 08:33:33.78123 T(34897) _DBG message y 3
                        test message line 2
[12] 03/31/21 08:33:34.56712 T(34897) _DBG message y 4
5
  • You shouldn't need to set doprint in BEGIN because all variables are automatically initialized to zero in awk. Mar 31, 2021 at 20:56
  • It's not necessary but I like to show the initialization explicitly so that people who may not know how awk (in this case) works may be more likely to intuit how it works rather than just copying a working "black box".
    – DopeGhoti
    Mar 31, 2021 at 22:47
  • Don't name a condition variable (doprint) based on the action you'll take when the condition is true (print the current line) as that tightly couples the condition to the action. Imagine your requirements change and now you need to print the opposite set of lines to the ones you're currently printing. Because you named your condition variable doprint you'd now have to change every line of code that references it as doprint would no longer make sense. If instead you named the condition variable found or similar then the only change you have to make is found==1 -> found==0 or similar
    – Ed Morton
    Mar 31, 2021 at 23:27
  • Alternatively, one could keep the tightly coupled name and simply change the predicate of the if statement which determines whether its value is truthy or falsy.
    – DopeGhoti
    Apr 1, 2021 at 13:50
  • Then your "else" statement would be a double negative as it'd represent the condition where it is NOT true that $4 is NOT equal to marker and of course double negatives should always be avoided (which is also one of the reasons to avoid using a single negative at all when you could use a positive).
    – Ed Morton
    Apr 1, 2021 at 15:38
0

We first define some helper variables to assist in the formulation of field-based regexes.

# definition of a unit space
_s_='[:space:]'

# regex for a space char and nonspace char
s="[$_s_]" S="[^$_s_]"

# a field is a run of nonspaces followed by a run of spaces
F="$S\{1,\}$s\{1,\}"

   id=34897

sed -ne ":top
  /^\[$S*$s$s*\($F\)\{2\}T($id)$s/{
    :nxt
      p;n
    /^$s/b nxt
    b top
  }
" file

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.