strgeon

定制自己的ls命令
前言在Linux系统中,一切皆为文件。前段时间在Linux课上学习了Linux系统的底层调用,由此可以来通过C/C...
扫描右侧二维码阅读全文
11
2018/07

定制自己的ls命令

前言

在Linux系统中,一切皆为文件。
前段时间在Linux课上学习了Linux系统的底层调用,由此可以来通过C/C++来编写自己的Linux命令来对目录文件进行读写。

定制自己的ls命令就是通过命令行参数(不会的自行百度|Google,就是运行时传入的参数),选择选项然后输出某个目录下面的内容。


基础知识

文件描述符

每个打开的文件都被分配一个文件描述符。文件描述符是一个非负整数。linux内核通过文件描述符来访问文件。前三个描述符具有特定含义:

  • 0 标准输入(stdin) --键盘
  • 1 标准输出(stdout) --显示器
  • 2 标准错误(stderr) --默认在终端



    1. 如果打开的文件没有,那么fd为-1,可由此判断打开是否成功。
    2. fd的值慢慢增加的,从3开始,因为0,1,2已经被占用。
    3. 注意文件描述符(int)和文件流(FILE*)之间的区别。

目录dirent结构

#include <sys/types.h>
#include <dirent.h>
struct dirent{
 ino_t d_ino;
 off_t d_off;
 unsigned short d_reclen;
 unsigned char d_type;
 char d_name[NAME_MAX+1];
}
d_ino: 当前目录的inode节点号;
d_off:目录文件首部到下个dirent结构的位移;
d_reclen:该记录的长度;
d_type:目录文件类型;
d_name:目录文件名;

文件信息stat结构

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
struct stat{
 dev_t  st_dev;
 ino_t  st_ino;
 mode_t st_mode;
 nlink_t    st_nlink;
 uid_t  st_uid;
 gid_t  st_gid;
 dev_t  st_rdev;
 off_t  st_size;
 unsigned long  st_blksize;
 unsigned long  st_blocks;
 time_t st_atime;
 time_t st_mtime;
 time_t st_ctime;
}
st_dev:文件所处的磁盘设备ID号;
st_ino:文件的inode索引号;
st_mode:文件访问权限和文件类型;
st_nlink:文件的硬链接数;
st_uid:文件所属的用户ID;
st_gid:文件所属的组ID;
st_rdev:如果文件是字符或块设备,该值给出该设备的标识符信息;
st_size:对常规文件,该值为文件大小;对符号链接,该值为该符号链接所指向的目录长度;对设备文件,该值为0;
st_blksize:文件系统存储文件的块大小;
st_blocks:分配给文件的块数;
st_atime:文件的最近访问时间;
st_mtime:文件内容的最近修改时间;
st_ctime:文件属性的最近修改时间;

系统调用

打开关闭读写文件这里仅做记录,在本次内容并没有使用到。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int open(char *fileName,int mode[,int permissions])
int close(int fd)
ssize_t read(int fd, void* buf, size_t count)
ssize_t write(int fd, void* buf, size_t count)


详细设计及代码

文件结构

- src
    * main.c
    * myls.h
    * myls.c

流程

1.在main.c中判断传入的命令行参数是否符合要求。如果没有传入路径根据getwcd(char *buffer,size_t size)函数来获得当前路径。

2.在myls.h中声明两个函数分别为void printdir(char *dir,char option)和void printdetail(struct stat st)来进行调用。

3.printdir():使用全局变量来保存用户选择的选项,是否输出详细信息或者隐藏文件。涉及到的系统调用有:
DIR结构体:保存一个opendir()得到的内容
dirent结构体:存放了目录的信息,有文件名,目录文件类型,节点号和下一个结构的位移等。

4.chdir(dir)切换到该目录下。然后使用readdir(dp)来进行对该目录的循环读取,如果该读到文件是一个隐藏文件,那么判断给出的选项是否需要隐藏(即判断文件名的第一个字母是否则为'.'),还有是否需要输出详细信息的内容。

