用Python os模块获取Unix文件类型

7
我想获取指定路径文件的Unix文件类型(例如,找出它是正常文件、命名管道、块设备等)。我在文档中找到了 os.stat(path).st_type ,但在Python 3.6中,这似乎不起作用。另一种方法是使用 os.DirEntry 对象(例如通过 os.listdir(path)),但只有 is_dir() is_file() is_symlink() 方法。有什么办法吗?
3个回答

3
你可以使用 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的情况而言),但它是一个不错的库。

1
我绝对不知道这个。我刚刚打开了文档,看起来很好,谢谢。为了节省其他人的谷歌搜索时间:https://docs.python.org/3/library/pathlib.html - karlosss
1
它也可以作为Python 2的可安装包使用。我已经在PyPI上发布了ruamel.std.pathlib,它对Path进行了一些扩展,并提供了一个“PathLibConversionHelper”类来帮助过渡。 - Anthon

0

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中考虑未定义的stat函数并练习必要的try ... except语句。成员也被重新编号,因为在python 2中,enum.ENUM显然按值对成员进行排序,而这显然没有记录。由于现在不支持python 2,无论是否有错误,都是这样。 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'
>>> 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接