博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
linux 目录遍历
阅读量:4139 次
发布时间:2019-05-25

本文共 7135 字,大约阅读时间需要 23 分钟。

在linux 中,如何遍历指定目录下的所有文件夹呢?

要求能搜索结果中包含隐藏文件夹

脚本名:ergodic_folder.sh

脚本内容:

Shell代码  
  1. #!/bin/sh  
  2. list_alldir(){  
  3.     for file2 in `ls -a $1`  
  4.     do  
  5.         if [ x"$file2" != x"." -a x"$file2" != x".." ];then  
  6.             if [ -d "$1/$file2" ];then  
  7.                 echo "$1/$file2"  
  8.                 list_alldir "$1/$file2"  
  9.             fi  
  10.         fi  
  11.     done  
  12. }  
  13.   
  14. list_alldir ./test  
 

测试如下:

 

[root@localhost whuang]# ./ergodic_folder.sh

./test/.abc

./test/.abc/.ccc

./test/bbb

在linux下遍历某一目录下内容LINUX下历遍目录的方法一般是这样的
打开目录->读取->关闭目录
相关函数是opendir -> readdir -> closedir,其原型如下:
#include <dirent.h>
DIR *opendir(const char *dirname);
struct dirent *readdir(DIR *dirp);
int closedir(DIR *dirp);
简单列举一例:

  1. #include<dirent.h>    
  2.   
  3.         struct dirent* ent = NULL;
  4.         DIR *pDir;
  5.         
  6.         if( (pDir=opendir("/home/test")) == NULL)
  7.         {

  8.                 printf("open dir %s failed\n", pszBaseDir);
  9.                 return false;
  10.         }

  11.         while( (ent=readdir(pDir)) != NULL )
  12.         {

  13.               printf("the ent->d_reclen is%d the ent->d_type is%d the ent->d_name is%s\n", ent->d_reclen, ent->d_type, ent->d_name);  
  14.          }

  15.         closedir(pDir);
复制代码
dirent的结构体:
  1. On Linux, the dirent structure is defined as follows: 

  2. struct dirent {

  3.     ino_t          d_ino;       /* inode number */
  4.     off_t          d_off;       /* offset to the next dirent */
  5.     unsigned short d_reclen;    /* length of this record */
  6.     unsigned char  d_type;      /* type of file */
  7.     char           d_name[256]; /* filename */
  8. };
复制代码
其中inode表示存放的是该文件的结点数目(具体可了解linux下的文件系统),d_off 是文件在目录中的编移,这两个基本很少用。

d_type表示档案类型:


enum
{

    DT_UNKNOWN = 0,
# define DT_UNKNOWN DT_UNKNOWN
    DT_FIFO = 1,
# define DT_FIFO DT_FIFO
    DT_CHR = 2,
# define DT_CHR DT_CHR
    DT_DIR = 4,
# define DT_DIR DT_DIR
    DT_BLK = 6,
# define DT_BLK DT_BLK
    DT_REG = 8,
# define DT_REG DT_REG
    DT_LNK = 10,
# define DT_LNK DT_LNK
    DT_SOCK = 12,
# define DT_SOCK DT_SOCK
    DT_WHT = 14
# define DT_WHT DT_WHT
};

d_reclen认为是纪录的长度,计算方式应该是4(d_ino)+4(d_off)+2(d_reclen)+1(d_type)+1(补齐位)+4N(d_name会自动补齐:1.jpg为8,12.jpg也为8,1234.jpg也为8,12345.jpg则为12);所以一般d_reclen是20和24(其中.和..是16)。

d_name表示文件名,如test.jpg

1.  linux提供opendirreaddir(readdir_r)closedir和scandir等接口实现对目录的读取;

2.  readdir返回指向下一个目录项的指针,如果要自己传入缓冲区存储目录项,应使用readdir_r代替。readdir的结果中包含当前目录和上一级目录的目录项信息。

3.  在遍历过程中,进程的工作目录不会改变,在递归遍历的时候,需要改变工作目录(chdir)以识别相对路径,或者每次都限定全局路径。

4.  深度优先遍历目录树采用递归实现易编码(参见如下代码),广度优先遍历则需借助队列实现。当目录下的文件数量较少时,采用广度优先遍历效率会更高,因目录下的目录项基本都是连续存放,减少了很多磁盘寻道;而采用深度优先遍历,结果的聚合性更高。

 

  1. int dir_traverse(const char *dir_name)
  2. {
  3.     DIR *dirp = opendir(dir_name);
  4.     if(!dirp) {
  5.         perror("opendir");
  6.         return -1;
  7.     }
  8.     struct stat st;
  9.     struct dirent *dir;
  10.     char fullpath[FILENM_MAX];
  11.     while((dir = readdir(dirp)) != NULL) {
  12.         if(!strcmp(dir->d_name, ".") || // 考虑当前目录和上级目录,否则会死循环
  13.            !strcmp(dir->d_name, "..")) {
  14.             continue;
  15.         } 
  16.         sprintf(fullpath, "%s/%s", dir_name, dir->d_name); //获取全局路径
  17.         printf("%s\n", fullpath); // 打印路径
  18.         if(lstat(fullpath, &st) < 0) {
  19.             perror("lstat");
  20.             continue;
  21.         }
  22.         if(S_ISDIR(st.st_mode)) {
  23.             dir_traverse(fullpath); // 递归遍历子目录
  24.         }
  25.     }
  26.     closedir(dirp);
  27.     return 0;
  28. }

