But it gives me both lines. (I have the "LC_ALL=C" workaround from a RedHat article, otherwise I get a core dump for free. )
What's wrong with me, uhm, with the regex?
And yes, it should be grep because the new regex will be part of an existing regex file which is used by a script in order to grep some gigabytes of data multiple times a day. I don't know if it would be faster with awk or sth like this. Any advice is appreciated!
I will get back to you tomorrow - it's late, I'm the last one in the office, sun is gone...
Best regards
Stephan
Additional informations:
LSB Version: :base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0-noarch
Distributor ID: RedHatEnterpriseServer
Description: Red Hat Enterprise Linux Server release 6.6 (Santiago)
Release: 6.6
Codename: Santiago
grep UserID1 example.txt | grep -v foobuzz
OR
awk '/UserID1/ && !/foobuzz/' example.txt
OR
awk '!/.*foobuzz.*UserID1/' example.txt
OR
and the list goes on and on....
I have a shell script which calls a grep command (and does some more postprocessing).
This grep uses a file that holds a bunch of regexes and one of the regexes should be able to do a negative lookbehind (if that's the correct term for the regex logic that I want to achieve). So a piped "grep -v" doesn't help me in this case.
And wouldn't it slow down the processing if I would refactor it to awk (which handles the file with the regexes)?
Could you please explain why it didn't work out with a simple "." instead of the more exact "[^|]*\|[^|]*\|[^|]*\|[^|]\|"? I'm not that familiar with the perl'ish version of grep.
Greedy matching is doing you in. All "(?!foobuzz)..." needs to find is one spot before user1 which doesn't start in 'f' and its condition is satisfied. The .* after it swallows everything. So it scans the first character, goes 'yay its not foobuzz', searches for the username and accepts.
So, you need to force the regex to check for (?!foobuzz) in a relevant spot. Right after a | character perhaps. And you have to make it check all of them so it won't cheat by skipping ahead.
You can match entire fields with [^|]*\| , and force them to not start with foobuzz via (?!foobuzz)[^|]*\| , match several in a row by wrapping it in ()* and force it to start at the beginning of the line with ^ .
So ^((?!foobuzz)[^|]*\|)*UserID1 will only accept a line containing zero or more | fields, none of which begin with foobuzz, after which it must immediately find 'userid1'.
I don't think rewriting it in awk or sed would necessarily make it slower, but if you have a big pile of regexes already, could be a painful amount of work.
---------- Post updated 04-10-15 at 04:56 PM ---------- Previous update was 04-09-15 at 07:23 PM ----------
D'oh, I cheered to early. The one-liner did the job, but now grep surprised me by saying "grep: the -P option only supports a single pattern". Ok, I will find a different solution by means of postprocessing.
Well, if you're rewriting from scratch anyway, you could have a file full of awk expressions, like:
# Do NOT print line if this regex matches
/regex1/ { next }
# print line if it contains this literal string. Faster than regex
index($0, "literalstring") { print ; next }
# print line if one regex matches and another does not
/regex1/ && !/regex2/ { print ; next }
# print line if any of these regexes match
/regex1/ || /regex2/ || /regex3/ || /regex4/ { print ; next }
etc..
You can use that file like
awk -f expressions.awk
with a filename optionally specified afterwards.
There is also a variant of awk that's further optimized for speed, mawk.
There is a code, peg , that may do what you desire: multiple patterns of PERLRE expressions. Using the pattern from disedorgue and an augmented data file, a demo is:
-e overloaded
-e PERLEXPR
Specify a PERLEXPR to match.
If used more than once, then it is equivalent to using -o. For
example, "peg -e foo -e bar baz", "peg -o foo bar -- baz", and
"peg "/foo/ or /bar/" baz" are all equivalent.