You can write own implementation command "rm -rf" on pure the C programming language. Source code based only on headers: dirent.h, sys/stat.h and unistd.h. If you need portable code to other systems, as example to the Windows, you need only change headers with corresponding functionality, at the same time the algorithm will not be changed.
A file rmtree.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// POSIX dependencies
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
void
rmtree(const char path[])
{
size_t path_len;
char *full_path;
DIR *dir;
struct stat stat_path, stat_entry;
struct dirent *entry;
// stat for the path
stat(path, &stat_path);
// if path does not exists or is not dir - exit with status -1
if (S_ISDIR(stat_path.st_mode) == 0) {
fprintf(stderr, "%s: %s\n", "Is not directory", path);
exit(-1);
}
// if not possible to read the directory for this user
if ((dir = opendir(path)) == NULL) {
fprintf(stderr, "%s: %s\n", "Can`t open directory", path);
exit(-1);
}
// the length of the path
path_len = strlen(path);
// iteration through entries in the directory
while ((entry = readdir(dir)) != NULL) {
// skip entries "." and ".."
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
continue;
// determinate a full path of an entry
full_path = calloc(path_len + strlen(entry->d_name) + 1, sizeof(char));
strcpy(full_path, path);
strcat(full_path, "/");
strcat(full_path, entry->d_name);
// stat for the entry
stat(full_path, &stat_entry);
// recursively remove a nested directory
if (S_ISDIR(stat_entry.st_mode) != 0) {
rmtree(full_path);
continue;
}
// remove a file object
if (unlink(full_path) == 0)
printf("Removed a file: %s\n", full_path);
else
printf("Can`t remove a file: %s\n", full_path);
free(full_path);
}
// remove the devastated directory and close the object of it
if (rmdir(path) == 0)
printf("Removed a directory: %s\n", path);
else
printf("Can`t remove a directory: %s\n", path);
closedir(dir);
}
int
main(const int argc, char const *argv[])
{
if (argc != 2) {
fprintf(stderr, "Missing single operand: path\n");
return -1;
}
rmtree(argv[1]);
return 0;
}
Examine it.
I am use a shell script for generation a file/folder structure.
$ cat script.sh
mkdir -p dir1/{dir1.1,dir1.2,dir1.3}
mkdir -p dir1/dir1.2/{dir1.2.1,dir1.2.2,dir1.2.3}
mkdir -p dir2/{dir2.1,dir2.2}
mkdir -p dir2/dir2.2/dir2.2.1
mkdir -p dir2/dir2.2/{dir2.2.1,dir2.2.2}
mkdir -p dir3/dir3.1
mkdir -p dir4
mkdir -p dir5
touch dir1/dir1.1/file.scala
touch dir1/dir1.2/file.scala
touch dir2/dir2.2/{file.c,file.cpp}
touch dir2/dir2.2/dir2.2.2/{file.go,file.rb}
touch dir3/{file.js,file.java}
touch dir3/dir3.1/{file.c,file.cpp}
> dir4/file.py
Run the script
$ ./script.sh
Generated the file/folder structure
$ tree
.
├── dir1
│ ├── dir1.1
│ │ └── file.scala
│ ├── dir1.2
│ │ ├── dir1.2.1
│ │ ├── dir1.2.2
│ │ ├── dir1.2.3
│ │ └── file.scala
│ └── dir1.3
├── dir2
│ ├── dir2.1
│ └── dir2.2
│ ├── dir2.2.1
│ ├── dir2.2.2
│ │ ├── file.go
│ │ └── file.rb
│ ├── file.c
│ └── file.cpp
├── dir3
│ ├── dir3.1
│ │ ├── file.c
│ │ └── file.cpp
│ ├── file.java
│ └── file.js
├── dir4
│ └── file.py
├── dir5
├── rmtree.c
└── script.sh
16 directories, 13 files
Build the source code of the file rmtree.c by the GCC
$ cc -o -Wall -Werror -o rmtree rmtree.c
Remove a directory dir1/dir1.1
$ ./rmtree dir1/dir1.1
Removed a file: dir1/dir1.1/file.scala
Removed a directory: dir1/dir1.1
Remove a directory dir1/dir1.2
$ ./rmtree dir1/dir1.2
Removed a directory: dir1/dir1.2/dir1.2.3
Removed a file: dir1/dir1.2/file.scala
Removed a directory: dir1/dir1.2/dir1.2.1
Removed a directory: dir1/dir1.2/dir1.2.2
Removed a directory: dir1/dir1.2
Remove a directory dir1/
$ ./rmtree dir1
Removed a directory: dir1/dir1.3
Removed a directory: dir1
Remove a directory dir2/dir2.2/dir2.2.2
$ ./rmtree dir2/dir2.2/dir2.2.2
Removed a file: dir2/dir2.2/dir2.2.2/file.rb
Removed a file: dir2/dir2.2/dir2.2.2/file.go
Removed a directory: dir2/dir2.2/dir2.2.2
Remove a directory dir2/
$ ./rmtree dir2
Removed a directory: dir2/dir2.1
Removed a file: dir2/dir2.2/file.c
Removed a directory: dir2/dir2.2/dir2.2.1
Removed a file: dir2/dir2.2/file.cpp
Removed a directory: dir2/dir2.2
Removed a directory: dir2
Remove a directory dir3/dir3.1
$ ./rmtree dir3/dir3.1
Removed a file: dir3/dir3.1/file.c
Removed a file: dir3/dir3.1/file.cpp
Removed a directory: dir3/dir3.1
Remove a directory dir3
$ ./rmtree dir3
Removed a file: dir3/file.js
Removed a file: dir3/file.java
Removed a directory: dir3
Remove a directory dir4
$ ./rmtree dir4
Removed a file: dir4/file.py
Removed a directory: dir4
Remove a empty directory dir5
$ ./rmtree dir5
Removed a directory: dir5
If a passed path is not exists or is not path of a directory, you will can see next:
$ ./rmtree rmtree.c
Is not directory: rmtree.c
$ ./rmtree 11111111111111111
Is not directory: 11111111111111111
See results
$ tree
.
├── rmtree
├── rmtree.c
└── script.sh
0 directories, 3 files
Testing environment
$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 8.7 (jessie)
Release: 8.7
Codename: jessie
$ uname -a
Linux localhost 3.16.0-4-amd64 #1 SMP Debian 3.16.36-1+deb8u2 (2016-10-19) x86_64 GNU/Linux
$ cc --version
cc (Debian 4.9.2-10) 4.9.2