访问目录下某个文件时,需要逐个读取目录数据中的目录项并与目标进行匹配获得文件的inode号,假设文件的平均长度为10byte,加上inode、type及reclen等信息,每个目录项的平均长度为16byte,假设采用4K的数据块,则一个块可以存放256个目录项,按照ext2文件数据索引的方式,当目录下文件数n少于256*12时,则在目录下查找文件最多需要访问n/256(向上取整)个数据块,当目录下文件数更多的时候,需要访问的块数会更快的增加(后面得到存储数据的物理块号需要多级索引),这也是在目录下不应放太多文件的原因,如果将拥有很多文件的目录均分成多个子目录,多一级目录会多一次(或多次,具体依赖于子目录下文件数量)磁盘块访问,但在子目录中查找文件的磁盘访问开销会小很多。

stat函数讲解


表头文件:    #include <sys/stat.h>

             #include <unistd.h>

定义函数:    int stat(const char *file_name, struct stat *buf);

函数说明:    通过文件名filename获取文件信息,并保存在buf所指的结构体stat中

返回值:      执行成功则返回0,失败返回-1,错误代码存于errno


错误代码:

    ENOENT         参数file_name指定的文件不存在

    ENOTDIR        路径中的目录存在但却非真正的目录

    ELOOP          欲打开的文件有过多符号连接问题,上限为16符号连接

    EFAULT         参数buf为无效指针,指向无法存在的内存空间

    EACCESS        存取文件时被拒绝

    ENOMEM         核心内存不足

    ENAMETOOLONG   参数file_name的路径名称太长



#include <sys/stat.h>

#include <unistd.h>

#include <stdio.h>

int main() {

    struct stat buf;

    stat("/etc/hosts", &buf);

    printf("/etc/hosts file size = %d\n", buf.st_size);

}

-----------------------------------------------------

struct stat {

    dev_t         st_dev;       //文件的设备编号

    ino_t         st_ino;       //节点

    mode_t        st_mode;      //文件的类型和存取的权限

    nlink_t       st_nlink;     //连到该文件的硬连接数目,刚建立的文件值为1

    uid_t         st_uid;       //用户ID

    gid_t         st_gid;       //组ID

    dev_t         st_rdev;      //(设备类型)若此文件为设备文件,则为其设备编号

    off_t         st_size;      //文件字节数(文件大小)

    unsigned long st_blksize;   //块大小(文件系统的I/O 缓冲区大小)

    unsigned long st_blocks;    //块数

    time_t        st_atime;     //最后一次访问时间

    time_t        st_mtime;     //最后一次修改时间

    time_t        st_ctime;     //最后一次改变时间(指属性)

};


先前所描述的st_mode 则定义了下列数种情况:

    S_IFMT   0170000    文件类型的位遮罩

    S_IFSOCK 0140000    scoket

    S_IFLNK 0120000     符号连接

    S_IFREG 0100000     一般文件

    S_IFBLK 0060000     区块装置

    S_IFDIR 0040000     目录

    S_IFCHR 0020000     字符装置

    S_IFIFO 0010000     先进先出


    S_ISUID 04000     文件的(set user-id on execution)位

    S_ISGID 02000     文件的(set group-id on execution)位

    S_ISVTX 01000     文件的sticky位


    S_IRUSR(S_IREAD) 00400     文件所有者具可读取权限

    S_IWUSR(S_IWRITE)00200     文件所有者具可写入权限

    S_IXUSR(S_IEXEC) 00100     文件所有者具可执行权限


    S_IRGRP 00040             用户组具可读取权限

    S_IWGRP 00020             用户组具可写入权限

    S_IXGRP 00010             用户组具可执行权限


    S_IROTH 00004             其他用户具可读取权限

    S_IWOTH 00002             其他用户具可写入权限

    S_IXOTH 00001             其他用户具可执行权限


    上述的文件类型在POSIX中定义了检查这些类型的宏定义:

    S_ISLNK (st_mode)    判断是否为符号连接

    S_ISREG (st_mode)    是否为一般文件

    S_ISDIR (st_mode)    是否为目录

    S_ISCHR (st_mode)    是否为字符装置文件

    S_ISBLK (s3e)        是否为先进先出

    S_ISSOCK (st_mode)   是否为socket



    若一目录具有sticky位(S_ISVTX),则表示在此目录下的文件只能被该文件所有者、此目录所有者或root来删除或改名。


