commit: 33ce03eb7634e16c414b12f00c5ae78c794c8a56
parent a952e5be0a75ee5d8cce4008ccff44b2a9050c8e
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Fri, 12 Apr 2024 19:50:56 +0200
cmd/rm: Add support for -d option
Diffstat:
3 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/cmd/rm.1 b/cmd/rm.1
@@ -9,7 +9,7 @@
.Nd remove files and directories
.Sh SYNOPSIS
.Nm
-.Op Fl firRv
+.Op Fl dfirRv
.Op Ar files ...
.Sh DESCRIPTION
The
@@ -28,6 +28,8 @@ Care should be taken when using other programs as
only fails when the containing directory isn't writable, rather than the file itself.
.Sh OPTIONS
.Bl -tag -width Ds
+.It Fl d
+Remove empty directories, by skipping recursion to empty them first.
.It Fl f
Force: Never prompt before recursing into directories and removing files, non-existing files do not change exit status nor produce diagnostic messages.
Overrides
@@ -46,6 +48,10 @@ Verbose: Print when a file got removed
.Sh STANDARDS
.Nm
should be compliant with
-.St -p1003.1-2008
+.St -p1003.1-2008 .
+The
+.Fl fdv
+options are part of
+IEEE P1003.1-202x/D4 (“POSIX.1”).
.Sh AUTHORS
.An Haelwenn (lanodan) Monnier Aq Mt contact+utils@hacktivis.me
diff --git a/cmd/rm.c b/cmd/rm.c
@@ -23,7 +23,7 @@
#include <sys/stat.h> // chmod, fstatat, S_ISDIR
#include <unistd.h> // unlink, isatty
-bool force = false, recurse = false, verbose = false, opt_i = false;
+bool opt_d = false, force = false, recurse = false, verbose = false, opt_i = false;
char *argv0 = "rm";
// Consent therefore defaults to no
@@ -116,11 +116,11 @@ do_unlinkat(int fd, char *name, char *acc_path)
bool is_dir = S_ISDIR(stats.st_mode);
- if(is_dir)
+ if(is_dir && !opt_d)
{
if(!recurse)
{
- fprintf(stderr, "rm: Is a directory, pass -r to remove: %s\n", acc_path);
+ fprintf(stderr, "rm: Is a directory, pass -r or -d to remove: %s\n", acc_path);
return 1;
}
@@ -239,10 +239,13 @@ int
main(int argc, char *argv[])
{
int c = -1;
- while((c = getopt(argc, argv, ":firRv")) != -1)
+ while((c = getopt(argc, argv, ":dfirRv")) != -1)
{
switch(c)
{
+ case 'd':
+ opt_d = true;
+ break;
case 'f':
force = true;
break;
diff --git a/test-cmd/rm.t b/test-cmd/rm.t
@@ -33,7 +33,7 @@ POSIX rm(1p) step 2a:
$ touch no_rR.f
$ test -f no_rR.f
$ rm no_rR.d no_rR.f
- rm: Is a directory, pass -r to remove: no_rR.d
+ rm: Is a directory, pass -r or -d to remove: no_rR.d
[1]
$ test -d no_rR.d
$ test ! -e no_rR.f
@@ -132,6 +132,18 @@ POSIX, -f shouldn't return an error when no operands are passed
Usage: rm [-firRv] [files ...]
[1]
+POSIX 2024/D4.1, -d
+ $ mkdir empty_dir
+ $ rm -d empty_dir
+ $ test ! -e empty_dir
+ $ mkdir non_empty_dir
+ $ touch non_empty_dir/.keep
+ $ rm -d non_empty_dir
+ rm: Couldn't remove 'non_empty_dir': Directory not empty
+ [1]
+ $ test -e non_empty_dir
+ $ rm -fr non_empty_dir
+
Don't follow symlinks
$ touch bar
$ ln bar foo