自己动手写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:让你更好地管理文件

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