-----------------------------------------------------

struct statfs {

    long    f_type;          //文件系统类型

    long    f_bsize;         //块大小

    long    f_blocks;        //块多少

    long    f_bfree;         //空闲的块

    long    f_bavail;        //可用块

    long    f_files;         //总文件节点

    long    f_ffree;         //空闲文件节点

    fsid_t f_fsid;           //文件系统id

    long    f_namelen;       //文件名的最大长度

    long    f_spare[6];      //spare for later

};


stat、fstat和lstat函数(UNIX)



#include<sys/types.h>

#include<sys/stat.h>

int stat(const char *restrict pathname, struct stat *restrict buf);

提供文件名字,获取文件对应属性。感觉一般是文件没有打开的时候这样操作。

int fstat(int filedes, struct stat *buf);

通过文件描述符获取文件对应的属性。文件打开后这样操作

int lstat(const char *restrict pathname, struct stat *restrict buf);

连接文件


三个函数的返回:若成功则为0,若出错则为-1

给定一个pathname,stat函数返回一个与此命名文件有关的信息结构,fstat函数获得已在描述符filedes上打开的文件的有关信息。lstat函数类似于stat,但是当命名的文件是一个符号连接时,lstat返回该符号连接的有关信息,而不是由该符号连接引用的文件的信息。


第二个参数是个指针,它指向一个我们应提供的结构。这些函数填写由buf指向的结构。该结构的实际定义可能随实现而有所不同,但其基本形式是:


struct stat{

mode_t st_mode;   /*file tpye &mode (permissions)*/

ino_t st_ino;     /*i=node number (serial number)*/

dev_t st_rdev;   /*device number for special files*/

nlink_t st_nlink; /*number of links*/

uid_t    st_uid; /*user id of owner*/

gid_t    st_gid; /*group ID of owner*/

off_t   st_size; /*size in bytes for regular files*/

time_t st_atime; /*time of last access*/

time_t st_mtime; /*time of last modification*/

time_t st_ctime; /*time of last file status change*/

long st_blksize; /*best I/O block size */

long st_blocks; /*number of 512-byte blocks allocated*/

};

   注意,除最后两个以外,其他各成员都为基本系统数据类型。我们将说明此结构的每个成员以了解文件属性。

  

使用stat函数最多的可能是ls-l命令,用其可以获得有关一个文件的所有信息。

1 函数都是获取文件(普通文件,目录,管道,socket,字符,块()的属性。

函数原型

#include <sys/stat.h>


int stat(const char *restrict pathname, struct stat *restrict buf);

提供文件名字,获取文件对应属性。

int fstat(int filedes, struct stat *buf);

通过文件描述符获取文件对应的属性。

int lstat(const char *restrict pathname, struct stat *restrict buf);

连接文件描述命,获取文件属性。

2 文件对应的属性

struct stat {

        mode_t     st_mode;       //文件对应的模式,文件,目录等

        ino_t      st_ino;       //inode节点号

        dev_t      st_dev;        //设备号码

        dev_t      st_rdev;       //特殊设备号码

        nlink_t    st_nlink;      //文件的连接数

        uid_t      st_uid;        //文件所有者

        gid_t      st_gid;        //文件所有者对应的组

        off_t      st_size;       //普通文件,对应的文件字节数

        time_t     st_atime;      //文件最后被访问的时间

        time_t     st_mtime;      //文件内容最后被修改的时间

        time_t     st_ctime;      //文件状态改变时间

        blksize_t st_blksize;    //文件内容对应的块大小

        blkcnt_t   st_blocks;     //伟建内容对应的块数量

      };


可以通过上面提供的函数,返回一个结构体,保存着文件的信息。

转载地址:http://mdhvi.baihongyu.com/

你可能感兴趣的文章
Vue动态生成el-checkbox点击无法选中的解决方法
查看>>
MySQL Tricks1
查看>>
python 变量作用域问题(经典坑)
查看>>
pytorch
查看>>
pytorch(三)
查看>>
ubuntu相关
查看>>
C++ 调用json
查看>>
nano中设置脚本开机自启动
查看>>
动态库调动态库
查看>>
Kubernetes集群搭建之CNI-Flanneld部署篇
查看>>
k8s web终端连接工具
查看>>
手绘VS码绘(一):静态图绘制(码绘使用P5.js)
查看>>
手绘VS码绘(二):动态图绘制(码绘使用Processing)
查看>>
基于P5.js的“绘画系统”
查看>>
《达芬奇的人生密码》观后感
查看>>
论文翻译:《一个包容性设计的具体例子:聋人导向可访问性》
查看>>
基于“分形”编写的交互应用
查看>>
《融入动画技术的交互应用》主题博文推荐
查看>>
链睿和家乐福合作推出下一代零售业隐私保护技术
查看>>
Unifrax宣布新建SiFAB™生产线
查看>>