5

Looking for a way to recursively search a repository for all files containing a multi line string and return the file names that contain it. The paragraph is just a header approx 30 lines. So I know pcregrep can do multi-line search. But you need to put the whole expression in. As this is a long paragraph I'm searching for, I want to place it in variable and just do a grep of that variable. So basically it would be find . -name "*.[ch]" | xargs pcregrep -M $paragraph But this does not work could anyone point me in the right direction.

  • At a minimum, you should probably be quoting your search pattern "$paragraph" – steeldriver Feb 4 '16 at 12:59
  • youre searching .[ch]? use ctags – mikeserv Feb 4 '16 at 13:01
  • @steeldriver The pattern is 30 lines long though. – Gooner1990 Feb 4 '16 at 13:06
  • The searching of 30-lines string is useless. Try to choice some keywords from it to seek for. – Costas Feb 4 '16 at 13:11
  • @Gooner1990 I previously posted an answer and immediately deleted it as soon as I noticed that you were looking to match a 30 lines multi-line string; I changed my answer radically and undeleted it, it should help. – kos Feb 4 '16 at 14:37
2

Since you're trying to match the first 30 lines of your files you could save the text in a file e.g. ref_file then use diff to compare the reference file with the first 30 lines in each file:

find . -name "*.[ch]" -exec ./myscript {} \; -print

where ./myscript is

#!/bin/sh

head -n 30 "$1" | diff - /path/to/ref_file >/dev/null

so -print in the first command is only executed if the previous -exec evaluated true, that is if there was no difference between the reference file and the first 30 lines from the current file.
Or, if you prefer without a script and saving the file names in a logfile:

find . -type f -exec sh -c 'head -n 30 "$0" | diff - /path/to/ref_file >/dev/null' {} \; -print >logfile

Note that this assumes you're looking for an exact match otherwise diff exits with 1, even if the difference was a single space.

  • Works great thank you very much. Instead of printing, I want to log to a file. I did this but does not seem to work find . -name "*.[ch]" -exec ./myscript {} \; >>output.txt – Gooner1990 Feb 4 '16 at 15:12
  • I want to log the filenames – Gooner1990 Feb 4 '16 at 15:14
2

You could find each file to be processed using find and feed each filename to a script made on purpose to look for a match and print the filename in case of a match; I'd suggest to use a script rather than a one-liner for the added easiness of handling the multi-line string compared to the prompt.

That is:

find . -name "*.[ch]" -exec /path/to/script {} \;

Where script is this Perl script:

#! /usr/bin/perl

$/ = ""; # sets the input record separator to an empty string
$_ = <>; # stores the content of the file specified in the first argument in $_

$string = <<EOF; # The multi-line string to match starts here
My
multiline
string
EOF
# The multi-line string to match ends here

/\Q$string\E/ && print($ARGV."\n"); # If $_ matches $string, prints the name of the file
  • $/ = "";: sets Perl's input record separator to an empty string; this has the effect of making Perl read the whole file specified in the first argument to the script at once;
  • $_ = <>;: stores the content of the file specified in the first argument in $_;
  • $string = <<EOF; [...] EOF: stores the content of [...] in $string(replace [...] with the multi-line string to match);
  • /\Q$string\E/ && print($ARGV."\n");: if $_ matches $string, prints the name of the file.

This is the sample output on a test directory hierarchy made on purpose:

% for f in *; do printf '%s:\n\n' "$f"; <<<'' cat "$f" -; done
file1:

My
multiline
string

file2:

My
multiline
string

file3:

My
other
multiline
string

script.pl:

#! /usr/bin/perl

$/ = "";
$_ = <>;

$string = <<EOF;
My
multiline
string
EOF

$string = quotemeta($string);

/$string/&&print($ARGV."\n");

% find . -type f -exec ./script.pl {} \;                      
./file2
./file1
  • So I have perl script written and I find . -name "*.[ch]" -exec search.pl but I get I get an error returned find: missing argument to -exec'` – Gooner1990 Feb 4 '16 at 15:01
  • @Gooner1990 My bad, this is the right command: find . -name "*.[ch]" -exec /path/to/script {} \; – kos Feb 4 '16 at 15:02

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.