logo

utils-std

Collection of commonly available Unix tools
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:

Mcmd/rm.110++++++++--
Mcmd/rm.c11+++++++----
Mtest-cmd/rm.t14+++++++++++++-
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