3

i'm learning bash script by doing, and i had to find files which don't contain a certain string and the command i came up with didn't work. I solved the problem in the meantime by using grep -L (stackoverflow.com/questions/1748129/), but i would still want to know, what's wrong with my original command (so i can learn for the future).

the command is:

find path/ -name *.log -print0 | xargs -0 -i sh -c "if [ '1' == $(cat {} | grep -c 'string that should not occur') ]; then echo {}; fi"

and the error

cat: {}: No such file or directory

I also tried without 'sh -c' before, but it didn't work either.

edit: I also tried

find ./path -name *.log -print0 | xargs -0 -i bash -c "if [ '0' == $(cat $0 | grep -c \"ren0 \[RenderJob\] Render time:\") ]; then echo $0; fi" {}

which didn't work because of https://stackoverflow.com/a/1711985/4032670

Community
  • 1
  • 1
Adam
  • 488
  • 4
  • 17
  • Running a subshell and then interpolating the file name in the string passed to the shell totally negates the benefit of `find -print0` and `xargs -0`. You would want something like `find ... -print0 | xargs -r0 sh -c 'for f; do things with "$f"; done' _`so that arbitrary file names are passed correctly. – tripleee Feb 02 '16 at 10:54
  • 1
    Of course, your shell script is a prime example of [useless constructs](http://www.iki.fi/era/unix/award.html). The `cat` is useless, the `test` is useless, the `grep -c` is useless ... You want `grep -q 'string' "$f" || echo "$f"` plain and simple. Though as @anubhava points out, `grep -L` does this, if you have a `grep` version which supports this option. – tripleee Feb 02 '16 at 10:56
  • thanks for the comments, and thanks for the link :) – Adam Feb 02 '16 at 11:30

2 Answers2

3

You can use find and xargs like this:

find path/ -name '*.log' -print0 |
xargs -r0 -I {} bash -c 'grep -q "string that should not occur" "{}" || echo "{}"'

Without bash -c you can do this using grep -L:

find path/ -name '*.log' -print0 |
xargs -r0 grep -L "string that should not occur"
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • Why does `find ... | xargs -0 -i sh -c "echo {}"` work? – hek2mgl Feb 02 '16 at 10:45
  • 1
    Using `{}` at the end of a `xargs` command line is pointless; that's what it does anyway, in the absence of any `-I` option. – tripleee Feb 02 '16 at 11:00
  • As a final tweak, `xargs -r` avoids running the command if `find` did not return any matches. – tripleee Feb 02 '16 at 11:01
  • The `-I {}` and final `{}` is redundant in the first example, too. – tripleee Feb 02 '16 at 11:03
  • Hmm you're right about `-r` as I checked `man` page of `gnu-xargs` but `xargs` on OSX doesn't have `-r` – anubhava Feb 02 '16 at 11:03
  • @tripleee: No, `-I {}` and final `{}` is indeed needed in first command since it is used for populating `$1` – anubhava Feb 02 '16 at 11:07
  • thanks, it's not 'exactly' the answer to my question (why it didn't work), but it was helpful non the less and i was able to make my code work (although I understand it's inferior to grep -L, which i also linked in the question). – Adam Feb 02 '16 at 11:33
  • My bad, you're right. Though if you put in a loop over shell arguments, it would be more efficient, too. – tripleee Feb 02 '16 at 11:34
  • @anubhava `find | xargs -i sh -c "echo {} {}"` works for me as well. It's really unclear for me why the code in the question didn't worked. – hek2mgl Feb 02 '16 at 12:01
  • @hek2mgl: On OSX I don't have support for `-i` so cannot test that but I think another problem with OP's command was unquoted `*.log` in `-name` option of `find` causing it expand beforehand. However `{}` should be quoted to safely work with filenames with space in `xargs` – anubhava Feb 02 '16 at 15:13
  • 1
    @anubhava For me it looks atm like a bug. Of course the code of the OP can be optimized, for example it does not use `xargs` at all, it could use the `-exec` option of find. But still - IMHO - it should at least not throw that error. – hek2mgl Feb 02 '16 at 15:20
0

xargs will not interpolate the {}.

try:

find path/ -name "*.log" | \
  while read file; do
    if grep -q 'string that should not occur' "$file"; then
      echo $file ;
    fi ;
  done

print all files that match.

noob
  • 111
  • 7
  • This fails to cope with file names with whitespace or shell metacharacters in them, with multiple bugs related to this. (Sorry, out of downvotes for today ... /-: – tripleee Feb 02 '16 at 10:57