Home > OS >  Wanting to redirect the result of a grep command inside a find command (find -exec)
Wanting to redirect the result of a grep command inside a find command (find -exec)

Time:10-19

I'm using this find command to search all files in a directory ending with .err and .out. I'm using -exec with grep to search some content inside the files. But I can't figure out how to redirect each grep command to a file.

This my command:

find ./Documents/ -type f \( -name "*.out" -o -name "*.err" \) -exec sh -c "grep out {}; grep err {}" \;

I have tried this but it does not work (the files created are empty):

find ./Documents/ -type f \( -name "*.out" -o -name "*.err" \) -exec sh -c "grep out > file1 {}; grep err > file2 {}" \;

Thank you very much for any help you can provide.

CodePudding user response:

The conventional syntax is grep pattern inputfile >outputfile so that's what I would suggest.

However, you also should avoid {} inside -exec sh -c '...' because it will break if the file name contains shell metacharacters. You are creating a new shell inside the single quotes and inside that script, file names with, for example, single quotes need to have them escaped or otherwise handled.

Fortunately, the workaround is simple; variables which contain these charachers are fine (but you need to double-quote the variable interpolation, too!)

I'm also guessing you don't want to overwrite the results from the previous -exec each time find finds a new file, so I changed > to >>.

find ./Documents/ -type f \( \
    -name "*.out" -o -name "*.err" \) \
    -exec sh -c 'grep out "$1">>file1
        grep err "$1" >>file2
        ' _ {} \;

Another improvement would be to use -exec ... {} to run a single -exec for as many files as possible; you will then want to add a loop to the inner script.

find ./Documents/ -type f \( \
    -name "*.out" -o -name "*.err" \) \
    -exec sh -c 'for f; do
        grep out "$f">>file1
        grep err "$f">>file2
    done' _ {}  

Depending on your use case, you might be able to get rid of the loop and just run grep directly on all the files.

find ./Documents/ -type f \( \
    -name "*.out" -o -name "*.err" \) \
    -exec sh -c '
        grep -H out "$@">>file1
        grep -H err "$@">>file2
    ' _ {}  

The -H option turns off reporting of the file name for each match, which is otherwise enhbled by default if you run grep on more than one file.

See also When to wrap quotes around a shell variable? and https://mywiki.wooledge.org/BashFAQ/020

CodePudding user response:

You have wrong sequence in grep command. You should also redirect to append using '>>'. Otherwise your previous grep output will be overwritten by the next file found by the find command.

Try this command below:

find ./Documents/ -type f \( -name "*.out" -o -name "*.err" \) -exec sh -c "grep 'out' {} >> file1; grep 'err' {} >> file2" \;
  • Related