Blooze
Blooze

Reputation: 2257

Grabbing value ranges with regex

I'm trying to write two awk commands to check a file to meet both conditions.

  1. The first condition is that it will output any line that has 'deny=n' where n is either 0 or is greater than 10, and ignore commented out lines.

Example:

test deny=1
test2 deny=0
test deny=4
#test=3 deny=0
test deny=44

I want it to display:

test deny=44
test2 deny=0

Here is my current regex that doesn't appear to be outputting that correctly:

awk '$1 ~ /[^#]*/ && ! /deny=([1-9]|10)/' /etc/pam.d/system-auth
  1. My second awk I'd like to only display the line where deny=n where n is anything between 1-10.

Here is my current line for this output

awk '$1 ~ /[^#]*/ &&  /deny=1[1-9]/ &&  /deny=[2-9][0-9]/' /etc/pam.d/system-auth

Any thoughts on how to improve these? I wanted to see how this works in shell/bash before trying to port it to ruby.

Upvotes: 1

Views: 73

Answers (3)

Ed Morton
Ed Morton

Reputation: 203334

It always surprises me when people say they need a numeric comparison and then look for a way to cludge it with regexps. If you want to compare numbers, use a numeric comparison:

$ awk 'match($0,/^[^#]*deny=([0-9]+)/,a) && (a[1]==0 || a[1]>10)' file
test2 deny=0
test deny=44

$ awk 'match($0,/^[^#]*deny=([0-9]+)/,a) && (a[1]>=1 && a[1]<=10)' file
test deny=1
test deny=4

The above uses GNU awk for the 3rd arg to match(), with other awks it'd be substr() or sub() and a variable, e.g.:

$ awk '{n=$0} sub(/^[^#]*deny=/,"",n) && (n+0==0 || n+0>10)' file
test2 deny=0
test deny=44

$ awk '{n=$0} sub(/^[^#]*deny=/,"",n) && (n+0>=1 && n+0<=10)' file
test deny=1
test deny=4

Note that you need to add zero in the non-gawk version since the first operation we do on n is a string one (sub()) so at that point awk "knows" that n contains a string (despite it "looking like" a number which is why the gawk version doesn't require this special treatment) and so to use it as a number afterwards we need to explicitly cast it to a number by doing a numeric operation (+) on it.

Upvotes: 1

anubhava
anubhava

Reputation: 785068

You can use this awk command with custom field separator:

awk -F '[= \t]+' '!/^[ \t]*#/ && $2 == "deny" && ($3==0 || $3>10)' file

test2 deny=0
test deny=44
  • -F '[= \t]+' sets input field separator as = or a space or a tab
  • !/^[ \t]*#/ will match anyline that is not commented

EDIT:

As per comments below if deny can appear anywhere in input then use:

grep -E '^([[:blank:]]*[^#].*)?deny=(0\b|1[1-9]|[2-9][0-9])' file

test2 deny=0
test deny=44

(0\b|1[1-9]|[2-9][0-9]) will match either 0 or any 2 digit number greater than 10.

Upvotes: 1

glenn jackman
glenn jackman

Reputation: 246774

  1. the string deny=44 DOES match the regex /deny=([1-9]|10)/ -- you need a word anchor there: /deny=([1-9]|10)\>/
  2. the string #test=3 DOES match the regex /[^#]*/ because there are zero or more non-"#" characters

You want

awk '$1 !~ /^#/ && ! /deny=([1-9]|10)\>/'

Looks like you can benefit from a regular expression tutorial online. See https://stackoverflow.com/tags/regex/info

Upvotes: 0

Related Questions