How to use lookahead to exclude special characters in a regular expression
Asked Answered
O

2

6

I have a regular expression that I'm using to try to detect screen resolution as follows

xrandr | grep -P '(.*\d+x\d+)* | awk {'print$3'}

which when I use in my script gives me something like this

Output

1920x1200+0+0
1920x1200+1920+0
primary
1920x1200+1920+1200

I tried using lookahead, but I'm not using it correctly I guess because I'm getting the same match.

Code

xrandr | grep -P '(.*\d+x\d+)*^(?![\+]+\d\+\d) | awk {'print$3'}

Can someone explain how lookahead works with special characters so I can fix this? I want to only return the screen resolution in the form below or some variation. What I'm trying to do is distinguish between dual and 4 monitor displays.

xrandr | grep -P '(.*\d+x\d+)*^(?![\+]+\d\+\d) | awk {'print$3'} | tr -d '\\n'

Expectation

1920x12001920x1200primary1920x1200

or this

1920x1200
1920x1200
primary
1920x1200

Bonus points if it can return just the screen resolution.

1920x1200
1920x1200
1920x1200
1920x1200

xrandr output

Mon0 connected 1920x1200+0+0 0mm x 0 mm
   1920x1200_c 59.95*
Mon0 connected 1920x1200+0+0 0mm x 0 mm
   1920x1200_c 59.95*
Mon0 connected primary 1920x1200+0+1200 0mm x 0 mm
   1920x1200_c 59.95*
Mon0 connected 1029x1200+1920+1200 0mm x 0 mm
   1920x1200_c 59.95*
Openminded answered 3/2, 2021 at 15:12 Comment(5)
Is this what you're looking for? xrandr | grep -P '.*\d+x\d+(?=\+\d+\+\d+)'Jungly
@anubhava Added to the question the output of just xrandrOpenminded
@johnmastroberti, No, that catches everything after the screen resolution. What I want is to exclude everything after the special character +Openminded
If you have had a chance of checking the below approaches, please share some feedback, which one worked best for you. If you need more clarifications or remarks regarding the solutions please feel free to drop a comment below the answer.Wagers
Either one of them worked for me. I opted for the simpler one because I was doing this in an ancient programming language, so the syntax has to be altered slightly to make it work even though I'm ultimately running the command natively in bash.Openminded
D
2

The (.*\d+x\d+)*^(?![\+]+\d\+\d) pattern matches and captures into Group 1 an optional sequence of any zero or more chars other than line break chars, as many as possible, then 1+ digits, x, 1+ digits, and then requires the start of string position (this makes the pattern fail all the time!) and makes sure there is no one or more + chars, a digit, a + and a digit immediately to the right of the current location. This looks like a very corrupt pattern that never matches any string.

Your logic can be implemented as

xandr | grep -oP '\b\d+x\d+(?!(?:\+\d+\+)?\d)'

See the online demo and the regex demo.

Details:

  • -oP - output matches only and enable PCRE regex engine
  • \b\d+x\d+(?!(?:\+\d+\+)?\d):
    • \b - word boundary
    • \d+ - one or more digits
    • x - an x
    • \d+ - one or more digits
    • (?!(?:\+\d+\+)?\d) - a negative lookahead that fails the match if, immediately to the right of the current location, there is
    • (?:\+\d+\+)? - an optional sequence of +, one or more digits and +
    • \d - a digit.
Darendaresay answered 3/2, 2021 at 16:58 Comment(0)
A
3

You can skip grep and get this done in a single awk:

xrandr | awk 'NF >= 3 && /[0-9]+x[0-9]+/ {sub(/[+-].*/, "", $3); print $3}'

1920x1200
1920x1200
primary
1029x1200

Alternative single gnu grep solution:

xrandr | grep -oP 'connected\h+\K(\d+x\d+|\S+)'
Albuminate answered 3/2, 2021 at 15:31 Comment(0)
D
2

The (.*\d+x\d+)*^(?![\+]+\d\+\d) pattern matches and captures into Group 1 an optional sequence of any zero or more chars other than line break chars, as many as possible, then 1+ digits, x, 1+ digits, and then requires the start of string position (this makes the pattern fail all the time!) and makes sure there is no one or more + chars, a digit, a + and a digit immediately to the right of the current location. This looks like a very corrupt pattern that never matches any string.

Your logic can be implemented as

xandr | grep -oP '\b\d+x\d+(?!(?:\+\d+\+)?\d)'

See the online demo and the regex demo.

Details:

  • -oP - output matches only and enable PCRE regex engine
  • \b\d+x\d+(?!(?:\+\d+\+)?\d):
    • \b - word boundary
    • \d+ - one or more digits
    • x - an x
    • \d+ - one or more digits
    • (?!(?:\+\d+\+)?\d) - a negative lookahead that fails the match if, immediately to the right of the current location, there is
    • (?:\+\d+\+)? - an optional sequence of +, one or more digits and +
    • \d - a digit.
Darendaresay answered 3/2, 2021 at 16:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.