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