自己动手写shell命令之ls:让你更好地管理文件
$ mkdir my_ls$ cd my_ls然后创建一个名为my_ls.c的源代码文件,$ touch my_ls.c$ vim my_ls.c在编辑器中输入以下代码:
在日常使用计算机时,我们经常需要进行文件的管理和操作。而在Linux系统中,通过命令行来完成这些操作是一种非常高效的方式。其中,ls命令是最基础、最常用的一个。
本文将从零开始,教大家如何自己动手写一个ls命令,并且实现一些简单但实用的功能。
1. 基础语法
首先,我们来看一下ls命令的基础语法:
“`
$ ls [OPTION]… [FILE]…
其中OPTION表示可选参数,FILE表示要操作的文件名或目录名。如果没有指定FILE,则默认为当前目录。
例如:
$ ls
这条命令会列出当前目录下所有文件和子目录。
2. 实现基本功能
接下来我们就可以开始编写代码了。首先创建一个空白文件夹my_ls,并进入该目录:
$ mkdir my_ls
$ cd my_ls
然后创建一个名为my_ls.c的源代码文件,并编辑它:
$ touch my_ls.c
$ vim my_ls.c
在编辑器中输入以下代码:
“`c
#include
#include
int main(int argc, char *argv[]) {
DIR *dir;
struct dirent *entry;
if (argc == 1) {
dir = opendir(“.”);
} else {
dir = opendir(argv[1]);
}
while ((entry = readdir(dir)) != NULL) {
printf(“%sn”, entry->d_name);
closedir(dir);
return 0;
}
上述代码使用了两个头文件,分别是stdio.h和dirent.h。其中,stdio.h主要是为了使用printf函数,dirent.h则提供了操作目录的相关函数。
在main函数中,首先判断是否有传入参数。如果没有,则打开当前目录;否则打开指定的目录。然后通过readdir函数遍历该目录下的所有文件和子目录,并输出它们的名称。
保存并退出编辑器后,我们就可以编译这个程序:
$ gcc -o my_ls my_ls.c
然后运行它:
$ ./my_ls
这时会输出当前目录下所有文件和子目录的名称。
3. 添加选项
接下来我们来实现一些常用选项。例如,“-a”表示列出所有文件(包括隐藏文件),“-l”表示以长格式显示每个文件的详细信息等等。
为了实现这些选项,我们需要修改一下代码,在main函数中添加处理命令行参数的部分:
#include
#include
#include
#include
bool is_option(const char *arg) {
return arg[0] == ‘-‘;
bool has_option(const char *options, char option) {
return strchr(options, option) != NULL;
void print_file_info(const char *filename, const struct stat *info) {
printf(“%c”, S_ISDIR(info->st_mode) ? ‘d’ : ‘-‘);
printf(“%c%c%c”, info->st_mode & S_IRUSR ? ‘r’ : ‘-‘, info->st_mode & S_IWUSR ? ‘w’ : ‘-‘, info->st_mode & S_IXUSR ? ‘x’ : ‘-‘);
printf(“%c%c%c”, info->st_mode & S_IRGRP ? ‘r’ : ‘-‘, info->st_mode & S_IWGRP ? ‘w’ : ‘-‘, info->st_mode & S_IXGRP ? ‘x’ : ‘-‘);
printf(“%c%c%c “, info->st_mode & S_IROTH ? ‘r’ : ‘-‘, info->st_mode & S_IWOTH ? ‘w’ : ‘-‘, info->st_mode & S_IXOTH ? ‘x’ : ‘-‘);
printf(“%5ld “, (long int)info->st_nlink);
struct passwd *pw = getpwuid(info.st_uid);
if (pw != NULL) {
printf(“%-8s “, pw.pw_name);
} else {
printf(“UNKNOWN “);
}
![自己动手写shell命令之ls:让你更好地管理文件缩略图 自己动手写shell命令之ls:让你更好地管理文件](https://www.72715.net/wp-content/uploads/2023/05/2c18040b76f6551b77d74b638ecd87d6.png)
struct group *gr = getgrgid(info.st_gid);
if (gr != NULL) {
printf(“%-8s “, gr.gr_name);
} else {
printf(“UNKNOWN “);
}
printf(“%9lld “, (long long int)info.st_size);
char date[20];
strftime(date, 20, “%b %e %H:%M”, localtime(&info.st_mtime));
printf(“%s “, date);
// 打印文件名
if (S_ISDIR(info.st-mode)) { // 目录
printf(“33[34m%s/33[0mn”, filename);
} else if (info.st_mode & S_IXUSR || info.st_mode & S_IXGRP || info.st_mode & S_IXOTH) { // 可执行文件
printf(“33[32m%s*33[0mn”, filename);
} else { // 普通文件
printf(“%sn”, filename);
bool show_all = false;
bool show_long = false;
for (int i = 1; i < argc; ++i) {
if (is_option(argv[i])) {
for (int j = 1; j < strlen(argv[i]); ++j) {
switch (argv[i][j]) {
case ‘a’:
show_all = true;
break;
case ‘l’:
show_long = true;
default:
fprintf(stderr, “my_ls: invalid option — ‘%c’n”, argv[i][j]);
exit(1);
}
dir = opendir(argv[i]);
if (!dir) {
while ((entry == readdir(dir)) != NULL) {
// 如果不显示所有文件且是隐藏文件,则跳过
if (!show_all && entry->d_name[0] == ‘.’) continue;
char filepath[1024];
sprintf(filepath, “%s/%s”, argv[optind], entry->d_name);
struct stat filestat;
if (stat(filepath, &filestat)) continue;
if (show_long) { // 长格式输出
print_file_info(entry->d_name, &filestat);
} else { // 普通输出
printf(“%sn”, entry->d_name);
}
}
上述代码中,我们添加了两个bool类型的变量show_all和show_long,分别表示是否显示所有文件和是否以长格式输出。在处理命令行参数时,如果遇到“-a”或“-l”选项,则将相应的变量设置为true。
另外,我们还添加了一个print_file_info函数,在长格式输出时调用。该函数使用了stat函数来获取文件信息,并根据不同的类型进行不同的输出。
4. 运行效果
现在我们可以编译并运行这个程序了:
如果当前目录下有一些文件和子目录,则会按照默认方式列出它们的名称:
file1.txt
file2.txt
dir1/
dir2/
如果加上“-a”选项,则会列出所有文件(包括隐藏文件):
.
..
.file3.txt
.dir4/
如果加上“-l”选项,则会以长格式显示每个文件的详细信息:
-rw-r–r– 1 user staff