I have some log files that I'm grepping through which contain entries in the following form
foo($abc) - sometext
foo ($xyz) - moretext
baz($qux) - moartext
I'm looking to use grep that would output the first two lines as matches, i.e.
foo($abc)
foo ($xyz)
I've tried the following grep statement
grep 'foo(\$' log.txt
which outputs the first match, but I tried to include an optional space, and neither return:
grep 'foo\s?(\$' log.txt
I'm using the optional space incorrectly, but I'm unsure how
You are using a POSIX BRE regex and foo\s?(\$ matches foo, a whitespace, a literal ?, a literal ( and a literal $.
You can use
grep -E 'foo\s?\(\$' log.txt
Here, -E makes the pattern POSIX ERE, and thus it now matches foo, then an optional whitespace, and a ($ substring.
See an online demo:
s='foo($abc) - sometext
foo ($xyz) - moretext
baz($qux) - moartext'
grep -E 'foo\s?\(\$' <<< "$s"
Output:
foo($abc) - sometext
foo ($xyz) - moretext
You may still use a more universal syntax like
grep 'foo[[:space:]]\{0,1\}(\$' log.txt
It is a POSIX BRE regex matching foo, one or zero whitespaces, and then ($ substring.
You can either change the query slightly and use * instead of ?:
grep 'foo *(\$' log.txt
or use a literal whitespace and escape ?:
grep 'foo \?(\$' log.txt
Both solutions would work with GNU, busybox and FreeBSD grep.
Related
I got .txt file with city names, each in separate line. Some of them are few words with one or multiple spaces or words connected with '-'. I need to create bash command which will echo those lines out. Currently I'm using cat piped with grep but I can't get both spaces and dash into one search and I had problems with checking for multiple spaces.
print lines with dash:
cat file.txt | grep ".*-.*"
print lines with spaces:
cat file.txt | grep ".*\s.*"
tho when I try to do:
cat file.txt | grep ".*\s+.*"
I get nothing.
Thanks for help
Something like that should work:
grep -E -- ' |\-' file.txt
Explanation:
-E: to interpret patterns as extended regular expressions
--: to signify the end of command options
' |\-': the line contains either a space or a dash
This does not directly address your question, but is too much to put in a comment.
You don't need the .* in your patterns. .* at the beginning or end of a pattern is useless, because it means "0 or more of any character" and so will always match.
These lines are all identical:
cat file.txt | grep ".*-.*"
cat file.txt | grep "-.*"
cat file.txt | grep "-"
Plus you don't need to cat and pipe:
grep "-" file.txt
When grep pattern matches, the default action is to print the whole line, so .* in all your patterns are redundant, you may delete them. Also, you don't have to use cat file | as you may specify the file to grep directly after pattern, i.e. grep 'pattern' file.txt.
Here are some more details:
grep ".*-.*" = grep -- "-" - returns any lines having a - char (-- singals the end of options, the next thing is the pattern)
grep ".*\s.*" = grep "\s" - matches and returns lines containing a whitespace char (only GNU grep)
grep ".*\s+.*" = grep "\s+" - returns line containing a whitespace followed with a literal + char (since you are using POSIX BRE regex here the unescaped + matches a literal plus symbol).
You want
grep "[[:space:]-]" file.txt
See the online demo:
#!/bin/bash
s='abc - def
ghi
jkl mno'
grep '[[:space:]-]' <<< "$s"
Output:
abc - def
jkl mno
The [[:space:]-] POSIX BRE and ERE (enabled with -E option) compliant pattern matches either any whitespace (with the [:space:] POSIX character class) or a hyphen.
Note that [\s-] won't work since \s inside a bracket expression is not treated as a regex escape sequence but as a mere \ or s.
My hostname details are as below after showing command of hostname in linux
my-host-test-db-10001.dns.biz.xyz.com
my-host-test2-db-10002.dns.biz.xyz.com
my-host-test3-db-10003.dns.biz.xyz.com
I want to fetch the 3rd string from these above (test/test2/test3). how can I achieve it?
In addition to the simpler solution using cut, you can use more flexible grep:
hostname | grep -Po '^[^-]+-[^-]+-\K[^-]+'
For example:
grep -Po '^[^-]+-[^-]+-\K[^-]+' <<< 'my-host-test2-db-10002.dns.biz.xyz.com'
Output:
test2
Here, GNU grep uses the following options:
-P : Use Perl regexes.
-o : Print the matches only (1 match per line), not the entire lines.
\K : Cause the regex engine to "keep" everything it had matched prior to the \K and not include it in the match. Specifically, ignore the preceding part of the regex when printing the match.
SEE ALSO:
perlre - Perl regular expressions
I am trying to extract the version from a colon delimited list. The value I want is for foo, however there is another value in the list called foo-bar causing both values to return. This is what I am doing:
LIST="foo:1.0.0
foo-bar:1.0.1"
VERSION=$(echo "${LIST}" | grep "\bfoo\b" | cut -s -d':' -f2)
echo -e "VERSION: ${VERSION}"
Output:
VERSION: 1.0.0
1.0.1
NOTE: Sometimes LIST will look like the following, which should result in version being empty (this is expected).
LIST="foo
foo-bar:1.0.1"
You may use a PCRE regex enabled with -P option and use a (?!-) negative lookahead that will fail the match in case there is a - after a whole word foo:
grep -P "\bfoo\b(?!-)"
See online demo
This regex should extract any number and optional dots at the end of each line. If the line ends with a colon, then it won't match.
grep -oE '(([[:digit:]]+[.]*)+)$
I want to match tags in files (with optional brackets) ... easy one would think ... the regex is something like ^\[?MyTag\]?. But ... Grep doesn't like it. None of the lines that would be valid matches are actually matched.
The interesting part is: if I replace the ? with a * (so zero to infinite matches, not zero or one) it matches everything like it should, but really that would mean the feature is broken and I don't believe that.
Any input?
Using grep (GNU grep) 2.22 on Windows.
PS: so grep is like this ...
grep -e "^\[?MyTag\]?" file.txt
and my test file is like this
[MyTag] hello
NotMyTag ugly
[NotMyTag] dumb
MyTag world
which obviously should result in 1st and 4th line showing but shows nothing.
First off, ? is not supported in vanilla grep, so you need to use the -E flag to enable extended regex. You can easily verify this by running grep '?' <<< 'a' and grep -E '?' <<< 'a'. Only the latter will match. -e just explicitly indicates what your regex is. It is not the same as -E.
Your initial command works fine if you change the -e to upper case:
grep -E '^\[?MyTag\]?'
Example:
$ grep -E '^\[?MyTag\]?' <<< '[MyTag] hello
> NotMyTag ugly
> [NotMyTag] dumb
> MyTag world'
Output:
[MyTag] hello
MyTag world
Credit goes to the answers of this question on SuperUser.
? is not part of the basic regular expressions, which grep supports. GNU grep supports them as an extension, but you have to escape them:
$ grep '^\[\?MyTag\]\?' file.txt
[MyTag] hello
MyTag world
Or, as pointed out, use grep -E to enable extended regular expressions.
For GNU grep, the only difference between grep and grep -E, i.e., using basic and extended regular expressions, is what you have to escape and what not.
Basic regular expressions
Capture groups and quantifying have to be escaped: \( \) and \{ \}
Zero or one (?), one or more (+) and alternation (|) are not part of BRE, but supported by GNU grep as an extension (but need to be escaped: \? \+ \|)
Extended regular expressions
Capture groups and quantifying don't have to be escaped: ( ) and { }
?, + and | are supported and don't need be be escaped
I'm loosing something here. If I have a file with contents:
foo
<foo>
And I do grep "\<foo\>" file_name shouldn't it match only the second line? I'm also matching the first.
I'm not very good with grep so I'm probably messing things up.
Escaping them activates their meta-character properties and turns them into word boundaries in GNU grep:
$ grep 'foo' file
foo
<foo>
foobar
$ grep '\<foo\>' file
foo
<foo>
The 2nd grep above isn't looking for the string <foo>, it's looking for the string foo NOT preceded or succeeded immediately by word-constituent characters.
In general it's not safe to escape characters without knowing exactly what it means to do so. Here's another example:
$ printf 'aa\na{2}b\n'
aa
a{2}b
$ printf 'aa\na{2}b\n' | grep 'a{2}'
a{2}b
$ printf 'aa\na{2}b\n' | grep 'a\{2\}'
aa
The above \{..\} is activating their meta character properites as regexp interval delimiters.