I'm trying to stack a find command so that the results it returns only contains files with "warning:" or "error:" text within them. I also require those results in a specific format so I'm using exec stat for that.

I can get a version of this to work, but I need to be able to use -exec stat within the while loop to formulate a specific string, and that -exec stat is making my shell hand me the > prompt. The part that's not working is in bold below.

Separately I can get these to work. Together I cannot.

Here's an example of one I've got working:

find . -type f -exec grep -li error: {} \; | while read -r file; do ls -l "$file"; done

So now I'm trying to shoehorn that in with another process that limits the files to specific times, types etc. It's not going well:

find /my/directory/here/ -type f -name *.log -mmin -480 -mmin +60 -exec grep -li warning: {} \; -exec grep -li error: {} \; | while read -r file; do -exec stat -c "$file" '%n|%y|%x|%s|%U|%u' {} \; done

Note, the whole thing works if I remove the -exec stat from within the while loop. If I replace that with a simple ls -l, results are given back to me. Right now the shell is just giving me a command prompt.

I know I'm not doing this right but I have no ideas right now on what I should be resolving first.

2 Answers 2

find /my/directory/here/ -type f -name '*.log' -mmin -480 -mmin +60 \
    -exec grep -qi warning: {} \; \
    -exec grep -qi error: {} \; \
    -exec stat -c '%n|%y|%x|%s|%U|%u' {} +

That is, you don't need the actual output of the two grep command, only the exit status, so we use -q instead of -l. There is no need for a while loop. The stat may be called with multiple files, so we use -exec stat ... {} +. I have also quoted the *.log pattern to prevent it from expanding to any such name in the current directory.


Another way of doing the same thing:

find /my/directory/here/ -type f -name '*.log' -mmin -480 -mmin +60 \
    -exec sh -c '
        for pathname do
            if grep -qi warning: "$pathname" &&
               grep -qi error: "$pathname"
                stat -c "%n|%y|%x|%s|%U|%u" "$pathname"
        done' sh {} +

This uses find as a pathname generator for a for loop running in a child shell. This would however call stat once for each file that contains matches for both patterns, so it's slightly less efficient.

  • Thanks for that answer and explanation. I've just submitted that and nothing was returned to me for some time...and then boom, everything arrived all in one go. I'll test the output to make sure it's only picking up logs with warnings and errors, but functionally it appear to be working exactly as I need. Cheers.
    – Dan
    Commented Jun 20, 2019 at 12:49
  • @Dan Yes, with the -exec ... {} + form, find would collect as many files as possible and then run the command (stat) on all of them at once (a single or only a few invocations of stat). If you change that to -exec ... {} \; it would run stat on each file as it gets to it.
    – Kusalananda
    Commented Jun 20, 2019 at 12:51
  • Understood, and that's why with my previous example it was returning it line by line then. As this will be running via an application that doesn't return the shell window to me (and hence cannot interact with the shell or see what it's doing) that is absolutely fine.
    – Dan
    Commented Jun 20, 2019 at 12:54

GNU's grep(1) can do recursive pattern matching:

grep -R warning that-dir

checks all files under that-dir.

  • This is true, but I'm not certain how this would help the user since they want to have more fine-grained control over what files are being searched, and then call stat on those files that have matches.
    – Kusalananda
    Commented Jun 20, 2019 at 15:13

You must log in to answer this question.

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