How to use regular expression in Linux to output number? - regex

How do you output only the number of words in /usr/share/dict/words that begin with any letter, let's say j?
I was hoping to use egrep 'J*' /usr/share/dict/words, but does not work well.

If your words are one on each line, then your solution is very close.
grep -ci '^j' /usr/share/dict/words
The ^ symbol means "start of line". -i flag means case insensitive search, -c means only report the count.

Related

Why do these two grep commands produce different results?

$ grep "^底线$" query_20220922 | wc -l
95701
$ grep -iF "底线" query_20220922 | wc -l
796591
Shouldn't the count be exactly the same? I want to count the exact match of the string.
-F matches a fixed string anywhere in a line. ^xyz$ matches lines which contain "xyz" exactly (nothing else).
You are looking for -x/--line-regexp and not -F/--fixed-strings.
To match lines which contain your search text exactly, without anything else and without interpreting your search text as regular expression, combine the two flags: grep -xF 'findme' file.txt.
Also, case-insensitive matching (-i) can match more lines too than case-sensitive matching (the default).
No, they do different things. The first uses a regular expression to search for "底线" alone on an input line (^ in a regular expression means beginning of line, and $ means end of line).
The second searches for the string anywhere on an input line. The -i flag does nothing at all here (it selects case-insensitive matching, but this is not well-defined for CJK character sets, so basically a no-op) and -F says to search literally (which makes the search faster for internal reasons, but doesn't change the semantics of a search string which doesn't contain any regex metacharacters).
It should be easy to see how they differ. For a large input file, it might be a bit challenging to find the differences if they are not conveniently mixed; but for a quick start, try
diff -u <(grep -m5 "^底线$" query_20220922) <(grep -m5Fi "底线" query_20220922)
where -m5 picks out the first five matches. (Try a different range, perhaps with tail, if the differences are all near the end of the file, for example.)
Tangentially, you usually want to replace the pipe to wc -l with grep -c; also,you might want to try grep -Fx "底线" as a faster alternative to the first search.

Can I perform a 'non-global' grep and capture only the first match found for each line of input?

