7

I would like to get the unix file type of a file specified by path (find out whether it is a regular file, a named pipe, a block device, ...)

I found in the docs os.stat(path).st_type but in Python 3.6, this seems not to work.

Another approach is to use os.DirEntry objects (e. g. by os.listdir(path)), but there are only methods is_dir(), is_file() and is_symlink().

Any ideas how to do it?

3 Answers 3

3

You use the stat module to interpret the result of 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

You can make a general function to return the determined type. This works for both Python 2 and 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>
Sign up to request clarification or add additional context in comments.

Comments

2

Python 3.6 has pathlib and its Path objects have methods:

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

pathlib takes a bit to get used to (at least for me having come to Python from C/C++ on Unix), but it is a nice library

2 Comments

I absolutely did not know about this one. I just opened the docs and it looks nice, thank you. Just to save a google search for the others: docs.python.org/3/library/pathlib.html
It is also available as an installable package for Python 2. I have published ruamel.std.pathlib on PyPI, which has a few extensions on Path, and a"PathLibConversionHelper" class that can assist in transitioning.
0

20 November 2020

The code supplied in the answer by 'Artyer' does not work in python 2.7.17, but appears to work in python 3.6.9 except for one detail:

The call:

path = os.stat(path).st_mode

should be:

path = os.lstat(path).st_mode

Otherwise you just get the regular file that a soft link points to.

For python 2 (and 3), if you wish to follow this style of coding, the following is better, with some name changes for clarity:

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"]

Notice the reordering that forces testing in python 2 to consider undefined stat functions and exercise the necessary try...except statement. The members are also renumbered since in python 2, enum.ENUM apparently sorts the members by value, and this is apparently undocumented. Since python 2 is now not supported, bug or not, that is the way it is.

The enum documentation recommends against using value 0 since that is boolean False and all members are supposed to be boolean True. The documentation also recommends using this simpler helper function style.
(https://cpython-test-docs.readthedocs.io/en/latest/library/enum.html)

To avoid some of these ambiguities the following will behave as documented, and also orders processing to return the most likely results quickly:

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"]

The enum.ENUM functional API apparently does not have the sorting bug and keeps the members in the order they are presented, as documented.

Given the troublesome nature of enum.ENUM, it is probably better simply to avoid it by using time tested python primitives:

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'

This is a lot more efficient than the code 'Artyer' offers, which is important when processing large numbers of files. Usage for both python2 and 3:

>>> 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'
>>> 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.