【问题标题】:Get unix file type with Python os module使用 Python os 模块获取 unix 文件类型
【发布时间】:2017-11-19 14:44:21
【问题描述】:

我想获取path指定的文件的unix文件类型(看看它是普通文件,命名管道,块设备,...)

我在文档 os.stat(path).st_type 中找到,但在 Python 3.6 中,这似乎不起作用。

另一种方法是使用os.DirEntry 对象(例如os.listdir(path)),但只有is_dir()is_file()is_symlink() 方法。

有什么办法吗?

【问题讨论】:

    标签: python unix operating-system


    【解决方案1】:

    您使用stat 模块来解释os.stat(path).st_mode 的结果。

    >>> import os
    >>> import stat
    >>> stat.S_ISDIR(os.stat('/dev/null').st_mode)
    False
    >>> stat.S_ISCHR(os.stat('/dev/null').st_mode)
    True
    

    您可以创建一个通用函数来返回确定的类型。这适用于 Python 2 和 3。

    import enum
    import os
    import stat
    
    class PathType(enum.Enum):
        dir = 0  # directory
        chr = 1  # character special device file
        blk = 2  # block special device file
        reg = 3  # regular file
        fifo = 4  # FIFO (named pipe)
        lnk = 5  # symbolic link
        sock = 6  # socket
        door = 7  # door  (Py 3.4+)
        port = 8  # event port  (Py 3.4+)
        wht = 9  # whiteout (Py 3.4+)
    
        unknown = 10
    
        @classmethod
        def get(cls, path):
            if not isinstance(path, int):
                path = os.stat(path).st_mode
            for path_type in cls:
                method = getattr(stat, 'S_IS' + path_type.name.upper())
                if method and method(path):
                    return path_type
            return cls.unknown
    
    PathType.__new__ = (lambda cls, path: cls.get(path))
    
    >>> PathType('/dev/null')
    <PathType.chr: 1>
    >>> PathType('/home')
    <PathType.dir: 0>
    

    【讨论】:

      【解决方案2】:

      Python 3.6 有 pathlib 并且它的 Path 对象有方法:

      • is_dir()
      • is_file()
      • is_symlink()
      • is_socket()
      • is_fifo()
      • is_block_device()
      • is_char_device()

      pathlib 需要一点时间来适应(至少对我来说是从 Unix 上的 C/C++ 来到 Python 的),但它是一个不错的库

      【讨论】:

      • 我完全不知道这个。我刚刚打开文档,看起来不错,谢谢。只是为了保存其他人的谷歌搜索:docs.python.org/3/library/pathlib.html
      • 它也可以作为 Python 2 的可安装包使用。我在 PyPI 上发布了 ruamel.std.pathlib,它在 Path 上有一些扩展,以及一个可以帮助转换的“PathLibConversionHelper”类.
      【解决方案3】:

      2020 年 11 月 20 日

      “Artyer”在答案中提供的代码在 python 2.7.17 中不起作用,但似乎在 python 3.6.9 中起作用,除了一个细节:

      电话:

      path = os.stat(path).st_mode
      

      应该是:

      path = os.lstat(path).st_mode
      

      否则你只会得到软链接指向的常规文件。

      对于python 2(和3),如果您希望遵循这种编码风格,以下更好,为了清晰起见,更改了一些名称:

      import enum,os,stat
      class PathTypes(enum.Enum):
        door = 0  # door       (Py 3.4+)
        port = 1  # event port (Py 3.4+)
        wht  = 2  # whiteout   (Py 3.4+)
        dir  = 3  # directory
        chr  = 4  # character special device file
        blk  = 5  # block special device file
        reg  = 6  # regular file
        fifo = 7  # FIFO (named pipe)
        lnk  = 8  # symbolic link
        sock = 9  # socket
        unimplemented = 10
      
      def filetype(path):
        mode=os.lstat(path).st_mode # do not follow links
        for t in PathTypes:
          try: func=getattr(stat, 'S_IS' + t.name.upper())
          except AttributeError: continue
          if func(mode): return t
        return PathTypes["unimplemented"]
      

      注意重新排序会强制python 2 中的测试考虑未定义的统计函数并执行必要的try...except 语句。成员也被重新编号,因为在python 2enum.ENUM 中显然是按值对成员进行排序,这显然是无证的。由于现在不支持python 2,不管有没有bug,就是这样。

      enum 文档建议不要使用值 0,因为它是布尔值 False,并且所有成员都应该是布尔值 True。文档还建议使用这种更简单的辅助函数样式。
      (https://cpython-test-docs.readthedocs.io/en/latest/library/enum.html)

      为避免其中一些歧义,以下内容将按照记录的方式进行,并命令处理以快速返回最可能的结果:

      import enum,os,stat
      
      members= ( \
        ('reg',  'S_ISREG' ), \
        ('dir',  'S_ISDIR' ), \
        ('lnk',  'S_ISLNK' ), \
        ('fifo', 'S_ISFIFO'), \
        ('sock', 'S_ISSOCK'), \
        ('chr',  'S_ISCHR' ), \
        ('blk',  'S_ISBLK' ), \
        ('door', 'S_ISDOOR'), \
        ('port', 'S_ISPORT'), \
        ('wht',  'S_ISWHT' ), \
        ('unimplemented', '') \
        )
      FileTypes=enum.Enum('FileTypes',members)
      
      def filetype(path):
        """Get unix filetype:
             reg,dir,lnk,fifo,sock,chr,blk
           and for py3.4+:
             door,port,wht.
          'path' is a full pathname for some file."""
        mode=os.lstat(path).st_mode # do not follow links
        for t in FileTypes:
          try: func=getattr(stat, t.value)
          except AttributeError: continue
          if func(mode): return t
        return FileTypes["unimplemented"]
      

      enum.ENUM 函数式 API 显然没有排序错误,并且按照文档中的说明保持成员的显示顺序。

      考虑到enum.ENUM 的麻烦性质,最好通过使用时间来避免它 测试python原语:

      import os,stat
      
      _stat_funcs=( \\
        'S_ISREG','S_ISDIR','S_ISLNK','S_ISFIFO','S_ISSOCK', \\
        'S_ISCHR','S_ISBLK','S_ISDOOR','S_ISPORT','S_ISWHT'  )
      #                     ^----- python 3.4+ only ----->|
      #                     ^-- SOLARIS only --->|^--BSD->|
      _ls_chr=( \\
        '-'      ,'d'      ,'l'       ,'p'       ,'s'       , \\
        'c'      ,'b'      ,'D'       ,'P'       ,'w'          )
      #                     ^----- python 3.4+ only------>|
      #                     ^-- SOLARIS only --->|^--BSD->|
      _ftypes=tuple( (c,getattr(stat,f)) \\
          for c,f in zip(_ls_chr,_stat_funcs) if f in dir(stat))
      
      def filetype(path):
        """Get unix filetype designator used in 'ls' shell command listings:
             reg('-'),dir('d'),lnk('l'),fifo('p'),sock('s'),chr('c'),blk('b')
           and for py3.4+:
             door('D'),port('P'),wht('w'). (solaris,solaris,BSD only)
           'path' is a full pathname for some file.    Returns 'u' for an
           unknown or unimplemented filetype."""
        mode=os.lstat(path).st_mode # do not follow links
        for c,func in _ftypes:
          if func(mode): return c
        return 'u'
      

      这比“Artyer”提供的代码效率高得多,这在处理大量文件时很重要。 python23 的用法:

      >>> filetype('/dev/null') 
      'c'
      >>> filetype('/dev/sda')
      'b'
      >>> filetype('/home/test')
      'd'
      >>> filetype('/home/random.txt')
      '-'
      >>> filetype('/home/test/hlnk') # hard link
      '-'
      >>> filetype('/home/test/slnk') # soft link
      'l'
      >>> filetype('/home/test/sckt')
      's'
      >>> filetype('/home/test/fifo.pipe')
      'p'
      >>> 
      

      【讨论】:

        猜你喜欢
        • 2015-09-30
        • 2012-05-16
        • 1970-01-01
        • 2018-07-01
        • 2019-07-12
        • 2017-06-13
        • 1970-01-01
        • 2020-11-15
        相关资源
        最近更新 更多