2

I am attempting to write a script to delete files from a directory.

find /home/docs/* -name "*.log" -type f -print -exec rm -f {} \;

The above works however I need to use grep to target directories using grep. When I put grep I get failures.

find /home/docs/* -name "*.log" -type f | grep -i "Testing"-print -exec rm -f {} \;

Directory structure.

 /home/docs/2023/01/Testing/
 /home/docs/2024/01/Testing/
 /home/docs/2024/02/Approve/
 /home/docs/2022/01/Testing/

I need to remove log from the Testing directory. I can use grep with the find however when I try to use -exec to remove the files I am getting an errors. I have tried a lot of different combinations.

Working to just list the files find /home/docs/* -name "*.log" -type f | grep -i "Testing" Getting a list of files

 /home/docs/2023/01/Testing/3.log
 /home/docs/2023/aisle/drawing/01/Testing/2.log
 /home/docs/2023/drawing/Testing/1.log

Adding -exec doesn't delete find /home/docs/* -name "*.log" -type f | grep -i "Testing" -exec rm -f {} \; This iteration I am not getting an error just getting grep: {}: No such file or directory

5
  • 3
    You added the -exec to grep and not to find Commented Apr 30 at 18:13
  • I would need the list of filtered files before I -exec rm Commented Apr 30 at 18:27
  • 1
    I agree with @KamilMaciorowski. But just for general knowledge, after a pipe, you start a new command and can't give arguments to the first command anymore. So if you need to do the -exec after the grep you need to find another solution, because sticking the -exec on the grep will not achieve what you want Commented Apr 30 at 18:43
  • note that even if you actually need to use regular expessions, there's no need to use grep, most implementations of find have regex support! In other words, your problem really only comes from the fact that you think you need to use find with grep; but find can do what you need, alone. So, maybe ask in detail about what you want to achieve! Commented Apr 30 at 18:56
  • You've got two contradictory example directory structures. The first is uniform and regular. The second is not. Which do you really have? Please remove or correct the one that's wrong Commented May 1 at 9:34

3 Answers 3

8

you don't need grep; you need to read the manual page of find; man find!

There, you'll find (search for "regex", by typing /regex) that with -iregex you can just match against regexes. However, you don't even need regexes!

And you'll also find that deletion can be done without -exec:

find /home/docs -ipath '*Testing*.log' -type f -delete

that's it. No weird corner cases, no problems with line breaks, no calling grep.

Should you really need regexes:

find /home/docs -iregex '.*Testing.*\.log$' -type f -delete

But, for your problem, you don't need regexes at all.

In fact, you don't even need find! Your shell can glob (automatically complete filenames) well enough for this task:

In bash (probably the shell you're using, unless you know otherwise), a simple

shopt -s globstar nocaseglob
rm -f /home/docs/**/*Testing*/**/*.log

would do¹. In zsh, you don't even need to set any special options, so it's simply rm -f /home/docs/(#i)**/*Testing*/*.log(.) if you're using zsh and have extendedglob enabled. The (#i) makes the rest of the glob matching case-insensitive², the trailing (.) only matches regular files.


¹ beware that /home/docs/**/Testing/**/*.log wouldn't find /home/docs/x/testing/file.log though as only the /-delimited components that have glob operators are affected by nocaseglob. You'd need /home/docs/**/[T]esting*/**/*.log for instance for that to match Testing case-insensitively. That's different with zsh's (#i)

² change it to /home/docs/**/*((#i)Testing)*/*.log(.) for the case insensitivity to only apply to Testing (and not log).

5
  • Thanks, Stéphane, for the excellent additions to this answer! Commented May 1 at 6:51
  • 2
    Worth noting that -ipath, -regex, -iregex and -delete are all non-standard extensions. While they are widespread these days, note that there are significant incompatibilities in the handling of -regex/-iregex between implementations that support it. Commented May 1 at 6:52
  • May be worth also noting that while **/ in zsh/ksh93/yash/tcsh or bash-5.0+ won't follow symlinks (see ***/ for the symlink following variant in zsh/yash/tcsh), if /home/docs or any of the files matching /home/docs/**/*Testing* are symlinks to directories, they will be followed (contrary to find which doesn't follow any symlink unless asked to with -H (for the file arguments only), -L or -follow Commented May 1 at 7:16
  • Thank everyone for all of the help and the explanations. I did try regex but I was getting errors Commented May 1 at 15:01
  • 2
    @newdeveloper remark for the future: Then, clearly, tell us what you did and what error exactly you were getting. There was probably just a small syntax error in your regex, and we might have helped you fixed that. Commented May 1 at 15:24
4

One of these. (The first will work on many platforms, the second will work on all.)

find /home/docs -path '*/Testing/*.log' -delete
find /home/docs -path '*/Testing/*.log' -exec rm -f {} +    # POSIX

If your first example directory structure is correct, a simple shell glob expansion could work even more easily (assuming not "too many" files match the expansions:

rm -f  /home/docs/????/??/Testing/*.log
1
  • Technically, -path (from BSD) was only added in the 2008 version (latest) of the POSIX standard, you might still find old systems that don't support it. The -exec ... {} + variant, while it's been in POSIX for longer was added much later to some implementations. See also -path '*/[Tt][Ee][Ss][Tt][iİıI][Nn][Gg]/*.log' to match on Testing case insensitively (adding ıI as there are locales such as English ones where uppercase i is I instead of İ 😜) Commented May 1 at 11:28
2

As mentioned in the comments to your post:

  • There are ways to do this entirely in find, without using grep
  • In any case, -exec is not an option you can pass to grep

However, if you want to use grep anyway, I would recommend using xargs, like this:

find /home/docs/* -name "*.log" -type f | grep -i "Testing" | xargs rm -f

Or, to work correctly even when there are files with whitespace, quoting, apostrophe or backslash characters in their path:

find /home/docs/* -name "*.log" -type f -print0 | grep -z -i "Testing" | xargs -0 rm -f

You must log in to answer this question.

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