linux 取文件路径为空_linux 路径中取文件名 - CSDN
  • windows和linux下的文件路径表示小结

    万次阅读 2015-11-19 14:34:47
    提起路径,我们都会想到“绝对路径”和“相对路径”,实际上绝对路径是一个广泛的概念,它表示的是可以唯一定位一个文件或文件夹的路径,有多种形式的路径都属于绝对路径的范畴,例如:  完整路径

    转载canchew的专栏的博客内容

    Windows路径是我们经常使用的东西,它看似简单,实际上隐含许多细节,这些都是在平常的使用过程中注意不到的。下面让我们来看看各种各样的Windows路径。 

    提起路径,我们都会想到“绝对路径”和“相对路径”,实际上绝对路径是一个广泛的概念,它表示的是可以唯一定位一个文件或文件夹的路径,有多种形式的路径都属于绝对路径的范畴,例如: 
    完整路径(Full path)

    我们平时所说的“绝对路径”通常就是指完整路径,它的格式如下:
    C:\Windows\System32\calc.exe 

    UNC(Universal Naming Convention通用命名规范)路径

    UNC路径用于在局域网上定位文件或文件夹,在通过网上邻居访问其它系统上的共享文件时使用的就是UNC路径。UNC路径的格式如下:
    \\PC101\ShareFiles\job.doc
    其中PC101是目标系统的计算机名,ShareFiles是目标系统上的共享文件夹名称。 
    以\\?\开头的路径

    在某些手工辅助杀毒的工具中可以看到以\\?\开头的路径,这种路径我还找不到正式的名称。完整路径和UNC路径都可以以\\?\开头:
    \\?\C:\Windows\System32\calc.exe
    \\?\UNC\PC101\ShareFiles\job.doc 
    相对路径(Relative path)

    上面四种路径都可以在本地或网络上绝对定位一个文件,因此属于绝对路径。另一种我们常常使用的是相对路径,这种路径需要与进程的当前文件夹(Current directory)一起合作才可以定位一个文件或文件夹。进程的当前文件夹并不是指EXE文件所在的文件夹,它是每个进程都有的一个属性。例如在命令提示符中显示的路径就是这个cmd.exe进程的当前文件夹,使用cd命令可以改变当前文件夹,无论当前文件夹如何改变,cmd.exe文件所在的文件夹总是不变的。在进程启动的时候可以指定它的当前文件夹,例如在快捷方式的属性中有一个“起始位置”输入框,这个就是进程启动后的当前文件夹。如果在启动时不指定进程的当前文件夹(直接双击EXE文件的情况),那么EXE文件的所在文件夹就会作为它的当前文件夹。 

    纯粹的相对路径格式如下: 

    Windows\System32\calc.exe
    要注意的是相对路径的开头不能有\,很多人以为有没有\都是一样的,其实不然,以\开头的路径是另外一种路径,下文会详细介绍这种路径。 

    如果在进程中操作文件时传递一个相对路径,那么进程会将当前文件夹的路径与这个相对路径合并,得到一个完整路径。例如命令提示符的当前文件夹是C:\Windows,如果执行start System32\calc.exe命令,程序就会将两个路径合并得到C:\Windows\System32\calc.exe。
    由于这样的特性,当前文件夹又称为工作文件夹,因为进程使用相对路径时都是针对该文件夹的。 

    除了上面所说的纯粹的相对路径,还有两种特殊的相对路径,为了不至于混淆,不再将它们称为相对路径。 
    以\开头的路径

    上面说过相对路径不能以\开头,一旦如此,它就表示相对于当前文件夹根目录的路径。例如进程的当前文件夹是C:\Windows,那么\System32\calc.exe就表示C:\System32\calc.exe。不论当前文件夹的路径有多深,只取根目录进行合并。可以在命令提示符中进行验证:首先将C:\Windows设置为当前文件夹,然后执行start \System32\calc.exe命令,将会提示错误,所找不到文件。再执行start \Windows\System32\calc.exe命令,这时计算器程序成功启动了,证明了上面的说法。 
    以盘符和冒号开头的路径

    这种路径就像是拼写错误的完整路径,它的格式如下:
    C:System32\calc.exe 

    盘符加冒号是一种特殊的表示法,表示的是进程在“该分区上的当前文件夹”。这里又蹦出来了一个当前文件夹,这与上文的当前文件夹是不同的。简单来说,对于每一个分区,进程都会保存在这个分区上最后使用的当前文件夹。可能解析的不够好,那么可以使用命令提示符来操作一遍以加深理解。 

    运行cmd,执行cd /d C:\Windows\System32命令进入该文件夹,然后切换到其它的分区,再执行C:calc.exe命令,可以看到计算器程序启动了。 

    我们在执行cd命令进入C:\Windows\System32文件夹后,进程便以此为当前文件夹,当切换到其余的分区时,进程先将这个文件夹的路径保存起来,再进行切换。使用盘符和冒号的形式读取指定分区上的当前文件夹。 
    路径中的限制

    除了路径格式上的限制,还有很多字符上的限制,下面一一来看看各种路径有什么限制。 

    除了以\\?\开头的路径之外,其它路径的长度都不能超过260个字符(包括结尾的\0字符),而\\?\开头的路径长度可以达到约32000个字符(系统在处理\\?\前缀的时候可能将它扩展成更长的字符串)。网络上流传的一则技巧:文件路径太长而不能被删除,可以在执行del命令时在路径前加上\\?\前缀。 

    文件或文件夹名称的最后一个字符不能为空格或句点。但在路径中,最后一个字符可以为句点,因为路径中的句点表示的是当期文件夹(这与上文的当前文件夹完全不同),两个句点表示的是上一级文件夹。以\\?\开头的路径中不能使用句点表示当前文件夹或上一级文件夹,在资源管理器的地址栏中虽然可以这么做,那是因为资源管理器对其作了处理。 

    文件或文件夹名称不能包含的字符 :
    < > : " / \ | ? *
    在路径中可以使用 : \ ?,冒号是与盘符一起使用的,问号是\\?\前缀中特有的,除此之外不能出现这两个字符;而\是文件夹分割符。虽然我们也能使用/作为分隔符,实际上这是不允许的,系统在处理路径时会将/替换成\。另外,如果路径最后一个字符是\,那么该路径表示的是文件夹;否则表示的是一个文件。 

    UNC路径的计算机名部分不能出现以下字符:
    ` ~ ! @ # $ % ^ & * ( ) = + _ [ ] { } \ | ; : . ‘ “ , < > / ?
    确实是够多的了,实际上标准的计算机名推荐只使用26个英文字母、10个数字以及-(连字符),否则可能会出现找不到计算机的情况。在更改计算机名的时候如果出现非标准字符,系统会作出提示。

     

    在Unix/Linux和windows中文件路径的表示方法是不一样的,在Unix/Linux中,路径的分隔采用正斜杠"/",比如"/home/fzu";而在Windows中,路径分隔采用反斜杠"\",比如"D:\tools\eclipse"。

    此外,由于编译器的字符串解析中还涉及到转义字符的问题,经常会出现莫名其妙的错误,所以为了避免这样不必要的麻烦,windows下的路径经常会用双反斜杠来表示,不管解析引擎是否将反斜杠解析成转义字符,最终在内存中得到的都是"\",因此写成"D:\\tools\\eclipse"是不会出问题的。


     linux相对路径的表示:
    . 表示用户所处的当前目录;
    .. 表示上级目录;
    _ 表示前一个工作目录;
    ~ 表示当前用户自己的根目录;

    ~account 表示[account]这个用户的根目录。


    展开全文
  • linux下某路径读取一个图片上传到其他地方,用File对象和FileInputSteam都是找不到文件路径不带中文,路径没有错,文件我直接cd 进去可以找到,权限wr都有,就是他妈报 路径文件和文件夹找不到的错误
  • Linux路径名查找

    千次阅读 2016-06-03 08:37:07
    对于文件系统的大部分操作(open、stat、unlink等等)都要涉及多多少少的路径解析。路劲解析是根据路径名(name string)得到对应的dentry对象,通过路径行走的方式。本文就来介绍路径行走。路径行走解释  路径是...

      对于文件系统的大部分操作(open、stat、unlink等等)都要涉及多多少少的路径解析。路劲解析是根据路径名(name string)得到对应的dentry对象,通过路径行走的方式。本文就来介绍路径行走。

    路径行走解释

      路径是由一个开始字符(根目录、当前工作目录或者fd指向的目录),和一串其他的文件名组成的,path中的每一个文件名用/分割开。名称查找(Name Lookup)就是希望根据已知的path和name的开始指针找到path中的最后一个文件名或者最后一个文件的父文件名。根据name指针找path中要求的当前父亲文件的孩子文件,如果孩子并不是最后一个path组件(component),孩子文件称为新的父文件,迭代这个过程,就像是在path上行走一样,因此被称为路径行走。
      这个过程要求父亲必须是一个目录,并且我们对父亲文件的inode节点拥有读权限。为了下一步查找把孩子文件变成父亲文件的时候,要求更多的检测规程。符号链接实质上是链接所指向文件的替代,符号链接要求递归的路径行走。挂载点必须被跟踪(因此改变vfsmount的后续路径元素引用),从挂载点路径切换到特定的安装vfsmount的根目录。这些不同的修改行为取决于具体的路径行走标志。

    路径行走必须做下面几件事:

    • 找到行走的起点
    • 执行权限和有效性检查
    • 执行目录项缓存元组上的的哈希name查找
    • 遍历挂载点
    • 遍历符号链接
    • 查找或者创建要求路径中的文件

      路径行走有两个模式:rcu_walk模式和ref_walk模式。ref-walk是传统的使用自旋锁(d_lock)去并发修改目录项。比起rcu-walk这个模式,ref-walk是操作简单,但是在在路劲行走的过程中,对于每一个目录项的操作可能需要睡眠、得到锁等等这些操作。rcu-walk是基于顺序锁的目录项查找,可以不用存储任何共享数据在目录项或inode里,查找中间的元素。rcu-walk并不适合与任何一种情况,比如说:如果文件系统必须沉睡或者执行不重要的操作,就需要切换到ref-walk模式。
      在 rcu-walk 期间将会禁止抢占,也决不能出现进程阻塞,所以其效率很高;ref-walk 会在 rcu-walk 失败、进程需要随眠或者需要取得某结构的引用计数(reference count)的情况下切换进来,很明显它的效率大大低于

    路径行走的过程总结

    路径行走到底是在找什么呢?说的简单一点,就是在找路径所指向的文件的父目录项。因为整个查找过程是一个分量一个分量的查找的,就像是在路径上行走一样,因此成为路径行走。行走结束,得到对应的dentry。

    1.中间数据

    nd变量:用来保存找到的当前分量dentry我们需要的信息,而且这个分量一定是一个普通的、真正的目录。
    path变量:既然nd变量只能保存真正目录的信息,如果我们得到了一个分量,我们在确定这个分量是真正的目录之前,找到的dentry信息就用path变量保存。
    name变量:指向路径中当前的分量开始位置

    2.大致过程

    在内核空间申请一页空间,把存储在用户空间的路径信息拷贝到内核空间,初次创建nd结构体。
    初始化nd结构体,理想情况下,nd保存的是name指针指向的分量的上一个分量信息,原因还是因为nd结构体要求保存真正的目录的信息。如果路径是绝对路径,nd结构体初始化为根目录的dentry信息,如果路径是相对路径,nd结构体初始化位当前进程cwd的dentry信息。
    开始”行走“第一个分量,这个分量信息在初始化的时候填充完成,因此第一次行走要做的工作并不多。然后移动name指针指向第二个分量,如果这个分量是普通目录,行走这个分量:

    根据分量名的hash值在内核缓冲区查找dentry
    如果没有找到,调用文件系统自身的lookup,
    

    并循环执行上面过程。
    如果这个分量是.目录或者..目录,则:

    如果是.目录,直接返回0。对于当前目录并不需要做任何操作
    如果是..目录,找到父目录的dentry,填充nd结构体
    

    如果这个分量是符号链接:
    找到符号链接指向的文件的dentry,填充nd结构体。

    代码解释

      函数user_path_parent主要执行path walking,我们下面来看看这个函数的内核实现。函数原型如下:

    static struct filename *
    user_path_parent(int dfd, const char __user *path, struct nameidata *nd, 
             unsigned int flags)

      函数返回值是filename结构体,参数分别是系统调用传入的dfd参数,存储在用户空间的path,带填充的nameidata结构体(这个结构体在路径行走的过程中,主要用来保存一些临时数据),最后是一个标志位。我们看到参数里面有一个nameidata结构体,这个结构体在整个路径行走过程中至关重要,所以还是在看函数之前还是看看这个结构体。定义如下:

    struct nameidata {
        struct path path;               //存储文件挂载点和dentry地址
        struct qstr last;               //路径名最后分量
        struct path root;               //存在文件所在文件系统根的信息
        struct inode    *inode;         //path.dentry.d_inode 
        unsigned int    flags;          //标志
        unsigned    seq, m_seq;      //seq 是相关目录项的顺序锁序号; m_seq 是相关文件系统(其实是mount)的顺序锁序号
        int     last_type;          //路径最后的文件类型
        unsigned    depth;              //解析符号链接过程中的递归深度
        char *saved_names[MAX_NESTED_LINKS + 1];            //相应递归深度的符号链接的路径
    };
    //其中type的取值是一个枚举类型,如下
    enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
    //分别代表了普通文件,根文件,.文件,..文件和符号链接文件

      下面我们还需要看看filename结构体是什么样子,因为我们需要分析的函数的返回值是这个类型的,我们必须知道我们的函数是返回了一个什么东西。定义如下:

    struct filename {
            const char      *name;    //真实的名字
        const __user char   *uptr;       //原来的用户指针
        struct audit_names  *aname;
        bool            separate;       //name是不是应该被释放
    };
    

      这里面的前两个指针变量不是很理解,没关系,等会分析完函数就明白了。接下来我们才来分析user_path_parent函数,函数代码如下:

    struct filename *s = getname(path);
        int error;
    
        /* only LOOKUP_REVAL is allowed in extra flags */
        //设置LOOKUP_REVAL标志
        flags &= LOOKUP_REVAL;
    
        if (IS_ERR(s))
            return s;
    
        error = filename_lookup(dfd, s, flags | LOOKUP_PARENT, nd);
        if (error) {
            putname(s);
            return ERR_PTR(error);
        }
    
        return s;

      很明显,可以看到除了一些出错处理,函数user_path_parent函数主要调用了getname函数和filename_loopup函数。所以我们还是分析这两个函数。显示分析getname函数:

        result = __getname();
        if (unlikely(!result))
            return ERR_PTR(-ENOMEM);
    
        /*
         * First, try to embed the struct filename inside the names_cache
         * allocation
            */
        kname = (char *)result + sizeof(*result);
        result->name = kname;
        result->separate = false;
        max = EMBEDDED_NAME_MAX;
    recopy:
        len = strncpy_from_user(kname, filename, max);
        if (unlikely(len < 0)) {
            err = ERR_PTR(len);
            goto error;
        }

      函数getname第一步就是调用__getname函数,为了避免本文边的很冗长,这个函数我们暂且不具体分析。他的功能就是在内核空间分配4096字节的空间,然后把地址赋给result,kname保存分配到的这篇空间的尾地址,并且把这个地址赋给filename的name成员变量,这里我们可以猜测一下,不管将来name里面保存了什么变量,肯定是存储在这片空间的末尾的,并且设置这个name不能被释放。EMBEDDED_NAME_MAX这个宏变量的是这页空间存储完filename结构体以后,还剩多少字节空间,把这个值赋给max变量。开始把存储在用户空间的path拷贝到内核空间,并且返回拷贝字节数,并且判断拷贝动作是否成功,若出错进行出错处理。

     if (len == EMBEDDED_NAME_MAX && max == EMBEDDED_NAME_MAX) {
            kname = (char *)result;
            result = kzalloc(sizeof(*result), GFP_KERNEL);
            if (!result) {
                err = ERR_PTR(-ENOMEM);
                result = (struct filename *)kname;
                goto error;
            }
    
            result->name = kname;
            result->separate = true;
    
            max = PATH_MAX;
        /* The empty path is special. */
        if (unlikely(!len)) {
            if (empty)
                *empty = 1;
            err = ERR_PTR(-ENOENT);
            if (!(flags & LOOKUP_EMPTY))
                goto error;
        }
    
        err = ERR_PTR(-ENAMETOOLONG);
        if (unlikely(len >= PATH_MAX))
            goto error;
    
        result->uptr = filename;
        result->aname = NULL;
        audit_getname(result);
        return result;
    

      判断如果拷贝到内核空间的path把新分配的这页占满了,kname指向这个空间开始的位置(kname时刻指向path开始的位置),然后调用kalloc函数分配再分配一页空间。成功以后,把kname的地址(也就是path存储在内核空间的首地址赋给result的name域,设置name可以被释放。)并且把之前春初在user空间的地址存储在result的utpr域 (现在明白了这个原来的用户是什么意思了吧)。获取到aname域,并且填充。最后返回result。这个getname函数我们就分析完成了。下面来看看filename_lookup函数:

        err = path_init(dfd, name, flags | LOOKUP_PARENT, nd, &base);
    
        if (unlikely(err))
            return err;
    
        current->total_link_count = 0;              //设置当前进程描述符符号链接数
    
        //路径名查找操作的核心
    
        err = link_path_walk(name, nd);
    
        ...

      这个函数功能就比较复杂了,我们先来大概讲讲这些函数的功能,并且展开一部分函数仔细分析。首先是path_init函数,顾名思义就是初始化path函数,我们前面说了整个路径查找的过程其实就是填充nameidata的过程,所以这个函数肯定就是初始化nameidata,然后是就函数link_path_walk,这个函数是整个路径名查找过程的核心功能实现函数,经过这个函数,我们就找到了路径中的最后一个目录的dentry和inode信息了。然后后面的我还没有研究清楚是做什么的函数,所以这里暂不分析。我们接下来看看path_init函数,

    static int path_init(int dfd, const char *name, unsigned int flags,
                 struct nameidata *nd, struct file **fp)
    {
        int retval = 0;
    
        nd->last_type = LAST_ROOT; /* if there are only slashes... */
        nd->flags = flags | LOOKUP_JUMPED;
        nd->depth = 0;    
    
        if (flags & LOOKUP_ROOT) {
            }

      首先将 last_type 设置成 LAST_ROOT,意思就是在路径名中只有“/”。为方便叙述,我们把一个路径名分成三部分:起点(根目录或工作目录)、子路径(以“/”分隔的一系列子字符串)和最终目标(最后一个子路径),Kernel 会一个子路径一个子路径的遍历整个路径。所以 last_type 表示的是当前子路径(不是 dentry 或 inode)的类型。LOOKUP_ROOT标志 可以提供一个路径作为根路径,主要用于两个系统调用 open_by_handle_at 和 sysctl,这里不做分析了。

      nd->root.mnt = NULL;
    
        nd->m_seq = read_seqbegin(&mount_lock);
        if (*name=='/') {                   
            if (flags & LOOKUP_RCU) {
                rcu_read_lock();
                nd->seq = set_root_rcu(nd
    else {
                set_root(nd);
                path_get(&nd->root);
            }
            nd->path = nd->root;                    
        } else if (dfd == AT_FDCWD) {                   //表示path应该是当前进程的cwd           
                if (flags & LOOKUP_RCU) {
                struct fs_struct *fs = current->fs;             //获取进程的fs结构体,             
                unsigned seq;
    
                rcu_read_lock();
    
                do {
                    seq = read_seqcount_begin(&fs->seq);
                    nd->path = fs->pwd;
                    nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
                } while (read_seqcount_retry(&fs->seq, seq));
            } else {
                get_fs_pwd(current->fs, &nd->path);
            }
    } 

      设置vfsmount域是空,初始化相关文件系统的的顺序锁序号。然后根据给定的path内容,设置其实位置。如果给定的是绝对路径(第一个字符是’/’),就把path指向进程的根目录,如果dfd==AF_FDCWD,表示path是调用进程的当前工作路径,获取current(这个在前面分析do_fork的博文里面解释了这个宏表示当前进程)的当前工作路径,把path执行这个路径。

    else {                       //这个else选择表示 path不是一个绝对路径,而且dfd不是一个特殊的值
            /* Caller must check execute permissions on the starting path component */
            struct fd f = fdget_raw(dfd);               
            struct dentry *dentry;
    
            if (!f.file)
                return -EBADF;
    
            dentry = f.file->f_path.dentry;            
    
            if (*name) {
                if (!d_can_lookup(dentry)) {
                    fdput(f);
                    return -ENOTDIR;
                }
    
            nd->path = f.file->f_path;                  
            if (flags & LOOKUP_RCU) {
                if (f.flags & FDPUT_FPUT)
                    *fp = f.file;
                nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
                rcu_read_lock();
            } else {
                path_get(&nd->path);
                fdput(f);
            }
        }
    
        nd->inode = nd->path.dentry->d_inode;              
        return 0;

      如果传入的path不是一个绝对路径,而且dfd不是一个特殊的值。根据这个dfd得到对应的fd结构体,然后获取这个dfd所指向文件的dentry结构体,把path指向这个目录。
      所以大概来说,path_init函数就是根据传入的不同参数初始化nd的path域。
    分析完了path_init函数,我们接下来分析path walk的核心函数——link_path_walk函数。

    static int link_path_walk(const char *name, struct nameidata *nd)
    {
        struct path next;
        int err;
    
        while (*name=='/')             
            name++;                     
        if (!*name)                    
            return 0;
    
        /* At this point we know we have a real path component. */
    
        for(;;) {
    
            struct qstr this;
            long len;
            int type;
    
            err = may_lookup(nd);
            if (err)
                break;
    
            len = hash_name(name, &this.hash);
    
            //填充quick string结构体
            this.name = name;
            this.len = len;
    
            type = LAST_NORM;               
    
            if (name[0] == '.') switch (len) {
                case 2:
                    if (name[1] == '.') {
                        type = LAST_DOTDOT;
    

      函数头解释:函数返回一个int值可以判断查找操作是否出错,参数name就是从用户空间复制来的path,nd表示查找过程中用来存储临时数据的nameidata结构体。
      首先跳过多个连续的/(内核是有这样的容错的,你可以试试输入一个 ls ///和ls /输出是不是一样的),这样做是为了是的name指针指向一个真正的path中的第一个文件名 判断name是不是空的,空返回。因为如果是/开始的,在path_init函数中nd填充的就是根文件的信息,如果此时name为空表示path就是根目录,那么就不需要pathwalk已经找到了对应的dentry和其他的信息。现在开始一个很大的死循环,可以肯定我们所有的查找操作都是在这个循环里。在查找之前要先检测权限。如果权限检测出错,跳出循环。计算name(系统调用传入的path),填充quick string结构体。初始化name的第一个文件的文件类型,(分为普通文件,.文件和..文件)。

                    if (name[1] == '.') {
                        type = LAST_DOTDOT;
                        nd->flags |= LOOKUP_JUMPED;
                    }
                    break;
                case 1:
                    type = LAST_DOT;
            }
            if (likely(type == LAST_NORM)) {            //说明path的第一个component并不是.或者..开始的,那就接着查询>下一个
                struct dentry *parent = nd->path.dentry;
                nd->flags &= ~LOOKUP_JUMPED;                //清除查找jumped标志
                if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
                    err = parent->d_op->d_hash(parent, &this);
                    if (err < 0)
                        break;
                }
            }
    
            nd->last = this;            //设置nameidata的路径名最后分量是当前路径,
            nd->last_type = type;           //设置type
            do {
                len++;     
            } while (unlikely(name[len] == '/'));
            if (!name[len])
                return 0;
    
            //使name再次指向下一个path component
            name += len; 
    

    如果第一个分量是普通文件,得到path中的第一个分量的dentry(这个在path_init这个函数在填充nd的path时候填充好了),nd中的last设置为当前的文件,。name[len]访问待查找路径中的下一个分量,判断如果下一个分量的第一个字符是空的,表示当前的文件是带查找路径的最后一个分量。跳过两个分量中间的斜线(这里和开始的时候相似,有可能用户输入了多个斜线),跳过以后再进行一次前面的判断。如果不空,name指针就指向下一个分量的开始位置(注意这里len并不是第一个分量的长度了,应该是前一个分量加上一个或者多个斜线),为下一次循环做准备。这里要注意修改name指针的时候,必须要确定后指针移动后指向的空间是可以访问的,在最高特权级下访问了不该访问的内存是一个不可挽回的错误

    捋一下,刚才的过程大概就是“走过”路径的第一个分量,如果路径以.或者..开头,设置nd的type成员是对应的类型。如果path是以/开始的,跳过前面的不止一个/,访问完第一个分量(访问是指根据该分量的信息填充nd的last和last_type成员),name指针指向下一个分量。

    现在我们已经知道了name此时指向的空间存储的是一个中间分量,调用walk_component“走过这个节点”,walk_component定义如下:

    static inline int walk_component(struct nameidata *nd, struct path *path,
            int follow)
    {
        struct inode *inode;
        int err;
    
        if (unlikely(nd->last_type != LAST_NORM))
            return handle_dots(nd, nd->last_type);
    
        //填充path结构体,在rcuwalk模式下,
        err = lookup_fast(nd, path, &inode);
        if (unlikely(err)) {
            if (err < 0)
                goto out_err;
    
            err = lookup_slow(nd, path);
            if (err < 0)
                goto out_err;
                inode = path->dentry->d_inode;
        }
    

    在遍历的时候我们看到了,子目录被分成三种情况,.目录或者..目录、普通目录或者一个符号链接。在walk_component函数开始的时候就判断了是不是.目录或者..目录,如果是执行handle_dots,这个函数不展开分析了,大概执行的功能是:如果是.目录就直接返回0,如果是..目录表示要走进父目录,返回的时候,nd结构体已经“站在了”父目录上(nd结构体中填充的是父目录的信息)。
    如果当前的目录是一个普通目录,我们前面说了路径行走有两个策略:先在效率高的rcu-walk模式下“行走”,如果失败了就在效率较低的ref-walk模式下“行走。lookshihouup_fast函数应该就是指的两个查找策略,先调用lookup_fast,当返回值大于0的时候,才会调用lookup_slow函数当我们先来分析lookup_fast(rcu-walk模式),这里有必要澄清一下,ref-walk模式并不是一定可以找到的,有可能也会失败。

    static int lookup_fast(struct nameidata *nd,
                   struct path *path, struct inode **inode)
    {
    
        if (nd->flags & LOOKUP_RCU) {
            unsigned seq;
            //找到nameidata last的目录项,__d_lookup_rcu函数查找dentry必须要求拥有rcu锁
            dentry = __d_lookup_rcu(parent, &nd->last, &seq);
    
            //如果找到的dentry是空的,则进行切换到ref_mode模式进行再次查找
            if (!dentry)
                goto unlazy;
    
           *inode = dentry->d_inode;
            if (read_seqcount_retry(&dentry->d_seq, seq))
                return -ECHILD;
    
                    if (__read_seqcount_retry(&parent->d_seq, nd->seq))
                return -ECHILD;
            nd->seq = seq;
    
            if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) {
                status = d_revalidate(dentry, nd->flags);
                if (unlikely(status <= 0)) {
                    if (status != -ECHILD)
                        need_reval = 0;
                    goto unlazy;
                }
            }
            path->mnt = mnt;
            path->dentry = dentry;
            if (unlikely(!__follow_mount_rcu(nd, path, inode)))
                goto unlazy;
    
            reurn 0

    调用__d_lookup_rcu函数在哈希桶中找到对应的dentry(这是哪个dentry还记得吗?这个是name指向的那个目录分量的dentry),如果找到了就返回dentry,如果没找到就跳转到unlazy标记处(切换到ref-walk模式继续查找)。根据这个dentry得到对应的inode,进行一系列的检查操作,这样是为了确保在读取的时候,并没有其他进程对这些结构进行修改操作(rcu-walk模式并没有加锁),更新的临时变量path,这时候不能直接修改nd变量,因为不能确定这个分量是不是目录,nd记录的信息必须是目录,然后结束。
    其中有很多个跳转到unlazy标志的语句,我们前面说了,跳到unlazy标志表示rcu模式查找失败,用ref模式进行查找。ref模式的fast查找还是在内核缓冲区查找相应的dentry,和上述过程类似,这就不深入讲了。下来大概看看lookup_slow函数:

    static int lookup_slow(struct nameidata *nd, struct path *path)
    {
        ...
        mutex_lock(&parent->d_inode->i_mutex);
        dentry = __lookup_hash(&nd->last, parent, nd->flags);
        mutex_unlock(&parent->d_inode->i_mutex);
        ...
    }

    看到这里,大家应该就明白了,为什么这种查找方法很慢呢,因为这种查找是互斥操作,进程可能会阻塞。而且lookup_hash函数还是再会回到dcache中在找一遍,如果没有找到的话就调用文件系统自己的lookup函数从头开始找,所以这种方式比lookup_fast方式比起来慢多了。
    看完了这两个lookup函数,我们还是回到walk_component函数,剩下的也不多了。

          if (should_follow_link(path->dentry, follow)) {
            if (nd->flags & LOOKUP_RCU) {
                if (unlikely(nd->path.mnt != path->mnt ||
                         unlazy_walk(nd, path->dentry))) {
                    err = -ECHILD;
                    goto out_err;
                }
            }
            BUG_ON(inode != path->dentry->d_inode);
            return 1;
        }
        path_to_nameidata(path, nd);
        nd->inode = inode;
        return 0;
    

    询问是否需要跟踪符号链接,还记得我们之前说的分类吗?在路径行走的时候,子分量被分为三种:.开头的目录、普通目录和符号链接。如果这个自分量是符号链接,根据这个链接找到指向的目标文件,用目标文件替代该链接文件。如果这个分量不是符号链接,这时候我们可以确定这个分量就是一个普通的目录,根据path的值,填充nameidata,也就是nd变量。此时nd已经“站在”了子分量上,通过这样的递归调用,最终nd回“站在”最后一个目录上。
    到此路径名查找就结束了。

    展开全文
  • Linux基础-文件管理和重定向

    千次阅读 2019-07-05 10:23:33
    Linux基础-文件管理和重定向 2018/7/27 15:00:07 1. 每一个分区都是一个独立存在的文件系统 2. 目录:路径映射符  3. 文件 文件有两种数据 元数据:描述数据的数据属性 metadata 数据:data FHS:文件系统...

    Linux基础-文件管理和重定向

    2018/7/27 15:00:07

    1. 每一个分区都是一个独立存在的文件系统

    2. 目录:路径映射符 

    3. 文件

    文件有两种数据

    • 元数据:描述数据的数据属性 metadata
    • 数据:data

    FHS:文件系统层级结构标准

    4. Linux下的文件类型

    • -:普通文件
    • d:目录文件
    • b:块设备
    • c:字符设备
    • l:符号链接文件
    • p:管道文件pip,FIFO(first in,first out)
    • s:套接字文件:服务加IP端口,作为一个服务的入口。socket

    5. 显示当前路径

    • 每个shell和系统进程都有一个当前的工作目录
    • CWD:current work directory
    • 显示当前shell CWD的绝对路径
    • pwd: printing working directory
      • -P 显示真实物理路径
      • -L 显示链接路径(默认)

    6. 绝对路径和相对路径

    绝对路径:

    • 以正斜杠开始
    • 完整的文件的位置路径
    • 可用于任何想指定一个文件名的时候

    相对路径名:

    • 不以斜线开始
    • 指定相对于当前工作目录或某目录的位置
    • 可以作为一个简短的形式指定一个文件名

    注:

    • 基名:basename
    • 目录名:dirname

    7. cd命令

    cd 改变目录

    • 使用绝对或相对路径:
      • cd /home/wang/
      • cd home/wang
    • 切换至父目录 :cd ..
    • 切换至当前用户主目录: cd
    • 切换至以前的工作目录: cd -
    • 选项:-P
    • 相关的环境变量:
      • 显示环境变量:printenv
        • PWD:当前目录路径
        • OLDPWD:上一次目录路径
    • ~ :当前用户家目录
    • ~mage :用户mage家目录
    • ~+ :当前工作目录
    • ~- :前一个工作目录

    8. 显示目录

    • 列出当前目录的内容或指定目录(显示的数据都是元数据)
    • 用法:ls [options] [files_or_dirs]
    • 示例:
      • ls -a :包含隐藏文件
      • ls -l :显示额外的信息
      • ls -R :目录递归显示
      • ls -ld : 目录和符号链接信息
      • ls -1 :文件分行显示
      • ls –S :按从大到小排序
      • ls –t : 按mtime排序
      • ls –u :配合-t选项,显示并按atime从新到旧排序
      • ls –U : 按目录存放顺序显示
      • ls –X : 按文件后缀排序

    9. 查看文件状态:显示当前文件的时间戳

    命令:stat

    • 文件:metadata, data
    • 三个时间戳:
      • access time:访问时间,atime,读取文件内容
      • modify time: 修改时间, mtime,改变文件内容(数据)
      • change time: 改变时间, ctime,元数据发生改变

    注意:
    元数据发生变化,数据不一定变化
    数据发生变化,元数据一定变化

    10. 文件通配符(对文件名做通配)(glob、globing、wildcard)

    • * :匹配零个或多个字符
    • ? :匹配任何单个字符
    • [0-9] : 匹配数字范围
    • [a-z] :字母
    • [A-Z] :字母
    • [wang] : 匹配列表中的任何的一个字符
    • [^wang] : 匹配列表中的所有字符以外的字符
    • 预定义的字符类:(帮助:man 7 glob)
      • [:digit:]:任意数字,相当于0-9
      • [:lower:]:任意小写字母
      • [:upper:]:任意大写字母
      • [:alpha:]:任意大小写字母
      • [:alnum:]:任意数字或字母
      • [:blank:]:水平空白字符
      • [:space:]:水平或垂直空白字符
      • [:punct:]:标点符号
      • [:print:]:可打印字符
      • [:cntrl:]:控制(非打印)字符
      • [:graph:]:图形字符
      • [:xdigit:]:十六进制字符

    11. touch命令:

    touch [OPTION]... FILE...

    • -a 仅改变 atime和ctime
    • -m 仅改变 mtime和ctime
    • -t [[CC]YY]MMDDhhmm[.ss]
      指定atime和mtime的时间戳
    • -c 如果文件不存在,则不予创建

    例如:touch -m -t 201806300000.00 /etc/passwd

        [root@localhost ~]# stat /etc/passwd
        File: `/etc/passwd'
        Size: 1677          Blocks: 8          IO Block: 4096           regular file
        Device: 803h/2051d  Inode: 787752      Links: 1
        Access: (0644/-rw-r--r--)  Uid: (   0/   root)  Gid: (0/  root)
        Access: 2018-07-26 18:57:44.311586312 +0800
        Modify: 2018-07-25 18:48:32.693647980 +0800
        Change: 2018-07-25 18:48:32.693647980 +0800
        [root@localhost ~]# touch -m -t 201806300000.00 /etc/passwd
        [root@localhost ~]# stat /etc/passwd
        File: `/etc/passwd'
        Size: 1677          Blocks: 8          IO Block: 4096   regular file
        Device: 803h/2051d  Inode: 787752      Links: 1
        Access: (0644/-rw-r--r--)  Uid: (  0/  root) Gid: ( 0/    root)
        Access: 2018-07-26 18:57:44.311586312 +0800
        Modify: 2018-06-30 00:00:00.000000000 +0800
        Change: 2018-07-27 14:13:22.439425983 +0800
    

    12. 复制:所谓的复制就是创建一个新的空文件,然后在将源文件的数 据流读出来,然后写进去。

    cp默认不复制目录下的内容,选项:-r:递归复制

    1.cp [OPTION]... [-T] SOURCE DEST
    2.cp [OPTION]... SOURCE... DIRECTORY
    3.cp [OPTION]... -t DIRECTORY SOURCE...(2.3等同)
    4.cp SRC DEST
    
    • SRC是文件:

      • 如果目标不存在:新建DEST,并将SRC中内容填充至DEST中
      • 如果目标存在:
        • 如果DEST是文件:将SRC中的内容覆盖至DEST中。
          基于安全,建议为cp命令使用-i选项。
        • 如果DEST是目录:在DEST下新建与原文件同名的文件,并 将SRC中内容填充至新文件中。
    • cp SRC... DEST

      • SRC...:多个文件
        • DEST必须存在,且为目录,其它情形均会出错;
    • cp SRC DEST

      • SRC是目录:此时使用选项:-r
        • 如果DEST不存在:则创建指定目录,复制SRC目录中所有文件 至DEST中;
        • 如果DEST存在:
          • 如果DEST是文件:报错
          • 如果DEST是目录
    • cp的常用选项

      -i:覆盖前提示     –n:不覆盖,注意两者顺序
      -r, -R: 递归复制目录及内部的所有内容
      -a: 归档,相当于-dR --preserv=all
      -d:--no-dereference --preserv=links 不复制件,只  复制 链接名
      --preserv[=ATTR_LIST]  (元数据信息)
          mode: 权限
          ownership: 属主、属组
          timestamp: (时间戳(atime、mtime不变)(cti会变))
          links:链接属性
          xattr:拓展属性
          context:保持selinux的安全属性 
          all:以上所有
      -p: 等同--preserv=mode,ownership,timestamp
      -v: --verbose 显示提示信息
      -f: --force 强制
      -u:--update 只复制源比目标更新文件或目标不存在的文件
      --backup=numbered 目标存在,覆盖前先备份加数字后缀
      

    cp命令中:具体的流程是:

    分配一个空闲的inode号,在inode表中生成新条目,在目录中创建一个目录项,将名称inode编号关联
    拷贝数据生成新的文件。

    13. 移动和重命名文件

    mv [OPTION]... [-T] SOURCE DEST
    mv [OPTION]... SOURCE... DIRECTORY
    mv [OPTION]... -t DIRECTORY SOURCE...
    
    常用选项: 
    -i: 交互式
    -f: 强制
    

    mv操作时,具体的流程是:

    如果mv命令的目标和源在相同的文件系统,作为mv 命令
    用新的文件名创建对应新的目录项,删除旧目录条目对应的旧的文件名,不影响inode(除时间戳)或磁盘上的数据位置:没有数据被移动!

    如果目标和源在一个不同的文件系统, mv相当于cp和rm。

    14. 删除

    rm [OPTION]... FILE...

    常用选项:

    • -i: 交互式
    • -f: 强制删除
    • -r: 递归
    • --no-preserve-root : 强制删除/目录,忽略root
      示例:
      rm -rf /

    rm操作时候,具体的逻辑是:

    • 链接数递减,从而释放的inode号可以被重用
    • 把数据块放在空闲列表中
    • 删除目录项
    • 数据实际上不会马上被删除,但当另一个文件使用数据块时将被覆盖。

    15. 目录操作

    tree 显示目录树

    • -d: 只显示类型为目录
    • -L level:指定显示的层级数目
    • -P :pattern 只显示由指定pattern匹配到的路径
    [root@localhost ~]# tree -P *Base* /etc/yum.repos.d/
    /etc/yum.repos.d/
    └── CentOS-Base.repo
    

    mkdir 创建目录

    • -p: 存在于不报错,且可自动创建所需的各目录
    • -v: 显示详细信息
    • -m MODE: 创建目录时直接指定权限
    [root@localhost ~]# mkdir -p -v -m 777 /tmp/c
    mkdir: created directory `/tmp/c'
    [root@localhost ~]#
    

    rmdir 删除空目录

    • -p: 递归删除父空目录
    • -v: 显示详细信息
    • rm -r 递归删除目录树
    [root@localhost ~]# mkdir -p -v /tmp/x/y/z
    mkdir: created directory `/tmp/x'
    mkdir: created directory `/tmp/x/y'
    mkdir: created directory `/tmp/x/y/z'
    [root@localhost ~]# rmdir -p -v /tmp/x/y/z
    rmdir: removing directory, `/tmp/x/y/z'
    rmdir: removing directory, `/tmp/x/y'
    rmdir: removing directory, `/tmp/x'
    rmdir: removing directory, `/tmp'
    rmdir: failed to remove directory `/tmp': Directory not     empty
    

    16. 索引节点

    inode(index node)表中包含文件系统所有文件列表
    一个节点 (索引节点)是在一个表项(inode table),包含有关文件的信息( 元数据 包括:

    • 文件类型,权限,UID,GID
    • 链接数(指向这个文件名路径名称个数)
    • 该文件的大小和不同的时间戳
    • 指向磁盘上文件的数据块指针
    • 有关文件的其他数据

    11830691-b45e402408acf428.jpg

    inode结构表

    11830691-056ec985b755059a.png

    inode存值形式

    11830691-1a24908fb5a0b703.png

    根据inode找文件步骤

    文件引用一个是 inode号

    人是通过文件名来引用一个文件

    一个目录是目录下的文件名和文件inode号之间的映射

    17. 硬链接和软链接

    详细说明[https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/inde.html#listing1(https://www.ibm.com/developerworks/cn/linux/l-cn-hardandsymb-links/inde.html#listing1 "转载自IBM博客")

    1. 硬链接

    • 创建硬链接会增加额外的记录项以引用文件
    • 对应于同一文件系统上一个物理文件
    • 每个目录引用相同的inode号
    • 创建时链接数递增
    • 删除文件时:
      • rm命令递减计数的链接
      • 文件要存在,至少有一个链接数
      • 当链接数为零时,该文件被删除
    • 不能跨越驱动器或分区
    • 语法: ln filename [linkname ]

    注意: 目录不能使用硬链接

    2. 软链接

    • 一个符号链接指向另一个文件
    • ls - l的 显示链接的名称和引用的文件
    • 一个符号链接的内容是它引用文件的名称
    • 可以对目录进行
    • 可以跨分区
    • 指向的是另一个文件的路径;其大小为指向的路径字符串的长度;不增 - 加或减目标文件inode的引用计数;
    • 语法:ln -s filename ​​[linkname]

    注意:加粗部分为硬链接和软链接的核心区别。

    18. 确定文件内容

    文件可以包含多种类型的数据
    检查文件的类型,然后确定适当的打开命令或应用程序使用

    语法:file [options] <filename>...

    常用选项:

    • -b 列出文件辨识结果时,不显示文件名称
    • -f filelist 列出文件filelist中文件名的文件类型
    • -F 使用指定分隔符号替换输出文件名后默认的”:”分隔符
    • -L 查看对应软链接对应文件的文件类型
    • --help 显示命令在线帮助

    19. 标准输入与输出

    • 程序:指令+数据

      • 读入数据:Input
      • 输出数据:Output
    • 打开的文件都有一个fd: file descriptor (文件描述符)

    • Linux给程序提供三种I/O设备

      • 标准输入(STDIN)-0 默认接受来自键盘的输入
      • 标准输出(STDOUT)-1 默认输出到终端窗口
      • 标准错误(STDERR)-2 默认输出到终端窗口
    • I/O重定向:改变默认位置

    • 输入重定向:

      [root@localhost ~]# cat > /tmp/luoq.txt <<EOF
      > woshishui
      > EOF
      [root@localhost ~]# cat /tmp/luoq.txt
      woshishui
      

    20. 把输出和错误重新定向到文件

    • STDOUT和STDERR可以被重定向到文件
    • 命令 操作符号 文件名
    • 支持的操作符号包括:
      • >  把STDOUT重定向到文件
      • 2> 把STDERR重定向到文件
      • &> 把所有输出重定向到文件
    [root@localhost ~]# touch /tmp/error.txt;cat /tmp/luo.txt1 2>/tmp/error.txt
    [root@localhost ~]# cat /tmp/error.txt
    cat: /tmp/luo.txt1: No such file or directory
    [root@localhost ~]# 
    [root@localhost ~]# cat /tmp/luoq.txt &> /tmp/error.txt
    [root@localhost ~]# cat /tmp/error.txt
    woshishui
    [root@localhost ~]# cat /tmp/luoq.txt11 &>  /tmp/error.txt
    [root@localhost ~]# cat /tmp/error.txt
    cat: /tmp/luoq.txt11: No such file or directory
    
    • > 文件内容会被覆盖
      • set –C 禁止将内容覆盖已有文件,但可追加
      • >| file 强制覆盖
      • set +C 允许覆盖
    • >> 原有内容基础上,追加内容
    • 2> 覆盖重定向错误输出数据流
    • 2>> 追加重定向错误输出数据流
    • 标准输出和错误输出各自定向至不同位置 COMMAND > /path/to/file.out 2>/path/to/error.out
    • 合并标准输出和错误输出为同一个数据流进行重定向
      • &> 覆盖重定向
      • &>> 追加重定向
      • COMMAND > /path/to/file.out 2>&1 (顺序很重要)
      • COMMAND >> /path/to/file.out 2>&1
      • ():合并多个程序的STDOUT
      • ( cal 2007 ; cal 2008 ) > all.txt

    21. tr命令

    tr 转换和删除字符

    • 语法:tr [OPTION]... SET1 [SET2]
    • 选项:
      • -c –C --complement:取字符集的补集
      • -d --delete:删除所有属于第一字符集的字符
      • -s --squeeze-repeats:把连续重复的字符以单独一个字符表示
      • -t --truncate-set1:将第一个字符集对应字符转化为第二字符集对应的字符
      • [:alnum:]:字母和数字 [:alpha:]:字母 [:cntrl:]:控制(非打印)字符 [:digit:]:数字 [:graph:]:图形字符 [:lower:]:小写字母 [:print:]:可打印字符 [:punct:]:标点符号 [:space:]:空白字符 [:upper:]:大写字母 [:xdigit:]:十六进制字符

    例如:tr ‘a-z’ ‘A-Z’< /etc/issue

    22. 管道

    • 管道(使用符号“|”表示)用来连接命令

    • 命令1 | 命令2 | 命令3 | …

      • 将命令1的STDOUT发送给命令2的STDIN,命令2的STDOUT送到命令3的STDIN
      • STDERR默认不能通过管道转发,可利用2>&1 或 |& 实现
      • 最后一个命令会在当前shell进程的子shell进程中执行来
      • 组合多种工具的功能
      • ls | tr 'a-z' 'A-Z'
    • 有些命令不支持管道传输过来的数据,可以加上 - 执行

    23. tee命令,一路输入两路输出

    • 命令1 | tee [-a ] 文件名 | 命令2
      • 把命令1的STDOUT保存在文件中,做为命令2的输入
      • -a 追加
    • 使用:
      • 保存不同阶段的输出
      • 复杂管道的故障排除
      • 同时查看和记录输出

    例如:[root@localhost ~]# cat /etc/passwd | tr -d 'abc' | tr 'd-z' 'D-Z' | tee /tmp/luo.txt

    24. selinux:安全Linux策略

    • 配置文件在/etc/selinux/config :
    • 查看状态的命令:getenforce
      • 三种状态:
        • enforcing:强制:违反规则不能运行
        • permissive许可:可以允许运行,但是会记录,用来后期的审计
        • disabled:禁用:关闭selinux

    25. 历史命令:history的环境变量配置:

    HISTCONTROL

    • -ignoredups(忽略重复)
    • -ignorespace(忽略空格)
    • -ignoreboth(忽略所有)
    展开全文
  • Linux 命令之 find:查找文件

    万次阅读 2019-04-01 21:36:23
    温馨提示:本教程的 GitHub 地址linux-tutorial」,欢迎感兴趣的童鞋Star、Fork,纠错。 在 Linux 命令中,find用于在指定目录下查找文件。任何位于参数之前的字符串都将被视欲查找的目录名,其支持按名称...

    温馨提示:本教程的 GitHub 地址为「linux-tutorial」,欢迎感兴趣的童鞋StarFork,纠错。

    在 Linux 命令中,find用于在指定目录下查找文件。任何位于参数之前的字符串都将被视为欲查找的目录名,其支持按名称查找、按正则表达式查找、按文件大小查找、按文件权限查找等多种查询方式。如果在使用该命令时,不设置任何参数,则find命令将在当前目录下查找子目录与文件,并且将查找到的子目录和文件全部进行显示。

    • 语法:find + 目标目录(路径) + <选项> + 参数

    常用选项列表

    选项 含义
    -perm <权限数值> 查找符合指定的权限数值的文件或目录
    -type <文件类型> 只寻找符合指定的文件类型的文件
    -name <范本样式> 指定字符串作为寻找文件或目录的范本样式
    -expty 寻找文件大小为 0 Byte 的文件,或目录下没有任何子目录或文件的空目录
    -ls 假设find指令的回传值为ture,就将文件或目录名称列出到标准输出
    -maxdepth <目录层级> 设置最大目录层级
    -mindepth <目录层级> 设置最小目录层级
    -exec <执行指令> 假设find指令的回传值为true,就执行该指令
    -ok <执行指令> 此参数的效果和指定-exec类似,但在执行指令之前会先询问用户,若回答yY,则放弃执行命令

    示例

    首先,给出演示使用的文件结构:

    0000

    • 示例 1:查找当前目录及其子目录下所有文件和文件夹
    find .
    

    01

    • 示例 2:在/testLinux目录下查找以.txt结尾的文件名
    // 需要书写完整的路径
    find /tmp/cg/testLinux -name "*.txt"
    

    02

    • 示例 3:组合查找文件名以file1开头(与、或、非)file2开头的文件
    /**
     * 组合查找语法:
     * -a        与(取交集)
     * -o        或(取并集)
     * -not      非(同 !)
     * !         非(同 not)
     */
    
    find . -name "file1*" -a -name "file2*"
    find . -name "file1*" -o -name "file2*"
    find . -name "file1*" -not -name "file2*"
    find . -name "file1*" ! -name "file2*"
    

    03

    • 示例 4:根据文件类型进行搜索
    /**
     * 查找当前目录及所有子目录下的普通文件
     */
    
    find . -type f
    

    04

    • 示例 5:基于目录深度进行搜索
    /**
     * 限制最大深度为 3
     */
    
    find . -maxdepth 3 -type f
    
    /**
     * 限制最大深度为 2
     */
    
    find . -maxdepth 2 -type f
    

    05

    • 示例 6:基于文件权限进行搜索
    /**
     * 搜索权限为 777 的文件
     */
    
    find . -type f -perm 777
    
    /**
     * 搜索 .txt 格式且权限不为 777 的文件
     */
    
    find . -type f -name "*.txt" ! -perm 777
    

    06

    • 示例 7:借助-exec命令,将当前目录及子目录下所有.txt格式的文件以File:文件名的形式打印出来
    find . -type f -name "*.txt" -exec printf "File: %s\n" {} \;
    

    07

    • 示例 8:借助-exec命令,将当前目录及子目录下所有 3 天前的.txt格式的文件复制一份到old目录
    find . -type f -mtime +3 -name "*.txt" -exec cp {} old \;
    

    08


    文件类型参数列表

    文件类型参数 含义
    f 普通文件
    l 符号连接
    d 目录
    c 字符设备
    b 块设备
    s 套接字
    p Fifo

    文件大小单元列表

    文件大小单元 含义
    b 块(512 字节)
    c 字节
    w 字(2 字节)
    k 千字节
    M 兆字节
    G 吉字节

    选项列表

    选项 含义
    -amin <分钟> 查找在指定时间曾被存取过的文件或目录,单位以分钟计算
    -atime <24小时数> 查找在指定时间曾被存取过的文件或目录,单位以 24 小时计算
    -cmin <分钟> 查找在指定时间之时被更改过的文件或目录
    -ctime <24小时数> 查找在指定时间之时被更改的文件或目录,单位以 24 小时计算
    -anewer <参考文件或目录> 查找其存取时间较指定文件或目录的存取时间更接近现在的文件或目录
    -cnewer <参考文件或目录> 查找其更改时间较指定文件或目录的更改时间更接近现在的文件或目录
    -daystart 从本日开始计算时间
    -depth 从指定目录下最深层的子目录开始查找
    -expty 寻找文件大小为 0 Byte 的文件,或目录下没有任何子目录或文件的空目录
    -exec <执行指令> 假设find指令的回传值为true,就执行该指令
    -false find指令的回传值皆设为false
    -fls <列表文件> 此参数的效果和指定-ls参数类似,但会把结果保存为指定的列表文件
    -follow 排除符号连接
    -fprint <列表文件> 此参数的效果和指定-print参数类似,但会把结果保存成指定的列表文件
    -fprint0 <列表文件> 此参数的效果和指定-print0参数类似,但会把结果保存成指定的列表文件
    -fprintf <列表文件> <输出格式> 此参数的效果和指定-printf参数类似,但会把结果保存成指定的列表文件
    -fstype <文件系统类型> 只寻找该文件系统类型下的文件或目录
    -gid <群组识别码> 查找符合指定群组识别码的文件或目录
    -group <群组名称> 查找符合指定群组名称的文件或目录
    -help——help 在线帮助
    -name <范本样式> 指定字符串作为寻找文件或目录的范本样式
    -iname <范本样式> 此参数的效果和指定-name参数类似,但忽略字符大小写的差别
    -ilname <范本样式> 此参数的效果和指定-lname参数类似,但忽略字符大小写的差别
    -inum <inode编号> 查找符合指定的inode编号的文件或目录
    -path <范本样式> 指定字符串作为寻找目录的范本样式
    -ipath <范本样式> 此参数的效果和指定-path参数类似,但忽略字符大小写的差别
    -iregex <范本样式> 此参数的效果和指定-regexe参数类似,但忽略字符大小写的差别
    -links <连接数目> 查找符合指定的硬连接数目的文件或目录
    -ls 假设find指令的回传值为ture,就将文件或目录名称列出到标准输出
    -maxdepth <目录层级> 设置最大目录层级
    -mindepth <目录层级> 设置最小目录层级
    -mmin <分钟> 查找在指定时间曾被更改过的文件或目录,单位以分钟计算
    -mount 此参数的效果和指定-xdev相同
    -mtime <24小时数> 查找在指定时间曾被更改过的文件或目录,单位以 24 小时计算
    -newer <参考文件或目录> 查找其更改时间较指定文件或目录的更改时间更接近现在的文件或目录
    -nogroup 找出不属于本地主机群组识别码的文件或目录
    -noleaf 不去考虑目录至少需拥有两个硬连接存在
    -nouser 找出不属于本地主机用户识别码的文件或目录
    -ok <执行指令> 此参数的效果和指定-exec类似,但在执行指令之前会先询问用户,若回答yY,则放弃执行命令
    -perm <权限数值> 查找符合指定的权限数值的文件或目录
    -print 假设find指令的回传值为ture,就将文件或目录名称列出到标准输出,格式为每列一个名称,每个名称前皆有./字符串
    -print0 假设find指令的回传值为ture,就将文件或目录名称列出到标准输出,格式为全部的名称皆在同一行
    -printf <输出格式> 假设find指令的回传值为ture,就将文件或目录名称列出到标准输出,格式可以自行指定
    -prune 不寻找字符串作为寻找文件或目录的范本样式
    -regex <范本样式> 指定字符串作为寻找文件或目录的范本样式
    -size <文件大小> 查找符合指定的文件大小的文件
    -true find指令的回传值皆设为true
    -type <文件类型> 只寻找符合指定的文件类型的文件
    -uid <用户识别码> 查找符合指定的用户识别码的文件或目录
    -used <日数> 查找文件或目录被更改之后在指定时间曾被存取过的文件或目录,单位以日计算
    -user <拥有者名称> 查找符和指定的拥有者名称的文件或目录
    -version——version 显示版本信息
    -xdev 将范围局限在先行的文件系统中
    -xtype <文件类型> 此参数的效果和指定-type参数类似,差别在于它针对符号连接检查
    展开全文
  • 获取jar包中的文件路径

    千次阅读 2017-09-30 11:30:46
    /**-----------------------------------------------------------------------  *getAppPath需要一个当前程序... *Java可执行文件(jar,war)所处的系统目录名或非打包Java程序所处的目录  *@param clsClas
  • Linux文件系统详解

    万次阅读 多人点赞 2019-05-29 16:07:53
    从操作系统的角度详解Linux文件系统层次、文件系统分类、文件系统的存储结构、不同存储介质的区别(RAM、ROM、Flash)、存储节点inode。本文参考: http://blog.chinaunix.net/uid-8698570-id-1763151.html ...
  • 操作系统——linux系统文件目录机构

    万次阅读 2015-12-03 21:52:27
      Linux下的文件系统树形结构,入口/ 树形结构下的文件目录: 无论哪个版本的Linux系统,都有这些目录,这些目录应该是标准的。各个Linux发行版本会存在一些小小的差异,但总体来说,还是大体差不多。1. / ...
  • mysqldump定时任务生成备份文件内容为空解决方法 2018年05月25日 09:54:13 黄如果 阅读数:2079 1问题:写好了一个mysqldump备份脚本(如图)直接执行可以正常生成备份文件,但在用crontab运行时却生成内容为空 2...
  • linux桌面入口文件(.desktop)规范

    千次阅读 2019-07-05 23:18:39
    文章目录介绍文件命令桌面文件ID文件的基本格式注释组项可能的值类型键的本地化值正式的桌面文件KeyExec KeyD-Bus激活A.示例桌面条目文件 介绍 KDE和GNOME桌面环境都采用了类似的“桌面入口”格式,或描述特定程序...
  • Linux设备文件简介

    千次阅读 2016-09-18 23:06:03
    设备文件简介 本文转载,留给自己学习之用 大家可以直接到原作者的空间查看: 《Linux设备文件简介》 http://lamp.linux.gov.cn/Linux/device_files.html -------------------------------------------...
  • 日期 内核版本 架构 作者 GitHub CSDN 2016-06-04 Linux-4.5 X86 & arm ...对象文件格式对象文件首先,你需要知道的是所谓对象文件(Object files)有三个种类: 可重定位的对象文件(Relocatable file)
  • linux文件时间戳修改

    千次阅读 2016-09-19 09:56:15
    linux的touch命令不常用,一般在使用make的时候可能会用到,用来修改文件时间戳,或者新建一个不存在的文件。 命令格式: touch [选项]… 文件… 命令参数: -a 或–time=atime或–time=access或–time=use 只...
  • python中对文件、文件夹(文件操作函数)的操作需要涉及到os模块和shutil模块。 得到当前工作目录,即当前Python脚本工作的目录路径: os.getcwd() 返回指定目录下的所有文件和目录名:os.listdir() 函数用来删除一个...
  • linux驱动开发-文件系统与设备文件

    千次阅读 2018-10-15 22:07:53
    1.Linux文件系统操作 Linux文件创建,打开,关闭函数 Linux文件读写函数 2.C库文件操作 3.Linux文件系统 3.1根目录结构 3.2.VFS VFS 虚拟文件系统基础概念 Linux文件系统与设备驱动关系: 设备驱动结构体:...
  • 嵌入式Linux文件系统和挂载

    千次阅读 2017-12-21 17:12:47
    嵌入式Linux系统由三部分组成: uboot、kernel、根文件系统, 还是这张老图 这里的根文件系统可以说是包含两个部分: 一个是根,一个是文件系统 那么什么是根呢?哈哈 其实根表示的就是第一个的意思 ...
  • linux .so文件详解

    千次阅读 2014-03-07 16:36:08
    linux文件的类型是不依赖于其后缀名的,但一般来讲: .o,是目标文件,相当于windows中的.obj文件 .so 共享库,是shared object,用于动态连接的,和dll差不多 .a静态库,是好多个.o合在一起,用于静态连接 .la...
  • linux之fstab文件详解

    万次阅读 多人点赞 2014-01-06 14:26:12
    /etc/fstab是用来存放文件系统的静态信息的文件。位于/etc/目录下,可以用命令less /etc/fstab 来查看,如果要修改的话,则用命令 vi /etc/fstab 来修改。 当系统启动的时候,系统会自动地从这个文件读取信息,...
  • 常用Linux命令-文件目录类命令

    千次阅读 2020-03-16 14:26:49
    ls: 显示目录文件 ls: 功能描述:显示目录文件 语法:ls [-a/l/d] [文件或目录] -a 显示所有文件,包括隐藏文件 -l 详细信息显示 -d 查看目录属性 -r 递归列出子目录 pwd: 显示当前所在的工作目录...
  • 嵌入式Linux文件系统制作和挂载

    万次阅读 2016-05-02 20:33:56
    嵌入式Linux系统由三部分组成: uboot、kernel、根文件系统, 还是这张老图 这里的根文件系统可以说是包含两个部分: 一个是根,一个是文件系统 那么什么是根呢?哈哈 其实根表示的就是第一个的意思 下面贴...
1 2 3 4 5 ... 20
收藏数 48,981
精华内容 19,592
关键字:

linux 取文件路径为空