I understand that what I'm asking can be accomplished using awk or sed, I'm asking here how to do this using GREP.
Given the following input:
.bash_profile
.config/ranger/bookmarks
.oh-my-zsh/README.md
I want to use GREP to get:
.bash_profile
.config/
.oh-my-zsh/
Currently I'm trying
grep -Po '([^/]*[/]?){1}'
Which results in output:
.bash_profile
.config/
ranger/
bookmarks
.oh-my-zsh/
README.md
Is there some simple way to use GREP to only get the first matched string on each line?
I think you can grep non / letters like:
grep -Eo '^[^/]+'
On another SO site there is another similar question with solution.
You don't need grep for this at all.
cut -d / -f 1
The -o option says to print every substring which matches your pattern, instead of printing each matching line. Your current pattern matches every string which doesn't contain slashes (optionally including a trailing slash); but it's easy to switch to one which only matches this pattern at the beginning of a line.
grep -o '^[^/]*' file
Notice the addition of the ^ beginning of line anchor, and the omission of the -P option (which you were not really using anyway) as well as the silly beginner error {1}.
(I should add that plain grep doesn't support parentheses or repetitions; grep -E would support these constructs just fine, of you could switch to toe POSIX BRE variation which requires a backslash to use round or curly parentheses as metacharacters. You can probably ignore these details and just use grep -E everywhere unless you really need the features of grep -P, though also be aware that -P is not portable.)

Bash Regex: Search for a maximum of 3 consecutive vowels

I am trying to Search for a maximum of 3 consecutive vowels
I tried
grep -E "([AEIOUaeiou]{3})" gpl3.txt
and got the results
What I want is to NOT get the (aaaaaaaaa) that you see in the first line of output. All other output is correct.
Any help is appreciated
If you want to avoid the -P option and lookaheads, you can use something like the following.
grep -iE '(^|[^aeiou])[aeiou]{3}([^aeiou]|$)' gpl3.txt
It just matches
the start of the line or a non-vowel
three vowels
a non-vowel or the end of the line
A test run:
IT070137 ~/tmp $ cat gpl3.txt
aaaaaaaaaaaaaaa
asdaiosd
aa
aaa
aaaa
this is a righteous queue
IT070137 ~/tmp $ grep -E '(^|[^aeiou])[aeiou]{3}([^aeiou]|$)' gpl3.txt
asdaiosd
aaa
this is a righteous queue
If you want to find all occurrences of exactly three vowels (no more, no less), then you can try this pattern:
grep -iP '(?<![aeiou])[aeiou]{3}(?![aeiou])'
Using option -P makes grep use the Perl library for regular expressions which is more feature-rich than the standard regexp library. For instance, it knows the patterns (?<!something) (?!something) which mean "must not be preceded by something" and "must not be followed by something", respectively. Using this I express the following:
»Find stuff which is three vowels long and not preceded by a vowel and not followed by a vowel.« This is another way of saying »exactly three vowels long«.
Concerning portability: Using this you need to use a grep which is capable of using Perl regular expressions. Today I guess this won't be an issue but if you happen to code for historical machines, you need to check this first.
Try using a negative lookahead which asserts that four or more vowels do not appear consecutively:
grep -P "^(?!.*[AEIOUaeiou]{4,}).*$" gpl3.txt
We need to run this in Perl mode to use negative lookaheads.
Demo

Grep for lines not beginning with "//"

I'm trying but failing to write a regex to grep for lines that do not begin with "//" (i.e. C++-style comments). I'm aware of the "grep -v" option, but I am trying to learn how to pull this off with regex alone.
I've searched and found various answers on grepping for lines that don't begin with a character, and even one on how to grep for lines that don't begin with a string, but I'm unable to adapt those answers to my case, and I don't understand what my error is.
> cat bar.txt
hello
//world
> cat bar.txt | grep "(?!\/\/)"
-bash: !\/\/: event not found
I'm not sure what this "event not found" is about. One of the answers I found used paren-question mark-exclamation-string-paren, which I've done here, and which still fails.
> cat bar.txt | grep "^[^\/\/].+"
(no output)
Another answer I found used a caret within square brackets and explained that this syntax meant "search for the absence of what's in the square brackets (other than the caret). I think the ".+" means "one or more of anything", but I'm not sure if that's correct and if it is correct, what distinguishes it from ".*"
In a nutshell: how can I construct a regex to pass to grep to search for lines that do not begin with "//" ?
To be even more specific, I'm trying to search for lines that have "#include" that are not preceeded by "//".
Thank you.
The first line tells you that the problem is from bash (your shell). Bash finds the ! and attempts to inject into your command the last you entered that begins with \/\/. To avoid this you need to escape the ! or use single quotes. For an example of !, try !cat, it will execute the last command beginning with cat that you entered.
You don't need to escape /, it has no special meaning in regular expressions. You also don't need to write a complicated regular expression to invert a match. Rather, just supply the -v argument to grep. Most of the time simple is better. And you also don't need to cat the file to grep. Just give grep the file name. eg.
grep -v "^//" bar.txt | grep "#include"
If you're really hungup on using regular expressions then a simple one would look like (match start of string ^, any number of white space [[:space:]]*, exactly two backslashes /{2}, any number of any characters .*, followed by #include):
grep -E "^[[:space:]]*/{2}.*#include" bar.txt
You're using negative lookahead which is PCRE feature and requires -P option
Your negative lookahead won't work without start anchor
This will of course require gnu-grep.
You must use single quotes to use ! in your regex otherwise history expansion is attempted with the text after ! in your regex, the reason of !\/\/: event not found error.
So you can use:
grep -P '^(?!\h*//)' file
hello
\h matches 0 or more horizontal whitespace.
Without -P or non-gnu grep you can use grep -v:
grep -v '^[[:blank:]]*//' file
hello
To find #include lines that are not preceded by // (or /* …), you can use:
grep '^[[:space:]]*#[[:space:]]*include[[:space:]]*["<]'
The regex looks for start of line, optional spaces, #, optional spaces, include, optional spaces and either " or <. It will find all #include lines except lines such as #include MACRO_NAME, which are legitimate but rare, and screwball cases such as:
#/*comment*/include/*comment*/<stdio.h>
#\
include\
<stdio.h>
If you have to deal with software containing such notations, (a) you have my sympathy and (b) fix the code to a more orthodox style before hunting the #include lines. It will pick up false positives such as:
/* Do not include this:
#include <does-not-exist.h>
*/
You could omit the final [[:space:]]*["<] with minimal chance of confusion, which will then pick up the macro name variant.
To find lines that do not start with a double slash, use -v (to invert the match) and '^//' to look for slashes at the start of a line:
grep -v '^//'
You have to use the -P (perl) option:
cat bar.txt | grep -P '(?!//)'
For the lines not beginning with "//", you could use (^[^/]{2}.*$).
If you don't like grep -v for this then you could just use awk:
awk '!/^\/\//' file
Since awk supports compound conditions instead of just regexps, it's often easier to specify what you want to match with awk than grep, e.g. to search for a and b in any order with grep:
grep -E 'a.*b|b.*a`
while with awk:
awk '/a/ && /b/'

Grep regex to unscramble a word

I want to unscramble a word using the grep command.
I am using below code. I know there are other ways to do it, but I think I'm missing something here:
grep "^[yxusonlia]\{9\}$" /usr/share/dict/words
should produce one output:
anxiously
but it produces:
annulosan
innoxious
and many more. Basically I can't find how I should specify that characters
can only be matched once, so that I get only one output.
I apologise if it seems very simple but I tried a lot and can't find anything.
You can use grep -P (PCRE regex) with negative lookahead
grep -P '^(?:([yxusonlia])(?!.*?\1)){9}$' /usr/share/dict/words
anxiously
Explanation:
This grep regex uses negative lookahead (?!.*?\1) for each character matched by group #1 i.e. \1. Each character is matched only and only when it is not followed by the same character again in the string till the end.
You can use lookaheads to make sure that each letter is matched exactly one time. It is verbose and requires a version of grep that supports lookaheads (e.g. via -P). It may be better to build the search string programmatically.
grep -P "^(?=.*y)(?=.*x)(?=.*u)(?=.*s)(?=.*o)(?=.*n)(?=.*l)(?=.*i)(?=.*a)[yxusonlia]{9}$" /usr/share/dict/words