4

I have a file (search.patterns) with a list of patterns to be searched into a list of other txt files.

search.patterns

home
dog 
cat

file 1.txt

home 3
tiger 4
lion 1

file 2.txt

dolphin 6
jaguar 3
dog 1

file 3.txt

donkey 3
cat 4
horse 1

so I want the first line of the pattern file to be searched in the file1, the second line searched in the file2 and the third line in file3

Output:

home 3
dog 1
cat 4

I have written some code like this:

for f in *.txt;
    do 
    while IFS= read -r LINE; 
        do grep -f "$LINE" "$f" > "$f.out"
    done < search.patterns
done

However, the output files are empty

Any help, highly appreciated,thanks

3
  • Are the files numbered like file1.txt, file2.txt or you just assumed them?
    – Prvt_Yadav
    Commented Oct 1, 2019 at 11:05
  • @msp9011 actually would be better to have only one joined file output, otherwise I can join later with cat command Commented Oct 1, 2019 at 11:08
  • @msp9011 the given data does not have home here but in my data there could be Commented Oct 1, 2019 at 11:23

3 Answers 3

6

With GNU awk (gawk) you could use a BEGINFILE rule to read a new pattern each time the input file changes:

$ gawk 'BEGINFILE{getline pat < "search.patterns"} $0 ~ pat' file\ {1..3}.txt
home 3
dog 1
cat 4

You should really check that getline returns a new pattern, for example

gawk '
  BEGINFILE {
    if((getline pat < "search.patterns") <= 0) {
      print "Error reading pattern" > "/dev/stderr"
      exit 1
    }
  } 
  $0 ~ pat
' file\ {1..3}.txt

Note that awk patterns are extended regular expressions, similar to those supported by grep with the -E option.

You could achieve the same in non-GNU awk by passing search.patterns as the first file and using NR and FNR appropriately to either read the patterns into an indexed array, or look up the next pattern in the array.

5

Using bash:

#!/bin/bash

files=( 'file 1.txt' 'file 2.txt' 'file 3.txt' )

while IFS= read -r pattern; do
    grep -e "$pattern" "${files[0]}"
    files=( "${files[@]:1}" )
done <search.patterns

Testing it:

$ bash script.sh
home 3
dog 1
cat 4

The script saves the relevant filenames in the files array, and then proceeds to read patterns from the search.patterns file. For each pattern, the first file in the files list is queried. The processed file is then deleted from the files list (yielding a new first filename in the list).

If the number of patterns exceeds the number of files in files, there will be errors from grep.

5
  • is it possible to write files=(file*.txt) as before? because I have 50 files and I cannot type each file name Commented Oct 1, 2019 at 11:19
  • by the way, does not seems to work... Commented Oct 1, 2019 at 11:20
  • actually, example of my file name is 5pop.chr1.20000_new.1.tree.txt 5pop.chr1.20000_new.2.tree.txt till 50...then there could be 5pop.chr1.30000_new.1.tree.txt 5pop.chr1.30000_new.2.tree.txt till 50, so I have m any files Commented Oct 1, 2019 at 11:30
  • files=(*.txt) echo "${files[@]}" while IFS= read -r pattern; do grep -e "$pattern" "${files[0]}" files=( "${files[@]:1}" ) done <search.patterns Commented Oct 1, 2019 at 11:30
  • the last code works Commented Oct 1, 2019 at 11:30
3

You could use paste to match the pattern with the file:

paste <(printf "%s\n" *.txt) search.patterns | while IFS=$'\t' read -r file pattern; do
    grep -- "$pattern" "$file"
done

I'm assuming the filenames do not contain tabs.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .