6

I know that

grep -rhI "# Active" > out.txt 

will output any line containing # Active within the searched directory but I want the entire .txt file contents, so example

example.txt

Line1 
Line2
Line3 # Active
Line4
Line5
etc

So if I grep for # Active I want it to not just output the line containing # Active within these .txt file but all the other lines too, example

output.txt

Line1 
Line2
Line3 # Active
Line4
Line5
etc
2
  • 1
    Maybe you can try to use -C (context) switch for grep. Not ideal (need to know maximum lines in file), but helps. Nov 23, 2017 at 22:39
  • 1
    What kind of line separator do you have between your lines? Windows (#10#13) or UNIX newlines (#13)? Maybe the whole file is treated as one line but displayed on multiple lines.
    – Dominique
    Nov 24, 2017 at 8:46

4 Answers 4

12

For non-GNU versions of grep, which are unlikely to have -z, or if portability is required...

grep -q pattern file && cat file

-q suppresses any output but, per usual, exit status is set based on whether or not a pattern match was found. With a pattern match grep returns the success code 0 which is equivalent to true and that allows the cat command to be executed.

1
  • a variant : grep -l "pattern" ./file*s | xargs cat . displays the (concatenated) entirety of all files that have at least 1 line matching "pattern" Nov 24, 2017 at 12:12
10

Normally, grep will just show the matching line:

$ grep -rhI "# Active"
Line3 # Active

To see the whole file, add the -z flag:

$ grep -rhIz "# Active"
Line1 
Line2
Line3 # Active
Line4
Line5
etc

-z is a GNU extension that tells grep not to use newline as the 'line' separator but to use a NUL character instead. Since text files generally do not have NUL characters in them, this has the effect of telling grep to read the whole file as if it were a single 'line'. Consequently, if there is a match, the whole file is printed.

On BSD/OSX versions of grep, the NUL input option is not available and -z means something else.

The other grep options

-r tells grep to search files and directories recursively.

-I tells grep to ignore binary files

-h tells grep to print the match without the filename attached.

3

sed alternative:

sed -e 'H;1h;/PATTERN/!d;x;:do' -e 'n;b do' infile

this script works by accumulating lines in the hold buffer (and deleting them) until a matching line is encountered at which point it exchanges the buffers and executes n (i.e. print & get the next line) until there's no more input.

1

If you want to do it on multiple files:

cat `grep -rIl "# Active" *`

Grep will return the list of filenames and cat will print them.

5
  • 2
    and pray that none of those paths contains spaces or other funky chars... Nov 24, 2017 at 17:48
  • True, but this is the only way I see to do it with multiple files. If you have a solution to the "funky chars" feel free to edit my answer.
    – ChatterOne
    Nov 24, 2017 at 21:35
  • There are too many ways to do it right: use a for loop, use find with -exec, use grep with -Z and pipe it to xargs -0 cat, use zsh with estring qualifier etc... Nov 24, 2017 at 21:49
  • shopt -s globstar ; for file in **/*; do test -f "$file" && grep -qlF '# Active' "$file" && cat "$file"; done Nov 24, 2017 at 21:59
  • @don_crissti As I said, feel free to edit my answer
    – ChatterOne
    Nov 25, 2017 at 20:10

You must log in to answer this question.

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