This option is not posix (not like that's stopped find accumulating a dozen
extensions), but it is in gnu and freebsd (for 20 years). it's also somewhat
popular among sysadmins and blogs, etc. and perhaps most importantly, it
nicely solves one of the more troublesome caveats of find (which the man page
actually covers twice because it's so common and easy to screw up). So I think
it makes a good addition.

Code snatched from freebsd.

In passing, I'll also note that the man page example is very inefficient.
    $ find . \( -name \*.jpg -o -name \*.gif \) -exec rm {} \;
This would be much faster with +. We can fix that too, but I'll add that a lot
of the online advice suggests -delete, and if not available, -exec rm {} \;,
instead of the smarter + exec. Not surprising since even the man page gets
this wrong.

Index: extern.h
===================================================================
RCS file: /cvs/src/usr.bin/find/extern.h,v
retrieving revision 1.21
diff -u -p -r1.21 extern.h
--- extern.h    5 Oct 2015 15:25:16 -0000       1.21
+++ extern.h    3 Jan 2017 04:57:04 -0000
@@ -53,6 +53,7 @@ PLAN  *c_atime(char *, char ***, int);
 PLAN   *c_cmin(char *, char ***, int);
 PLAN   *c_cnewer(char *, char ***, int);
 PLAN   *c_ctime(char *, char ***, int);
+PLAN   *c_delete(char *, char ***, int);
 PLAN   *c_depth(char *, char ***, int);
 PLAN   *c_empty(char *, char ***, int);
 PLAN   *c_exec(char *, char ***, int);
@@ -87,5 +88,5 @@ PLAN  *c_mtime(char *, char ***, int);
 PLAN   *c_not(char *, char ***, int);
 PLAN   *c_or(char *, char ***, int);
 
-extern int ftsoptions, isdepth, isoutput, isxargs;
+extern int ftsoptions, isdelete, isdepth, isoutput, isxargs;
 extern int mayexecve;
Index: find.1
===================================================================
RCS file: /cvs/src/usr.bin/find/find.1,v
retrieving revision 1.91
diff -u -p -r1.91 find.1
--- find.1      11 Sep 2015 18:58:16 -0000      1.91
+++ find.1      3 Jan 2017 05:04:52 -0000
@@ -182,6 +182,23 @@ was started, rounded up to the next full
 .Ar n
 24-hour periods.
 .Pp
+.It Ic -delete
+Delete found files and/or directories.
+Always returns true.
+This executes
+from the current working directory as
+.Nm
+recurses down the tree.
+It will not attempt to delete a filename with a
+.Dq Pa /
+character in its pathname relative to
+.Dq Pa \&.
+for security reasons.
+Depth-first traversal processing is implied by this option.
+The
+.Ic -delete
+primary will fail to delete a directory if it is not empty.
+Following symlinks is incompatible with this option.
 .It Ic -depth
 This primary always evaluates to true.
 The same as specifying the
@@ -587,6 +604,7 @@ primaries
 .Ic -anewer ,
 .Ic -cmin ,
 .Ic -cnewer ,
+.Ic -delete ,
 .Ic -empty ,
 .Ic -execdir ,
 .Ic -flags ,
Index: find.c
===================================================================
RCS file: /cvs/src/usr.bin/find/find.c,v
retrieving revision 1.20
diff -u -p -r1.20 find.c
--- find.c      10 Oct 2015 20:35:00 -0000      1.20
+++ find.c      3 Jan 2017 04:58:29 -0000
@@ -154,9 +154,15 @@ find_execute(PLAN *plan,   /* search plan 
        int r, rval;
        PLAN *p;
 
-       if (mayexecve == 0)
-               if (pledge("stdio rpath getpw", NULL) == -1)
-                       err(1, "pledge");
+       if (mayexecve == 0) {
+               if (isdelete) {
+                       if (pledge("stdio cpath rpath getpw", NULL) == -1)
+                               err(1, "pledge");
+               } else {
+                       if (pledge("stdio rpath getpw", NULL) == -1)
+                               err(1, "pledge");
+               }
+       }
 
        rval = 0;
     
Index: find.h
===================================================================
RCS file: /cvs/src/usr.bin/find/find.h,v
retrieving revision 1.17
diff -u -p -r1.17 find.h
--- find.h      15 Mar 2015 00:41:28 -0000      1.17
+++ find.h      3 Jan 2017 04:44:39 -0000
@@ -37,7 +37,7 @@
 enum ntype {
        N_AND = 1,                              /* must start > 0 */
        N_AMIN, N_ANEWER, N_ATIME, N_CLOSEPAREN, N_CMIN, N_CNEWER, N_CTIME,
-       N_DEPTH, N_EMPTY, N_EXEC, N_EXECDIR, N_EXPR,
+       N_DELETE, N_DEPTH, N_EMPTY, N_EXEC, N_EXECDIR, N_EXPR,
        N_FLAGS, N_FOLLOW, N_FSTYPE, N_GROUP, N_INAME, N_INUM, N_LINKS, N_LS,
        N_MMIN, N_MAXDEPTH,
        N_MINDEPTH, N_MTIME, N_NAME, N_NEWER, N_NOGROUP, N_NOT, N_NOUSER,
Index: function.c
===================================================================
RCS file: /cvs/src/usr.bin/find/function.c,v
retrieving revision 1.44
diff -u -p -r1.44 function.c
--- function.c  18 Apr 2015 18:28:37 -0000      1.44
+++ function.c  3 Jan 2017 05:00:20 -0000
@@ -295,6 +295,63 @@ c_depth(char *ignore, char ***ignored, i
 
        return (palloc(N_DEPTH, f_always_true));
 }
+
+/*
+ * -delete funcions
+ */
+int
+f_delete(PLAN *plan, FTSENT *entry)
+{
+
+       /* can't delete these */
+       if (strcmp(entry->fts_accpath, ".") == 0 ||
+           strcmp(entry->fts_accpath, "..") == 0)
+               return 1;
+
+       /* sanity check */
+       if (isdepth == 0 ||                     /* depth off */
+           (ftsoptions & FTS_NOSTAT))          /* not stat()ing */
+               errx(1, "-delete: insecure options got turned on");
+       if (!(ftsoptions & FTS_PHYSICAL) ||     /* physical off */
+           (ftsoptions & FTS_LOGICAL))         /* or finally, logical on */
+               errx(1, "-delete: forbidden when symlinks are followed");
+
+       /* Potentially unsafe - do not accept relative paths whatsoever */
+       if (entry->fts_level > FTS_ROOTLEVEL &&
+           strchr(entry->fts_accpath, '/') != NULL)
+               errx(1, "-delete: %s: relative path potentially not safe",
+                   entry->fts_accpath);
+#if 0
+       /* Turn off user immutable bits if running as root */
+       if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) &&
+           !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) &&
+           geteuid() == 0)
+               lchflags(entry->fts_accpath,
+                   entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE));
+#endif
+       /* rmdir directories, unlink everything else */
+       if (S_ISDIR(entry->fts_statp->st_mode)) {
+               if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY)
+                       warn("-delete: rmdir(%s)", entry->fts_path);
+       } else {
+               if (unlink(entry->fts_accpath) < 0)
+                       warn("-delete: unlink(%s)", entry->fts_path);
+
+       }
+
+       return 1;
+}
+
+PLAN *
+c_delete(char *ignore, char ***ignored, int unused)
+{
+       ftsoptions &= ~FTS_NOSTAT;
+       isoutput = 1;
+       isdelete = 1;
+       isdepth = 1;
+
+       return (palloc(N_DELETE, f_delete));
+}
  
 /*
  * -empty functions --
Index: main.c
===================================================================
RCS file: /cvs/src/usr.bin/find/main.c,v
retrieving revision 1.30
diff -u -p -r1.30 main.c
--- main.c      18 May 2014 08:10:00 -0000      1.30
+++ main.c      3 Jan 2017 04:57:34 -0000
@@ -48,6 +48,7 @@
 time_t now;                    /* time find was run */
 int dotfd;                     /* starting directory; may be -1 */
 int ftsoptions;                        /* options for the fts_open(3) call */
+int isdelete;                  /* user specified -delete operator */
 int isdepth;                   /* do directories on post-order visit */
 int isoutput;                  /* user specified output operator */
 int isxargs;                   /* don't permit xargs delimiting chars */
Index: option.c
===================================================================
RCS file: /cvs/src/usr.bin/find/option.c,v
retrieving revision 1.19
diff -u -p -r1.19 option.c
--- option.c    5 Oct 2015 15:25:16 -0000       1.19
+++ option.c    3 Jan 2017 04:41:50 -0000
@@ -59,6 +59,7 @@ static OPTION options[] = {
        { "-cmin",      N_CMIN,         c_cmin,         O_ARGV },
        { "-cnewer",    N_CNEWER,       c_cnewer,       O_ARGV },
        { "-ctime",     N_CTIME,        c_ctime,        O_ARGV },
+       { "-delete",    N_DELETE,       c_delete,       O_ZERO },
        { "-depth",     N_DEPTH,        c_depth,        O_ZERO },
        { "-empty",     N_EMPTY,        c_empty,        O_ZERO },
        { "-exec",      N_EXEC,         c_exec,         O_ARGVP },

Reply via email to