5

Is it possible to write a command in Terminal that would delete all files on Mac except for some?

Let’s say I have one general folder that consists of N folders with different titles/names. I need to keep all the folders. Yet, I need to delete all files within them except for photos. These photo-files have different names but all start with “image-” in title.

before deleting

Is there any command that would mean “please, delete everything in these folders except for jpges that start with “image-“?

To make it like this:

after deleting

Thanks!

2 Answers 2

7

You can use find as follows (replace one general folder [before] as necessary):

find "one general folder [before]" -type f -not -iname "image-*jpg" -exec rm {} \;

The option -type f selects only files (that is, it excludes folders, symlnks, sockets and other "special" files).

The option -iname "image-*jpg" is a case-insensitive search that selects all JPEG images that start with image-' (or other case combinations like IMAGE- or Image-. If you prefer an exact match, use -name instead). Since this option is prepended by -not, the search is reversed, so that find selects all files except JPEG images that start with image-.

The option -exec rm {} \; deletes all files found by find.

I've tested it with a folder structure similar to the one you show in your screenshot and am confident it works as intended, but I strongly recommend that you do the following before running the command above:

  1. Make a copy of your data (for example, duplicate the affected folder)
  2. Make sure that find selects the correct files by running find "one general folder [before]" -type f -not -iname "image-*jpg" -print. This command should list all files that are not JPEG files whose name starts with image-.

For more information on find, see https://ss64.com/mac/find.html or run man find in Terminal.

5
  • 5
    Backups are critical, I would do two things with this. 1) change -delete to -print so people can inspect what files will be culled. 2) specify the absolute path to the start folder. Dropping it into the command line to properly escape any spaces. In the end, this is an ideal solution, just dangerous if . becomes another spot to mass delete.
    – bmike
    Commented Sep 5 at 13:31
  • Difference between -iname and -name? Also, I would say "image-*" to avoid deleting things like image-saved-this-as.png
    – WGroleau
    Commented Sep 5 at 18:32
  • 3
    In this case it's better to use -delete instead of -exec first because it's faster to let find delete the file instead of spawning another program to do it but more importantly it avoids bugs in writing the exec command.
    – slebetman
    Commented Sep 6 at 1:10
  • 2
    @WGroleau case insensitive in case the name is IMAGE-... instead. It's still fairly common on Mac since the filesystem is case preserving but not case sensitive.
    – slebetman
    Commented Sep 6 at 1:12
  • @slebetman There is some danger in using -delete: if you happen to forget -type f, -delete will silently remove not only all matching files but also all matching empty directories. -exec rm {} \;, on the other hand, will only remove files. That's why I never use -delete myself, but that's of course a personal choice.
    – jaume
    Commented Sep 6 at 9:46
3

First of all, it is important to remember that it is not Terminal that executes the commands, but the shell that runs inside it. On a reasonably recently created user account on a Mac, that shell will be zsh.

If you’re running zsh, it should be possible to list all files that do not match the pattern image-*.jpg using

setopt extended_glob
echo **/^image-*.jpg(.)

** matches the current working directory and any subdirectories, ^ inverts the following pattern, and (.) limits the whole thing to match only ordinary files (and not, for example, directories). For details, see the Filename Generation section of the zsh manual, or run man zshexpn.

If the list looks ok, then re-run the second command but with rm instead of echo.

WARNING 1: It is easy to slip up with these things, and I cannot promise that I have not accidentally given you a command that wipes something you did not intend to. Please check the output of the echo command carefully. Making a backup, as suggested by another answer, isn’t a bad idea either.

WARNING 2: If there are too many files, this will exceed the maximum allowed command size, and fail. In that case, the find solution given in another answer is probably the way to go.

3
  • I so appreciate your explanation here as I level up my zsh-fu. Should the command length limit be exceeded, does the shell echo (or rm) nothing and just report argument list too long or does it do as much work with the command echo and then abort at the limit?
    – bmike
    Commented Sep 6 at 12:55
  • 1
    @bmike The shell says "argument too long" or something to that effect. It does not get as far as actually running echo (or rm). Commented Sep 6 at 13:01
  • 2
    echo would actually not run into the command line length limit since it's a builtin. If the rm command fails due to the command line length, you can load a rm builtin (and others) with zmodload -F zsh/files b:rm, or load builtins under a different name with zmodload -mF zsh/files 'b:zf_*' and run zf_rm …` instead of rm …. Alternatively, you can load ``zargs` with autoload -zU zargs then run zargs -- ./**/… -- rm to split the command into multiple calls to rm if needed. Cc @bmike Commented Sep 6 at 17:39

You must log in to answer this question.

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