3

I need to extract (or count) the lines (in a file) that have two or more dots. The lines should not start with dot (it’s OK if they end with a dot), and there must not be two dots in a row (i.e., the dots are all separated with non-dot characters).

Output Example:

a.b.
a.b.com
a.b.c.
a.b.c.com

But not:

a.com
a..b
a.b.c..d

I did this command:

grep -P '^[^.]+\.([^.]+\.)+[.]+' file.txt | wc -l

but it didn't find any matching lines.  How should I do this?

  • @Jeff Schaller I do not worry. If you mean the [.]+ what I intend is to say followed b any character whether dot or other. The . in RegEx refers to any character. Am I wrong? Can you clarify which part you are talking about? – user9371654 Apr 21 at 20:09
3
  1. \. and [.] are equivalent — they both match a literal dot, and not any other character.  As a matter of style, pick one and use it consistently.
  2. Your problem is that your regular expression (i.e., pattern) has ([^.]+\.)+ followed by [.]+.  That’s really (sort of) equivalent to [^.]+\. followed by [.], with the result that your grep is looking for lines that contain text.text.., i.e., two dots in a row.  If you check, you’ll see that your command matches a.b...
  3. OK, I believe that the fix is fairly simple:
    grep -P '^[^.]+\.([^.]+\.)+[^.]*$'
    I.e., change the [.] to [^.] (perhaps that’s what you meant originally?), change the following + to an *, and add a $.  After some number of text. groups, require/allow any number (zero or more) characters other than dot, up to the end of the line.
  4. An even simpler approach (easier to understand) would be
    grep -P '^[^.]+\..*\.' file.txt | grep -v '\.\.'
    The first grep finds lines that begin with a non-dot character and include at least two dots.  The second grep removes lines that have two consecutive dots.
  5. Rather than do grep … | wc -l, just do grep -c ….
  • a.b.c. is possible but not a.b.. or a.b.c.. i.e., no consequent dots comes. Plz, try to do the minimum changes to correct my command rather than coming up with completely new way that needs me to parse and search and understand what it does. – user9371654 Apr 21 at 20:20
  • 1
    It would probably be enough to use -E here as your regular expressions don't seem to use PCRE expressions. – Kusalananda Apr 22 at 16:46
1

Using awk:

$ cat file
.com
.c.c.c.c
a.b.
a.b.com
a.b.c.
a.b.c.com
a.com
a..b
a.b.c..d
$ awk -F . 'NF > 2 && !/^\./ && !/\.\./' file
a.b.
a.b.com
a.b.c.
a.b.c.com

The awk program here uses the dot as a field separator. A line having two or more dots is the same as a line having more than two fields. This is what the NF > 2 test tests. The first regular expression discards lines that starts with a dot, and the second regular expression discards lines that contain two dots or more in a row. The rest of the lines are printed.

The same thing with grep:

grep '\..*\.' file | grep -v -e '^\.' -e '\.\.'

The first expression extracts lines that contain at least two dots, and the two others delete line that start with a dot or contains two consecutive dots.

Or with sed,

sed -n '/^\./d; /\.\./d; /\..*\./p' file
0

You can do this with lookarounds, like as shown:

$ grep -Pc '^(?!\.)(?!.*\.\..*)(?=.*\..*\.)' file.txt

To be read as:

  • I am standing at the beginning of the line and looking to my right ^
  • I see the line not begin with a literal dot (?!\.)
  • On looking further ahead nowhere do I see two consecutive literal dots (?!.*\.\..*)
  • But I do see two dots, but since consecutive dots were ruled out in my previous lookahead, that means these two dots MUST be separated by at least one non-dot character (?=.*\..*\.)
  • Q.E.D.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.