I have a working version using perl.
I thought this worked with GNU awk , but I did not. RS = An empty line is broken into empty lines. See Change History for a broken version of awk.
How to search for a multi-line pattern in a file? shows how to use pcregrep, but I donβt see a way to make it work when the search pattern may contain special regular expression characters. -F fixed-line mode works inefficiently with multi-line mode: it still treats the template as a set of lines that must be matched separately. (Not like a multi-line fixed string that needs to be matched.) I see you already used pcregrep in your attempt.
By the way, I think you have an error in your code in the case of non-sudo:
function writeToFile { if [ -w "$1" ] ; then "$2" >> "$1" # probably you mean echo "$2" >> "$1" else echo -e "$2" | sudo tee -a "$1" > /dev/null fi }
In any case, attempts to use linear tools have failed, so it's time to pull out a more serious programming language that does not force the agreement on a new line to us. Just read both files in variables and use search without regular expression:
#!/usr/bin/perl -w # multi_line_match.pl pattern_file target_file # exit(0) if a match is found, else exit(1) #use IO::File; use File::Slurp; my $pat = read_file($ARGV[0]); my $target = read_file($ARGV[1]); if ((substr($target, 0, length($pat)) eq $pat) or index($target, "\n".$pat) >= 0) { exit(0); } exit(1);
See What is the best way to cut a file into a string in Perl? to avoid dependency on File::Slurp (which is not part of the standard perl distro, or by default Ubuntu 15.04). I went for File :: Slurp in part for readability, which the program does for non-perl-geeks compared to:
my $contents = do { local(@ARGV, $/) = $file; <> };
I worked on not reading the complete file in memory, with the idea of http://www.perlmonks.org/?node_id=98208 . I think inconsistencies will usually read the entire file at once. In addition, the logic was quite complicated to handle the match at the beginning of the file, and I did not want to spend a lot of time testing to make sure that this was correct for all cases. Here is what I had before giving up:
#IO::File->input_record_separator($pat); $/ = $pat; # pat must include a trailing newline if you want it to match one my $fh = IO::File->new($ARGV[2], O_RDONLY) or die 'Could not open file ', $ARGV[2], ": $!"; $tail = substr($fh->getline, -1); #fast forward to the first match #print each occurence in the file #print IO::File->input_record_separator while $fh->getline; #FIXME: something clever here to handle the case where $pat matches at the beginning of the file. do { # fixme: need to check defined($fh->getline) if (($tail eq '\n') or ($tail = substr($fh->getline, -1))) { exit(0); # if there a 2nd line } } while($tail); exit(1); $fh->close;
Another idea was to filter the templates and files for searching through tr '\n' '\r' or something else, so all of them would be single-line. ( \r is a likely safe choice that would not come across anything already in the file or template.)