0

I want to print the "tmp and lines before until a line break", I'm a newbie to Unix.

EX: Input

AAA
CBH
VFh

GFD
DFC
VGF
HGD
TMP
JHG

TFD
PI1
98A
TMP
TMP
765
UBS

My desired output should be like this

GFD
DFC
VGF
HGD
TMP

TFD
PI1
98A
TMP
TMP
0
3

Using an awk:

$ awk -v RS='' -v ORS='\n\n' 'match($0,".*TMP") { print substr($0,1,RLENGTH) }' file
GFD
DFC
VGF
HGD
TMP

TFD
PI1
98A
TMP
TMP

This treats set of lines delimited by at least one empty line as a record. If a record matches the regular expression .*TMP, the bit after the match is removed and the rest of the record is printed.

The bit after the last occurrence of TMP is removed by matching the regular expression .*TMP against the current record, and then using substr() to print only the bit that matches that expression. The regular expression will match from the start of the record to the last TMP in it, and the match() function sets the variable RLENGTH to the length of that match.

You'll get a extra empty line at the end of the output since we're using a double newline as the output record separator.


If your file instead looks like

AAA
CBH
VFh

GFD
DFC
VGF
HGD
tmp/some/path/here
JHG

TFD
PI1
98A
tmp/some/path/here
tmp/some/path/here
765
UBS

... and you'd want to do the same sort of transformation based on the lines starting with tmp, then modify the regular expression used with match() in the command so that it matches all the way to the newline character at the end of the "tmp-line":

$ awk -v RS='' -v ORS='\n\n' 'match($0,".*tmp[^\n]*") { print substr($0,1,RLENGTH) }' file
GFD
DFC
VGF
HGD
tmp/some/path/here

TFD
PI1
98A
tmp/some/path/here
tmp/some/path/here

Note that I'm not 100% sure how awk is supposed to interpret \n inside a bracketed expression, but all the awk implementations that I have access to (OpenBSD awk, mawk, and GNE awk) seems to treat is as newline and not as the two separate characters \ and n.

5
  • Thanks Kusal it works,but how do we print the last record also,my output last line was truncated after tmp.
    – Renga
    Sep 4 '20 at 9:15
  • @RenganathanSeeralan I see no difference between my output and your desired output, except that I have an extra empty line at the end. If you want to remove that empty line at the very end, pipe the data through sed '$d'.
    – Kusalananda
    Sep 4 '20 at 9:21
  • My record will be like this. insert some date . . . . tmp/some path tmp/path... ... ... insert some date . . . . tmp/some path tmp/path... insert some date . . . . tmp/some path tmp/path... Output only print up to the word tmp..remains are truncated. EX Below:- nsert some date . . . . tmp/some path tmp insert some date . . . . tmp/some path tmp insert some date . . . . tmp/some path tmp
    – Renga
    Sep 4 '20 at 9:58
  • 2
    @RenganathanSeeralan Whenever asking a question here, make sure that the data that you show is representative of the real data. Having further text after TMP (or tmp) will make a huge difference to any of the answers here. It's not even obvious that it's pathnames that we're dealing with when reading your question...
    – Kusalananda
    Sep 4 '20 at 10:07
  • Thanks Kusal,Perfectly working..
    – Renga
    Sep 4 '20 at 15:50
1

Just inverse the text and do a normal print from regex to regex and then again re-inverse to get the original order

 tac < file.txt | sed -n '/TMP/,/^$/p' | tac
2
  • Thanks Zohaib,without tac i am trying,since my text file has more than lakhs of record.
    – Renga
    Sep 4 '20 at 5:52
  • I guess sed -n '/^$/,/TMP/p' would work in that case. Sep 4 '20 at 6:08
1

If you are okay with matching up to first TMP. Empty RS will result in paragraph mode, where two or more consecutive \n characters will be used as the record separator

$ # sub is used to remove everything after first occurrence of TMP
$ # return value of sub (0 if no match, 1 if match is found) determines
$ # if record should be printed or not
$ # use \nTMP\n to match only whole line
$ awk -v RS= 'sub(/TMP\n.*/, "TMP\n")' ip.txt
GFD
DFC
VGF
HGD
TMP

TFD
PI1
98A
TMP

If you need till the last TMP, you can do it with GNU awk (because of gensub) or perl

$ # use \nTMP\n to match only whole line
$ # same as: perl -00 -ne 'print if s/.*TMP\n\K.*/\n/s' ip.txt
$ awk -v RS= '/TMP/{print gensub(/(.*TMP\n).*/, "\\1", 1)}' ip.txt
GFD
DFC
VGF
HGD
TMP

TFD
PI1
98A
TMP
TMP
1
0

You had tagged with sed, and so we can do it as shewn. Note we are using GNU sed. As per my understanding you want to remove everything after the last line that begins with tmp (lowercase) in each paragraph of text file. A paragraph is an island of nonempty line(s) separated by atleast one empty line from the next instance.

sed -e '
  /./{H;$!d;}
  x;/\ntmp/!d
  :chop
  /\ntmp[^\n]*$/!s/\n[^\n]*$//
  t chop
' file

Accumulate a paragraph in the hold. When we hit a boundary (empty line or eof) we start to examine the para. In case no tmp at the beginning of newline is to be seen we promptly delete this para. Else, we start chopping off the lines from the end of para until we see the tmp line Stop processing this para and print it.

With GNU awk, we operate in tge paragraph mode and set the input filed separator to newline. Start examining the fields from the end. Soon as we see a field beginning with tmp We shrink the para upto that field and print and be done with this paragraph.

awk -F '\n' -v RS= -v OFS='\n' '
  /(^|\n)tmp/ {
    for(i=NF; i; i--) {
      if ($i ~ /^tmp/) {
        NF=i; NF++; print; break
      }
    }
  }
' file

rindex will tell the position in a string of a substring from the end. So get the posituon of the last tmp by rindex abd use this position to get the position of the next nearest newline to it's right.

perl -p00e '
  s/.*//s,next unless /^tmp/m;
  s/\z/\n/;
  my $p = 1+rindex($_, "\ntmp");
  my $q = 1+index($_, "\n", $p);
  substr($_, $q) = "\n";
' 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.