3

I'm trying to understand why the find command is not deleting files in one particular case. I've got the following command:

find C:\\path\\to\\Pictures\\pmcctv -name cap_*.jpg
-o -name cap_*.ogg -o -name cap_*.flv -o -name cap_*.mp4 -o -name cap_*.webm -mtime +7

Which returns the files I would expect:

C:\path\to/Pictures/pmcctv/cap_20160915T193251_620067800.jpg
C:\path\to/Pictures/pmcctv/cap_20160915T193322_742708800.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T081046_394767500.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T081125_615129600.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T081129_503678200.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T081255_842394600.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T161008_693586800.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T161012_749396800.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T161043_774132200.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T161150_497251900.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T161154_437379600.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T161337_350955300.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T161341_452596000.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T161412_870774000.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T161443_969064400.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T161554_468109900.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T161558_378086000.jpg
C:\path\to/Pictures/pmcctv/cap_20160916T161916_668433100.jpg

Now if I try to run the same command with -delete, the files are not deleted:

find C:\\path\\to\\Pictures\\pmcctv -name cap_*.jpg -o -name cap_*.ogg
-o -name cap_*.flv -o -name cap_*.mp4 -o -name cap_*.webm -mtime +7 -delete

I've also tried with -exec rm, but they are also not deleted:

find C:\\path\\to\\Pictures\\pmcctv -name cap_*.jpg -o -name cap_*.ogg
-o -name cap_*.flv -o -name cap_*.mp4 -o -name cap_*.webm -mtime +7 -exec rm {} \;

find returns no error and the files are owned by me with permissions 644.

Any idea what could be causing this?

(note: I've cut the commands to make it more readable but normally there's no line break)

4
  • 1
    The mixed slashes might confuse it, particularly in the -exec case. Commented Sep 18, 2016 at 14:50
  • 2
    Depending on what you meant to do, you may want to add one set of quoted parentheses "(" and ")" around the sequence of -name ... -o -name ... conditions. Commented Sep 18, 2016 at 15:01
  • 1
    @ThomasDickey No, Cygwin tools treat backslashes like slashes, and have no problem with -exec. Commented Sep 18, 2016 at 22:26
  • 1
    You do know that you can use forward slashes, Unix style, with Cygwin? So instead of C:\\path\\to\\Pictures\\pmcctv you could use /cygdrive/c/path/to/Pictures/pmcctv. Commented Sep 18, 2016 at 22:49

2 Answers 2

9

Juxtaposition binds more tightly than the -o operator. So your command is equivalent to

find C:\\path\\to\\Pictures\\pmcctv \( -name cap_*.jpg \) -o \
                                    \( -name cap_*.ogg \) -o \
                                    \( -name cap_*.flv \) -o \
                                    \( -name cap_*.mp4 \) -o \
                                    \( -name cap_*.webm -mtime +7 -delete \)

and only week-old files matching cap_*.webm are deleted.

In addition, if there are any files matching one of the patterns in the current directory, then that pattern is replaced by the list of matching files. If the pattern matches exactly one file in the current directory, then only the files with the same name will be matched in subdirectories; if it matches two or more files, you get a syntax error.

The command you're looking for is

find C:\\path\\to\\Pictures\\pmcctv \
     \( -name 'cap_*.jpg' -o -name 'cap_*.ogg' -o -name 'cap_*.flv' -o -name 'cap_*.mp4' -o -name 'cap_*.webm' \) \
     -mtime +7 -delete

You can abbreviate it to

find C:\\path\\to\\Pictures\\pmcctv \
     -regex '.*/cap_[^/]*\.\(jpg\|ogg\|flv\|mp4\|webm\)' \
     -mtime +7 -delete

Or you can use zsh and in particular its glob qualifier m to match files by modification time.

rm C:\\path\\to\\Pictures\\pmcctv/**/cap_*.(jpg|ogg|flv|mp4|webm)(md+7)
3
  • 1
    Would "adjacency" be clearer than "juxtaposition"? Commented Sep 19, 2016 at 0:15
  • 1
    @JeffSchaller Juxtaposition is the normal term when talking about grammar (both in linguistics and in CS). Commented Sep 19, 2016 at 8:11
  • 1
    Thank you for pointing this out... I think this is the 3rd or 4th time I've had to look up why OR in GNU find doesn't work the same was as basically every other language/tool. Commented Jan 27, 2023 at 8:33
1

Another way to solve this is to add the -delete option to each -o argument. I think Gilles said it best that the find command seems to tie the delete "locally" to each expression rather than "globally" to the totality of results.

I recognize this is entirely redundant in this case - but it may help if you need to specify specific options for certain kinds of files!

So for example:

### Basic example:
find C:\\path\\to\\Pictures\\pmcctv -name cap_*.jpg -mtime +7 -delete \
-o -name cap_*.ogg -mtime +7 -delete \
-o -name cap_*.flv -mtime +7 -delete \
-o -name cap_*.mp4 -mtime +7 -delete \
-o -name cap_*.webm -mtime +7 -delete

### OR - cleaner example:
find C:\\path\\to\\Pictures\\pmcctv '(' -name cap_*.jpg -mtime +1 -delete ')' \
-o '(' -name cap_*.ogg -mtime +2 -delete ')' \
-o '(' -name cap_*.flv -mtime +3 -delete ')' \
-o '(' -name cap_*.mp4 -mtime +4 -delete ')' \
-o '(' -name cap_*.webm -mtime +5 -delete ')'

One last caveat here - be sure to make sure delete is in the "correct" spot - notice I didn't put delete BEFORE the name. I placed it after each -name grouping and before the -o. From https://www.baeldung.com/linux/find-delete-files-directories:

-delete action implies the -depth option.

The -depth option asks the find command to search each directory’s contents before the directory itself. Therefore, if we put -delete as the first option, it’ll start deletion from each directory tree’s very bottom. First, it removes all files under a directory, then the empty directory itself, until everything has been removed.

When we use the find command, we should keep in mind that we should never put the -delete action at the first position. If we do, it can delete files unexpectedly.

2
  • (1) You might want to parenthesize the groups, for clarity, as I did in Change all folder permissions with one command.  (2) See also my answer here (“the find command line is, effectively, an executable statement in an arcane language.”) to see, in general, why actions should always be placed after tests (it’s not specific to -delete). Commented Aug 27, 2024 at 21:18
  • Very helpful @G-ManSays'ReinstateMonica'! I wish the find command was a bit clearer on how they parse stuff prior to executing - I think it would clear up some of these syntax/semantic errors. I'm in the middle of writing a basic bash script to archive & cleanup logs upon application restarts and find is a much safer way to remove files than the rm command. Using tips like yours - I'm able to drastically simplify & clarify the code! Commented Aug 28, 2024 at 13:12

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.