Remove matching and previous line

霸气de小男生 提交于 2019-12-07 19:05:37

问题


I need to remove a line containing "not a dynamic executable" and a previous line from a stream using grep, awk, sed or something other. My current working solution would be to tr the entire stream to strip off newlines then replace the newline preceding my match with something else using sed then use tr to add the newlines back in and then use grep -v. I'm somewhat weary of artifacts with this approach, but I don't see how else I can to it at the moment:

tr '\n' '|' | sed 's/|\tnot a dynamic executable/__MY_REMOVE/g' | tr '|' '\n'

EDIT:

Input is a list of mixed files piped to xargs ldd, basically I want to ignore all output about non library files since that has nothing to do with what I'm doing next. I didn't want to use lib*.so mask since that could concievably be different


回答1:


Most simply with pcregrep in multi-line mode:

pcregrep -vM '\n\tnot a dynamic executable' filename

If pcregrep is not available to you, then awk or sed can also do this by reading one line ahead and skipping the printing of previous lines when a marker line appears.

You could be boring (and sensible) with awk:

awk '/^\tnot a dynamic executable/ { flag = 1; next } !flag && NR > 1 { print lastline; } { flag = 0; lastline = $0 } END { if(!flag) print }' filename

That is:

/^\tnot a dynamic executable/ {  # in lines that start with the marker
  flag = 1                       # set a flag
  next                           # and do nothing (do not print the last line)
}
!flag && NR > 1 {                # if the last line was not flagged and
                                 # is not the first line
  print lastline                 # print it
}
{                                # and if you got this far,
  flag = 0                       # unset the flag
  lastline = $0                  # and remember the line to be possibly
                                 # printed.
}
END {                            # in the end
  if(!flag) print                # print the last line if it was not flagged
}

But sed is fun:

sed ':a; $! { N; /\n\tnot a dynamic executable/ d; P; s/.*\n//; ba }' filename

Explanation:

:a                                  # jump label

$! {                                # unless we reached the end of the input:

  N                                 # fetch the next line, append it

  /\n\tnot a dynamic executable/ d  # if the result contains a newline followed
                                    # by "\tnot a dynamic executable", discard
                                    # the pattern space and start at the top
                                    # with the next line. This effectively
                                    # removes the matching line and the one
                                    # before it from the output.

                                    # Otherwise:
  P                                 # print the pattern space up to the newline
  s/.*\n//                          # remove the stuff we just printed from
                                    # the pattern space, so that only the
                                    # second line is in it

  ba                                # and go to a
}
                                    # and at the end, drop off here to print
                                    # the last line (unless it was discarded).

Or, if the file is small enough to be completely stored in memory:

sed ':a $!{N;ba}; s/[^\n]*\n\tnot a dynamic executable[^\n]*\n//g' filename

Where

:a $!{ N; ba }                                  # read the whole file into
                                                # the pattern space
s/[^\n]*\n\tnot a dynamic executable[^\n]*\n//g # and cut out the offending bit.



回答2:


This might work for you (GNU sed):

sed 'N;/\n.*not a dynamic executable/d;P;D' file

This keeps a moving window of 2 lines and deletes them both if the desired string is found in the second. If not the first line is printed and then deleted and then next line appended and the process repeated.




回答3:


Always keep in mind that while grep and sed are line-oriented awk is record-oriented and so can easily handle problems that span multiple lines.

It's a guess given you didn't post any sample input and expected output but it sounds like all you need is (using GNU awk for multi-char RS):

awk -v RS='^$' -v ORS= '{gsub(/[^\n]+\n\tnot a dynamic executable/,"")}1' file


来源:https://stackoverflow.com/questions/28566616/remove-matching-and-previous-line

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!