43

Input file1 is:

dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

I give the match the pattern from in other file ( like dog 123 4335 from file2).

I match the pattern of the line is dog 123 4335 and after printing all lines without match line my output is:

cat 13123 23424
deer 2131 213132
bear 2313 21313

If only use without address of line only use the pattern, for example 1s how to match and print the lines?

13 Answers 13

26

If you have a reasonably short file grep alone might work:

grep -A5000 -m1 -e 'dog 123 4335' animals.txt

5000 is just my guess at "reasonably short", as grep finds the first match and outputs it together with the next 5000 lines (the file doesn't need to have that many). If you don't want the match itself you'll need to cut it off, e.g.

grep -A5000 -m1 -e 'dog 123 4335' animals.txt | tail -n+2


If you do not want the first, but the last match as delimiter you could use this:

tac animals.txt | sed -e '/dog 123 4335/q' | tac

This line reads animals.txt in reverse order of lines and outputs up to and including the line with dog 123 4335 and then reverses again to restore proper order.

Again, if you don't need the match in the result, append tail. (You could also complicate the sed expression to discard its buffer before quitting.)

  • By my test, GNU grep 3.0 doesn't output more than 132 lines in after-context (regardless of specified value). – ruvim Aug 29 '17 at 9:44
26

Assuming you want to match the whole line with your pattern, with GNU sed, this works:

sed -n '/^dog 123 4335$/ { :a; n; p; ba; }' infile

Standard equivalent:

sed -ne '/^dog 123 4335$/{:a' -e 'n;p;ba' -e '}' infile

With the following input (infile):

cat 13123 23424 
deer 2131 213132
bear 2313 21313
dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

The output is:

cat 13123 23424 
deer 2131 213132
bear 2313 21313

Explanation:

  • /^dog 123 4335$/ searches for the desired pattern.
  • :a; n; p; ba; is a loop that fetches a new line from input (n), prints it (p), and branches back to label a :a; ...; ba;.

Update

Here's an answer that comes closer to your needs, i.e. pattern in file2, grepping from file1:

tail -n +$(( 1 + $(grep -m1 -n -f file2 file1 | cut -d: -f1) )) file1

The embedded grep and cut find the first line containing a pattern from file2, this line number plus one is passed on to tail, the plus one is there to skip the line with the pattern.

If you want to start from the last match instead of the first match it would be:

tail -n +$(( 1 + $(grep -n -f file2 file1 | tail -n1 | cut -d: -f1) )) file1

Note that not all versions of tail support the plus-notation.

  • This is the first example of the n and p commands in sed that I have seen that doesn't feel like taking sed too far. It seems (from my brief tests) that sed -n '/^dog 123 4335$/ { :a; p; n; ba; }' infile (with the p and n switched) successfully includes the line that matches as well. – Josiah Yoder Apr 3 at 15:25
21

In practice I'd probably use Aet3miirah's answer most of the time and alexey's answer is wonderful when wanting to navigate through the lines (also, it also works with less). OTOH, I really like another approach (which is kind of the reversed Gilles' answer:

sed -n '/dog 123 4335/,$p'

When called with the -n flag, sed does not print by default the lines it processes any more. Then we use a 2-address form that says to apply a command from the line matching /dog 123 4335/ until the end of the file (represented by $). The command in question is p, which prints the current line. So, this means "print all lines from the one matching /dog 123 4335/ until the end."

  • 3
    That prints the dog line though which is not wanted here. – Stéphane Chazelas Jun 10 '15 at 9:49
  • 1
    This looks like the best answer (and works for my own case) but would need to be adapted to skip the matched line as well. – Pavel Šimerda Mar 14 '16 at 7:17
  • 1
    sed -n '/dog 123 4335/,$p' | sed '1d' will remove the dog line – Kemin Zhou Jul 2 '18 at 5:26
  • 1
    sed -n '/dog 123 4335/,$p' | tail -n +2 will remove the match as well – gilad mayani Oct 24 '18 at 13:01
15
sed -e '1,/dog 123 4335/d' file1

If you need to read the pattern from a file, substitute it into the sed command. If the file contains a sed pattern:

sed -e "1,/$(cat file2)/d" file1

If the file contains a literal string to look for, quote all special characters. I assume the file contains a single line.

sed -e "1,/$(sed 's/[][\\\/^$.*]/\\&/g' file2)/d" file1

If you want the match to be the whole line, not just a substring, wrap the pattern in ^…$.

sed -e "1,/^$(sed 's/[][\\\/^$.*]/\\&/g' file2)\$/d" file1
  • 6
    That won't work if the pattern is on the first line. GNU sed has 0,/dog.../d for that. – Stéphane Chazelas Jun 10 '15 at 9:48
14

$ more +/"dog 123 4335" file1

  • 4
    It also works with less. – brandizzi May 26 '15 at 13:40
  • 3
    clever on the terminal, but it doesn't actually work if you pipe it into something else like tac. – jcomeau_ictx Jul 2 '15 at 6:25
  • i am using it like this , $ more +/"match my words" file1 >> file2 – AMB Jul 7 '15 at 17:38
  • 1
    Maybe + was replaced by -p in POSIX 7: pubs.opengroup.org/onlinepubs/9699919799/utilities/more.html but not yet implemented in util-linux 2.20.1. And this also prints skipping.. and some extra newlines (to stderr I expect, so might be fine). – Ciro Santilli 新疆改造中心996ICU六四事件 Sep 14 '16 at 12:41
  • maybe things have changed since then? my comment got 3 upvotes so it may have been relevant at the time... – jcomeau_ictx Nov 4 '17 at 16:42
11

With awk:

awk 'BEGIN {getline pattern < "other file"}
   NR == 1, $0 ~ pattern {next}; {print}' < "input file"
5

One way using awk:

awk 'NR==FNR{a[$0];next}f;($0 in a){f=1}'  file2 file1

where file2 contains your search patterns. First, all the contents of file2 are stored in the array "a". When the file1 is processed, every line is checked against the array, and printed only if is not present.

  • I think the OP wants to output every line following the pattern. – Thor Nov 23 '12 at 11:41
  • @Thor : thanks for pointing it out, updated it now... – Guru Nov 23 '12 at 11:57
  • Nicely done :). – Thor Nov 23 '12 at 13:04
