home
#!/usr/bin/env python
# Copyright 2008 by Aidin Abedi <fooguru@msn.com>
# Some parts are modifications of http://www.pixelbeat.org/talks/python/ls.py
# This script is essentially equivalent to "ls -lagFR"
# It recursively prints details of all (even hidden) files in a directory
# include necessary libraries
import os
import sys
import stat # index constants for os.stat()
import platform # OS name
import locale
import time
# global time constants, used in printInfo()
now = int(time.time())
recent = now - (6*30*24*60*60) # 6 months ago
# global color variables, used in main() and printInfo()
colorful = False # initially, no color support
colors = { # dictionary to convert color-name to terminal-code
"none": "",
"default": "\x1b[00m",
"blue": "\x1b[01;34m",
"cyan": "\x1b[01;36m",
"green": "\x1b[01;32m",
"red": "\x1b[01;31m"
}
#-----------------------------------------------------------------------------
# simplified from Python cookbook, #475186
def hasColors():
if not hasattr(sys.stdout, "isatty"):
return False
elif not sys.stdout.isatty():
return False # auto color only on TTYs
elif platform.system().lower() == "windows":
return False # windows not supported
else:
return True # guess
#-----------------------------------------------------------------------------
# find and print reason for error
def printError(filename, source):
if not os.access(filename, os.F_OK): # check if exists
print "%s: No such file or directory" % filename
elif not os.access(filename, os.R_OK): # check read access
print "%s: Premission denied" % filename
else: # bad happened
print "%s: Unknown %s() error" % (filename, source)
#-----------------------------------------------------------------------------
# print well formated file info
def printInfo(filename, filetitle = ""):
if not filetitle: # default value for filetitle
filetitle = filename
try:
# lstat() is like stat(), but do not follow symbolic links
filestat = os.lstat(filename) # get all file info
except:
printError(filename, "lstat") # print error
return # exit function
try:
import pwd # not available on all platforms
fileuser = pwd.getpwuid(filestat.st_uid)[0] # convert to user name
except (ImportError, KeyError):
fileuser = "usr#%d" %filestat.st_uid # just user id
try:
import grp # not available on all platforms
filegroup = grp.getgrgid(filestat.st_gid)[0] # convert to group name
except (ImportError, KeyError):
filegroup = "grp#%d" % filestat.st_gid # just group id
filemode = filestat.st_mode
fileperms = '-' # string representing premissions
fileisdir = False # fileisdir is returned at end of function
filecolor = "none"
# classify file
if stat.S_ISDIR(filemode): # directory
fileperms = 'd'
filecolor = "blue"
filetitle += '/' # add indication
fileisdir = True # inform function caller that file is a directory
elif stat.S_ISLNK(filemode): # symbolic link
fileperms = 'l'
filecolor = "cyan"
# get relative and absolute link filenames
targetrel = os.readlink(filename)
targetabs = os.path.join(os.path.dirname(filename), targetrel)
filetitle += " -> " + targetrel # add 'point to' target
if not os.path.exists(targetabs): # target doesn't exists
filecolor = "red" # bad link
elif os.path.isdir(targetabs): # target is a directory
filetitle += '/' # add indication
elif stat.S_ISREG(filemode): # regular file
if filemode & (stat.S_IXGRP|stat.S_IXUSR|stat.S_IXOTH): # executable
filecolor = "green"
filetitle += '*' # add indication
# loop every combination "S_I" + ["R", "W", "X"] + ["USR", "GRP", "OTH"]
for who in "USR", "GRP", "OTH":
for what in "R", "W", "X":
# lookup attributes at runtime using getattr
if filemode & getattr(stat, "S_I" + what + who):
fileperms += what.lower() # add 'r', 'w', or 'x'
else:
fileperms += '-' # doesn't have access
# get modification time stamp of file
filetime = filestat.st_mtime
global recent, now
if (filetime < recent) or (filetime > now): # time stamp is 6 months old
filetime = time.strftime("%b %d %Y", time.localtime(filetime))
else:
filetime = time.strftime("%b %d %H:%M", time.localtime(filetime))
# format file info to fixed-size columns
filenlink = "%2d" % filestat.st_nlink
filesize = "%8d" % filestat.st_size
fileuser = "%-8s" % fileuser
filegroup = "%-8s" % filegroup
# add color if available
global colorful, colors
if colorful and colors[filecolor]:
filetitle = colors[filecolor] + filetitle + colors["default"]
# print the result
print fileperms, filenlink, fileuser, filegroup, # don't add newline
print filesize, filetime, filetitle
return fileisdir # is this file a directory
#-----------------------------------------------------------------------------
# print all files and dirs in a directory and it's sub-directories
def printDir(directory):
if os.path.isfile(directory): # directory is actually a file
printInfo(directory)
return # exit function
try:
# get array of files (and dirs) in directory
files = os.listdir(directory) # Python doesn't have opendir, readdir
except:
printError(directory, "listdir") # print error
return # exit function
# do locale sensitive sort of files to list
locale.setlocale(locale.LC_ALL, '')
files.sort(locale.strcoll)
# sub-directories to traverse recursively after printing files
subdirs = []
# print directory name
print "%s:" % directory
# note listdir() does not add "." and ".." so we must do this manually
printInfo(os.path.join(directory, "."), ".") # print info of this directory
printInfo(os.path.join(directory, ".."), "..") # and it's parent
# print files and collect any sub-directories
for filetitle in files:
filename = os.path.join(directory, filetitle) # get full path name
if printInfo(filename, filetitle):
subdirs.append(filename) # file is a sub-directory
# print files in each sub-directory
for dirpath in subdirs:
print # newline
printDir(dirpath)
#-----------------------------------------------------------------------------
# process command line input
def main(argv = None):
if argv is None: # default value for argv
argv = sys.argv
# trigger color support if available
global colorful
colorful = hasColors()
if len(argv) == 1: # no arguments
printDir(".") # print current dir
else:
printDir(argv[1])
# process remaining arguments too
for arg in argv[2:]:
print # newline
printDir(arg)
#-----------------------------------------------------------------------------
if __name__ == "__main__": # the script is called directly by the interpreter
sys.exit(main()) # call main and return exit code