代码

感觉写的真丑,好久以前的。

//myls.h
#include<stdio.h>
#include<stdlib.h>

#include<unistd.h>
#include<dirent.h>
#include<string.h>
#include<sys/stat.h>

void printdir(char *dir,char option);
void printdetail(struct stat st);
//main.c
#include"myls.h"

int main(int argc,char **argv)
{
    char dir[256];
    if(argc==1)
    {//当没有命令行参数传入的情况
        if((getcwd(dir,256))!= NULL)
            printdir(dir,'n');
        else
         fprintf(stderr,"cannot open current dierectory:%s\n",dir);
    }else if(argc==2)
    {//支持两种参数
        if(argv[1][0]=='-')
        {//加入选项的模式
            if((getcwd(dir,256))!= 0)
                printdir(dir,argv[1][1]);
            else
                fprintf(stderr,"cannot open current dierectory:%s\n",dir);
        }else
        {//传入绝对路径
            printdir(argv[1],'n');
        }
    }else if(argc==3)
    {
        printdir(argv[2],argv[1][1]);
    }else
        printf("please check your input\n");
// printdir("/home/xuan/homework",0);
}
//myls.c
#include"myls.h"

void printdetail(struct stat st)
{//输出文件的详细信息
    int i;
    switch(st.st_mode & S_IFMT)
    {
    case S_IFREG: printf("-"); break;
    case S_IFDIR: printf("d"); break;
    case S_IFLNK: printf("l"); break;
    case S_IFBLK: printf("b"); break;
    case S_IFCHR: printf("c"); break;
    case S_IFIFO: printf("p"); break;
    case S_IFSOCK: printf("s"); break;
    }
    for(i = 8; i >= 0; i--)
    {
        if(st.st_mode & (1 << i))
        {
            switch(i%3)
            {
            case 2: printf("r"); break;
            case 1: printf("w"); break;
            case 0: printf("x"); break;
            }
        }
        else
            printf("-");
    }
}

void printdir(char *dir,char option) //option传递默认参数为n
{
    DIR *dp;
    struct dirent *entry;
    struct stat statbuf;

    if((dp=opendir(dir)) == NULL) //打开文件流指针
    {
        fprintf(stderr,"cannot open directory:%s\n",dir);
        return;
    }
    chdir(dir); //切换到该目录
    while((entry=readdir(dp)) != NULL)
    {
        lstat(entry->d_name,&statbuf);
        //把目录中的文件名得到statbuf
        if(S_ISDIR(statbuf.st_mode))
        {
            if(entry->d_name[0]=='.')
                if(option!='a')
                    continue;
            if(option=='l')
            {
                printdetail(statbuf);
                printf(" %s/\n",entry->d_name);
            }else
                printf("%s/ ",entry->d_name);
        }
        if(S_ISREG(statbuf.st_mode))
        {
            if(entry->d_name[0]=='.')
                if(option!='a')
                    continue;
            if(option=='l')
            {
                printdetail(statbuf);
                printf(" %s\n",entry->d_name);

            }else
                printf("%s ",entry->d_name);
        }
    }
    printf("\n");
    chdir("..");
    closedir(dp);
}

编译和运行

使用gcc进行编译之后即可得到一个执行程序,进行执行

$ gcc main.c myls.c -o myls
$ ./myls
这样就可以显示当前目录的内容
当然也还可以加入参数,显示home下的内容
$ ./myls /home/
显示/home/xuan下的详细信息
$ ./myls -a /home/xuan
还可以放到系统命令中的话可以直接调用
$ sudo cp myls /usr/bin
$ myls

在写了myls的命令之后还写了点其他的命令,比如mydir通过系统调用来递归遍历文件夹之后层次性输出等,方法也都大同小异,也就不过多写了。

最后修改:2018 年 08 月 10 日 03 : 57 PM
如果觉得我的文章对你有用,请随意赞赏

发表评论