2

Let's says there is a file "abc.txt" that contains a paragraph/lines:

Hello, how are you doing sir? 
when are you going to arrive at the SBAHN platform to catch the train?

How can I extract all the characters in the file from "how" to "catch"?

1
4

If you have a version of grep that support PCRE you can use the following command to do what you want:

$ grep -Pzo 'how.*\n.*catch' file 
how are you doing sir? 
when are you going to arrive at the SBAHN platform to catch

The switches:

  • -P - enables PCRE - Perl Compatible Regular Expressions
  • -z - Output a zero byte (the ASCII NUL character) instead of the character that normally follows a file name. For example, grep -lZ outputs a zero byte after each file name instead of the usual newline. This option makes the output unambiguous, even in the presence of file names containing unusual characters like newlines. This option can be used with commands like find -print0, perl -0, sort -z, and xargs -0 to process arbitrary file names, even those that contain newline characters.
  • -o - Print only the matched (non-empty) parts of a matching line, with each such part on a separate output line.
3

The right tool for this job is pcregrep

pcregrep -oM "how(.|\n)*catch" SPEC
  • pcregrep: a grep with Perl-compatible regular expressions.
  • -o: show only the part of the line that matched a pattern
  • -M: allow patterns to match more than one line
  • (.|\n)*: match any character or newline zero or more times

If you want rather non-greedy version add ? after *:

pcregrep -oM "how(.|\n)*?catch" SPEC
1
  • You could also have used the (?s) modifier to make . include \n i.e. pcregrep -oM "(?s)how.*?catch" Sep 21 '14 at 14:22
2

Using sed

This answer assumes that you have a good quality sed that can handle long lines. Assuming that your text is in the file called file:

$ tr '\n' '\001' <file | sed -n -r 's/.*(how.*catch).*/\1\n/p' | tr '\001' '\n' 
how are you doing sir? 
when are you going to arrive at the SBAHN platform to catch

Explanation:

  • tr '\n' '\001' <file

    This reads from file file and replaces all newline characters with octal 001 characters. This has the effect of converting the input to a single line.

  • sed -n -r 's/.*(how.*catch).*/\1\n/p'

    Now that the input is a single line, sed can handle this task easily. The above substitution command captures all the text from "how" to "catch" and prints it to stdout

    Because the -n option is used, nothing is printed unless the regular expression matches. Thus, if the input does not have a how.*catch sequence, nothing prints.

  • tr '\001' '\n'

    This converts the octal 001 characters back to newlines.

The octal 001 can be replaced by any character that (a) you are sure is not in your input file and that (b) your sed can handle correctly.

Using awk

$ awk '/how/{f=1;sub(/.*how/,"how")} /catch/{f=0;sub(/catch.*/,"catch");print} f' file
how are you doing sir? 
when are you going to arrive at the SBAHN platform to catch

Explanation:

  • /how/{f=1;sub(/.*how/,"how")}

    If the line contains the word "how", this removes all text preceding the the "how" and sets the flag variable f to 1

  • /catch/{f=0;sub(/catch.*/,"catch");print}

    If the line contains the word "catch", this removes all text after the "catch", sets the flag variable f to 0 and prints the revised line.

  • f

    If the flag is 1, this somewhat cryptic awk command will cause the line to be printed. If f==0, then nothing is printed.

4
  • Nice way to get around the "newline problem", but of course only works if '\001' is not in the input.
    – Anthon
    Sep 21 '14 at 5:42
  • @anton With a GNU toolchain, one can use \000 which is even less likely to be in the input file.
    – John1024
    Sep 21 '14 at 6:02
  • I agree that it is unlikely, and with your extension to the answer, little chance someone misses the fact. If I had not yet upvoted your A, I would do so now.
    – Anthon
    Sep 21 '14 at 6:05
  • Thank you very much, John. Thats the right answer and it works!. Sep 22 '14 at 7:35
1

You could do this through Perl,

$ perl -000pe 's/.*(how.*?catch).*/\1\n/s' file
how are you doing sir? 
when are you going to arrive at the SBAHN platform to catch
1

The sed example will fail if the text is not part of the file, then you will get the full file instead of nothing.

Use grep instead of sed:

tr '\n' '\001' < file | grep -o -E 'how.*catch' | tr '\001' '\n' 

greedy and non-greedy matching is an issue too, so if the "catch" is on line 2 and another one on line 5, you want non-greedy matching.

Read here how to achieve that, depends a lot on the grep version:

https://stackoverflow.com/questions/3027518/non-greedy-grep

0

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.