Radje
Radje

Reputation: 71

Find regular expression in a file matching a given value

I have some basic knowledge on using regular expressions with grep (bash). But I want to use regular expressions the other way around.

For example I have a file containing the following entries:

line_one=[0-3]
line_two=[4-6]
line_three=[7-9]

Now I want to use bash to figure out to which line a particular number matches. For example:

grep 8 file

should return:

line_three=[7-9]

Note: I am aware that the example of "grep 8 file" doesn't make sense, but I hope it helps to understand what I am trying to achieve.

Thanks for you help, Marcel

Upvotes: 2

Views: 321

Answers (6)

Walter A
Walter A

Reputation: 20002

You would like to do something like

grep -Ef <(cut -d= -f2 file) <(echo 8)

This wil grep what you want but will not display where. With grep you can show some message:

echo "8" | sed -n '/[7-9]/ s/.*/Found it in line_three/p' 

Now you would like to transfer your regexp file into such commands:

sed 's#\(.*\)=\(.*\)#/\2/ s/.*/Found at \1/p#' file

Store these commands in a virtual command file and you will have

echo "8" | sed -nf <(sed 's#\(.*\)=\(.*\)#/\2/ s/.*/Found at \1/p#' file) 

Upvotes: 0

dawg
dawg

Reputation: 103814

As I understand it, you are looking for a range that includes some value.

You can do this in gawk:

$ cat /tmp/file
line_one=[0-3]
line_two=[4-6]
line_three=[7-9]

$ awk  -v n=8 'match($0, /([0-9]+)-([0-9]+)/, a){ if (a[1]<n && a[2]>n) print $0 }' /tmp/file
line_three=[7-9]

Since the digits are being treated as numbers (vs a regex) it supports larger ranges:

$ cat /tmp/file
line_one=[0-3]
line_two=[4-6]
line_three=[75-95]
line_four=[55-105]

$ awk  -v n=92 'match($0, /([0-9]+)-([0-9]+)/, a){ if (a[1]<n && a[2]>n) print $0 }' /tmp/file
line_three=[75-95]
line_four=[55-105]

If you are just looking to interpret the right hand side of the = as a regex, you can do:

$ awk  -F= -v tgt=8 'tgt~$2' /tmp/file

Upvotes: 0

Charles Duffy
Charles Duffy

Reputation: 295383

This can be done in native bash using the syntax [[ $value =~ $regex ]] to test:

find_regex_matching() {
  local value=$1
  while IFS= read -r line; do         # read from input line-by-line
    [[ $line = *=* ]] || continue     # skip lines not containing an =
    regex=${line#*=}                  # prune everything before the = for the regex
    if [[ $value =~ $regex ]]; then   # test whether we match...
      printf '%s\n' "$line"           # ...and print if we do.
    fi
  done
}

...used as:

find_regex_matching 8 <file

...or, to test it with your sample input inline:

find_regex_matching 8 <<'EOF'
line_one=[0-3]
line_two=[4-6]
line_three=[7-9]
EOF

...which properly emits:

line_three=[7-9]

You could replace printf '%s\n' "$line" with printf '%s\n' "${line%%=*}" to print only the key (contents before the =), if so inclined. See the bash-hackers page on parameter expansion for a rundown on the syntax involved.

Upvotes: 1

tinkertime
tinkertime

Reputation: 3042

As others haven pointed out, awk is the right tool for this:

awk -F'=' '8~$2{print $0;}' file

... and if you want this tool to feel more like grep, a quick bash wrapper:

#!/bin/bash
awk -F'=' -v seek_value="$1" 'seek_value~$2{print $0;}' "$2"

Which would run like:

./not_exactly_grep.sh 8 file
line_three=[7-9]

Upvotes: 2

meta11co
meta11co

Reputation: 41

My first impression is that this is not a task for grep, maybe for awk.

Trying to do things with grep I only see this:

for line in $(cat file); do echo 8 | grep "${line#*=}" && echo "${line%=*}" ; done

Using while for file reading (following comments):

while IFS= read -r line; do echo 8 | grep "${line#*=}" && echo "${line%=*}" ; done < file

Upvotes: 1

user743382
user743382

Reputation:

This is not built-in functionality of grep, but it's easy to do with awk, with a change in syntax:

/[0-3]/ { print "line one" }
/[4-6]/ { print "line two" }
/[7-9]/ { print "line three" }

If you really need to, you could programmatically change your input file to this syntax, if it doesn't contain any characters that need escaping (mainly / in the regex or " in the string):

sed -e 's@\(.*\)=\(.*\)@/\2/ { print "\1" }@'

Upvotes: 0

Related Questions