Remove match and previous line
I need to remove the line containing "not dynamic executable" and the previous line from the stream using grep, awk, sed or whatever. My current working solution would be to flatten the whole stream to disable newlines, then replace the newline before my match with something else using sed, then use tr to add newlines, and then use grep -v ... I'm a little tired of artifacts with this approach, but I don't understand how else I can do this 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 non-library file output as it has nothing to do with what I am doing next. I didn't want to use the lib * .so mask as it could be different.
source to share
Most simple with pcregrep
in multi-line mode:
pcregrep -vM '\n\tnot a dynamic executable' filename
If pcregrep
not available to you, then awk
or sed
also can be done by reading one line forward and skipping printing previous lines when the marker line appears.
You can be boring (and sane) with awk:
awk '/^\tnot a dynamic executable/ { flag = 1; next } !flag && NR > 1 { print lastline; } { flag = 0; lastline = $0 } END { if(!flag) print }' filename
I.e:
/^\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 store it entirely 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.
source to share
Always remember that while grep and sed are line oriented, awk is write oriented and can therefore easily handle problems that span multiple lines.
You weren't supposed to post any sample input and expected output, but this seems like everything you need (using GNU awk for multi-char RS):
awk -v RS='^$' -v ORS= '{gsub(/[^\n]+\n\tnot a dynamic executable/,"")}1' file
source to share
This might work for you (GNU sed):
sed 'N;/\n.*not a dynamic executable/d;P;D' file
This keeps the moving window from two lines and removes them if the search line is in the second. If not the first line is printed, then it is removed and then the next line is added and the process repeats.
source to share