4

My answer for the question in the subject, without storing pattern in a second file. Here is my test file:

$ cat animals.txt 
cat 13123 23424 
deer 2131 213132
bear 2313 21313
dog 123 4335
cat 13123 23424 
deer 2131 213132
bear 2313 21313

GNU sed:

 $ sed '0,/^dog 123 4335$/d' animals.txt 
 cat 13123 23424 
 deer 2131 213132
 bear 2313 21313

Perl:

$ perl -ne 'print unless 1.../^dog 123 4335$/' animals.txt
cat 13123 23424 
deer 2131 213132
bear 2313 21313

Perl variant with pattern in a file:

$ cat pattern.txt 
dog 123 4335
$ perl -ne 'BEGIN{chomp($p=(<STDIN>)[0])};print unless 1../$p/;' animals.txt < pattern.txt
cat 13123 23424 
deer 2131 213132
bear 2313 21313
4

If the input is an lseekable regular file:

With GNU grep:

{ grep  -xFm1 'dog 123 4335' >&2
  cat; } <infile 2>/dev/null >outfile

With sed:

{ sed -n '/^dog 123 4335$/q'
  cat; } <infile >outfile

A GNU grep called w/ the -m option will quit input at the match - and it will leave its (lseekable) input fd immediately after the point it found its last match. So calling grep w/ -m1 finds the first occurrence of a pattern in a file, and leaves the input offset at precisely the right place for cat to write everything following the pattern's first match in a file to stdout.

Even without a GNU grep you can do the exact same thing w/ a POSIX compatible sed - when sed quits it is specified to leave its input offset right where it does. GNU sed is not standards-compliant in this way, though, and so the above will likely not work w/ a GNU sed unless you call it with its -u switch.

  • note, the sed stream sharing demonstrated here is not specially (though, yes, the standard referenced does specifically example sed as a utility thus capable) of the free-form and conditionally cooperative workflow shown. notably, all standard utilities are meant and specified to thus cooperate and share cursor positions of input streams without failing the next reader any processing at all. grep -q should do this; quietly grep should return as soon as any match in input is found, and any remaining of input should not, by standard, be consumed by default. – mikeserv Oct 29 '18 at 10:43
2

Wth ed:

ed -s file1 <<< '/dog 123 4335/+1,$p'

This sends one print command to ed in a here-string; the print command is limited in range to one after (+1) the dog 123 4335 match until the end of the file ($).

1

If you don't mind the creation of a temporary file, and have csplit available, this works:

sh -c 'csplit -sf"$1_" "$1" "%^$(cat "$2")%+1" && cat "${1}_00"' sh file1 file2

Note file1 is the input file and file2 is the pattern file (as stated in the question).

The long form of the above command is:

sh -c 'csplit --quiet --prefix="$1_" "$1" "%^$(cat "$2")%+1" && cat "${1}_00"' sh file1 file2

i.e.,

csplit --quiet --prefix="file1_" "file1" "%^$(cat "file2")%+1" && cat "file1_00"

csplit without the prefix flag above would create the file xx00 (prefix being xx, and suffix being 00). With the flag above it creates the file file1_00. Without the quiet flag, it prints the output file size (size of the resulting file).

0

Since awk isn't expressly disallowed, here's my offering assuming 'cat' is the match.

awk '$0 ~ /cat/ { vart = NR }{ arr[NR]=$0 } END { for (i = vart; i<=NR ; i++) print arr[i]  }' animals.txt
0

How to print all lines after a match up to the end of the file?

Another way to put it is "how to delete all lines from the 1st one till the match (including)", and this can be sed-written as:

sed -e '1,/MATCH PATTERN/d'

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.