9. Directories

Introduction

#-----------------------------
entry = os.stat("/usr/bin/vi")
#-----------------------------
entry = os.stat("/usr/bin")
#-----------------------------
entry = os.stat(INFILE.name)
#-----------------------------
entry = os.stat("/usr/bin/vi")
ctime = entry.st_ino
size = entry.st_size
#-----------------------------
f = open(filename)

f.seek(0, 2)
if not f.tell():
    raise SystemExit("%s doesn't have text in it."%filename)
#-----------------------------

for filename in os.listdir("/usr/bin"):
    print "Inside /usr/bin is something called", filename
#-----------------------------

Getting and Setting Timestamps

#-----------------------------
fstat = os.stat(filename)
readtime = fstat.st_atime
writetime = fstat.st_mtime

os.utime(filename, (newreadtime, newwritetime))

#DON'T DO THIS:
readtime, writetime = os.stat(filename)[7:9]
#-----------------------------
SECONDS_PER_DAY = 60 * 60 * 24
fstat = os.stat(filename)
atime = fstat.st_atime - 7 * SECONDS_PER_DAY
mtime = fstat.st_mtime - 7 * SECONDS_PER_DAY

os.utime(filename, (atime, mtime))     
#-----------------------------
mtime = os.stat(filename).st_mtime
utime(filename, (time.time(), mtime))
#-----------------------------
#!/usr/bin/perl -w
# uvi - vi a file without changing its access times

import sys, os
if len(sys.argv) != 2:
    raise SystemExit("usage: uvi filename")
filename = argv[1]
fstat = os.stat(filename)
# WARNING: potential security risk
os.system( (os.environ.get("EDITOR") or "vi") + " " + filename)
os.utime(filename, (fstat.st_atime, fstat.st_mtime))
#-----------------------------

Deleting a File

#-----------------------------
os.remove(filename)

err_flg = 0
for filename in filenames:
    try:
        os.remove(filename)
    except OSError, err:
        err_flg = 1
if err_flg:
    raise OSError("Couldn't remove all of %s: %s" % (filenames, err))
#-----------------------------
os.remove(filename)
#-----------------------------
success = 0
for filename in filenames:
    try:
        os.remove(filename)
        success += 1
    except OSError, err:
        pass
if success != len(filenames):
    sys.stderr.write("could only delete %d of %d files" % \
                     (success, len(filenames)))

#-----------------------------

Copying or Moving a File

#-----------------------------
import shutil
shutil.copy(oldfile, newfile)
#-----------------------------
## NOTE: this doesn't do the same thing as the Perl code,
## eg, handling of partial writes.
infile = open(oldfile)
outfile = open(newfile, "w")

blksize = 16384          # preferred block size?

while True:
    buf = infile.read(blksize)
    if not buf:
        break
    outfile.write(buf)

infile.close()
outfile.close()
#-----------------------------
# WARNING: these are insecure - do not use in hostile environments
os.system("cp %s %s" % (oldfile, newfile))       # unix
os.system("copy %s %s" % (oldfile, newfile))     # dos, vms
#-----------------------------
import shutil

shutil.copy("datafile.dat", "datafile.bak")

shutil.copy("datafile.new", "datafile.dat")
os.remove("datafile.new")

#-----------------------------

Recognizing Two Names for the Same File

#-----------------------------
import os
seen = {}

def do_my_thing(filename):
    fstat = os.stat(filename)
    key = (fstat.st_ino, fstat.st_dev)
    if not seen.get(key):
        # do something with filename because we haven't
        # seen it before
        pass
    seen[key] = seen.get(key, 0 ) + 1

#-----------------------------
for filename in files:
    fstat = os.stat(filename)
    key = (fstat.st_ino, fstat.st_dev)
    seen.setdefault(key, []).append(filename)

keys = seen.keys()
keys.sort()
for inodev in keys:
    ino, dev = inodev
    filenames = seen[inodev]
    if len(filenames) > 1:
        # 'filenames' is a list of filenames for the same file
        pass
#-----------------------------

Processing All Files in a Directory

#-----------------------------
for filename in os.listdir(dirname):
    # do something with "$dirname/$file"
    pass
#-----------------------------
# XXX No -T equivalent in Python
#-----------------------------
# 'readir' always skipes '.' and '..' on OSes where those are
# standard directory names
for filename in os.listdir(dirname):
    pass
#-----------------------------
# XX Not Implemented -- need to know what DirHandle does
# use DirHandle;

#-----------------------------

Globbing, or Getting a List of Filenames Matching a Pattern

#-----------------------------
import glob
filenames = glob.glob("*.c")
#-----------------------------
filenames = [filename for filename in os.listdir(path) if filename.endswith(".c")] 
#-----------------------------
import re
allowed_name = re.compile(r"\.[ch]$", re.I).search
filenames = [f for f in os.listdir(path) if allowed_name(f)]
#-----------------------------
import re, os
allowed_name = re.compile(r"\.[ch]$", re.I).search

fnames = [os.path.join(dirname, fname) 
              for fname in os.listdir(dirname)
              if allowed_name(fname)]
#-----------------------------
dirs = [os.path.join(path, f)
            for f in os.listdir(path) if f.isdigit()]
dirs = [d for d in dirs if os.path.isdir(d)]
dirs = sorted(dirs, key=int)    # Sort by numeric value - "9" before "11"
#-----------------------------

Processing All Files in a Directory Recursively

# Processing All Files in a Directory Recursively

# os.walk is new in 2.3.

# For pre-2.3 code, there is os.path.walk, which is
# little harder to use.

#-----------------------------
import os
for root, dirs, files in os.walk(top):
    pass # do whatever

#-----------------------------
import os, os.path
for root, dirs, files in os.walk(top):
    for name in dirs:
        print os.path.join(root, name) + '/'
    for name in files:
        print os.path.join(root, name)

#-----------------------------
import os, os.path
numbytes = 0
for root, dirs, files in os.walk(top):
    for name in files:
        path = os.path.join(root, name)
        numbytes += os.path.getsize(path)
print "%s contains %s bytes" % (top, numbytes)

#-----------------------------
import os, os.path
saved_size, saved_name = -1, ''
for root, dirs, files in os.walk(top):
    for name in files:
        path = os.path.join(root, name)
        size = os.path.getsize(path)
        if size > saved_size:
            saved_size = size
            saved_name = path
print "Biggest file %s in %s is %s bytes long" % (
    saved_name, top, saved_size)

#-----------------------------
import os, os.path, time
saved_age, saved_name = None, ''
for root, dirs, files in os.walk(top):
    for name in files:
        path = os.path.join(root, name)
        age = os.path.getmtime(path)
        if saved_age is None or age > saved_age:
            saved_age = age
            saved_name = path
print "%s %s" % (saved_name, time.ctime(saved_age))

#-----------------------------
#!/usr/bin/env python
# fdirs - find all directories
import sys, os, os.path
argv = sys.argv[1:] or ['.']
for top in argv:
    for root, dirs, files in os.walk(top):
        for name in dirs:
            path = os.path.join(root, name)
            print path

Removing a Directory and Its Contents

#-----------------------------
# DeleteDir - remove whole directory trees like rm -r
import shutil
shutil.rmtree(path)

# DON'T DO THIS:
import os, sys
def DeleteDir(dir):
    for name in os.listdir(dir):
        file = os.path.join(dir, name)
        if not os.path.islink(file) and os.path.isdir(file):
            DeleteDir(file)
        else:
            os.remove(file)
    os.rmdir(dir)

Renaming Files

# Renaming Files

# code sample one to one from my perlcookbook
# looks strange to me.
import os
for fname in fnames:
    newname = fname
    # change the file's name
    try:
        os.rename(fname, newname)
    except OSError, err:
        print "Couldn't rename %s to %s: %s!" % \
                (fname, newfile, err)

# use os.renames if newname needs directory creation.

#A vaguely Pythonic solution is:
import glob
def rename(files, transfunc)
    for fname in fnames:
        newname = transfunc(fname)
        try:
            os.rename(fname, newname)
        except OSError, err:
            print "Couldn't rename %s to %s: %s!" % \
                  (fname, newfile, err)

def transfunc(fname): 
    return fname[:-5]
rename(glob.glob("*.orig"), transfunc) 

def transfunc(fname): 
    return fname.lower()
rename([f for f in glob.glob("*") if not f.startswith("Make)], transfunc) 

def transfunc(fname): 
    return fname + ".bad"
rename(glob.glob("*.f"), transfunc) 

def transfunc(fname): 
    answer = raw_input(fname + ": ")
    if answer.upper().startswith("Y"):
        return fname.replace("foo", "bar")
rename(glob.glob("*"), transfunc) 

def transfunc(fname):
    return ".#" + fname[:-1]
rename(glob.glob("/tmp/*~"), transfunc) 

# This _could_ be made to eval code taken directly from the command line, 
# but it would be fragile
#-----------------------------

Splitting a Filename into Its Component Parts

#-----------------------------
import os

base = os.path.basename(path)
dirname = os.path.dirname(path)
dirname, filename = os.path.split(path)
base, ext = os.path.splitext(filename)

#-----------------------------
path = '/usr/lib/libc.a'
filename = os.path.basename(path)
dirname = os.path.dirname(path)

print "dir is %s, file is %s" % (dirname, filename)
# dir is /usr/lib, file is libc.a
#-----------------------------
path = '/usr/lib/libc.a'
dirname, filename = os.path.split(path)
name, ext = os.path.splitext(filename)

print "dir is %s, name is %s, extension is %s" % (dirname, name, ext)
#   NOTE: The Python code prints
# dir is /usr/lib, name is libc, extension is .a
#   while the Perl code prints a '/' after the directory name
# dir is /usr/lib/, name is libc, extension is .a
#-----------------------------
import macpath
path = "Hard%20Drive:System%20Folder:README.txt"
dirname, base = macpath.split(path)
name, ext = macpath.splitext(base)

print "dir is %s, name is %s, extension is %s" % (dirname, name, ext)
# dir is Hard%20Drive:System%20Folder, name is README, extension is .txt
#-----------------------------
# DON'T DO THIS - it's not portable.
def extension(path):
    pos = path.find(".")
    if pos == -1:
        return ""
    ext = path[pos+1:]
    if "/" in ext:
        # wasn't passed a basename -- this is of the form 'x.y/z'
        return ""
    return ext
#-----------------------------

Program: symirror


#!/usr/bin/python
# sysmirror - build spectral forest of symlinks
import sys, os, os.path

pgmname = sys.argv[0]
if len(sys.argv)!=3:
    print "usage: %s realdir mirrordir" % pgmname
    raise SystemExit

(srcdir, dstdir) = sys.argv[1:3]
if not os.path.isdir(srcdir):
    print "%s: %s is not a directory" % (pgmname,srcdir)
    raise SystemExit
if not os.path.isdir(dstdir):
    try:
        os.mkdir(dstdir)
    except OSError:
        print "%s: can't make directory %s" % (pgmname,dstdir)
          raise SystemExit

# fix relative paths
srcdir = os.path.abspath(srcdir)
dstdir = os.path.abspath(dstdir)

def wanted(arg, dirname, names):
    for direntry in names:
        relname = "%s/%s" % (dirname, direntry)
        if os.path.isdir(relname):
            mode = os.stat(relname).st_mode
            try:
                os.mkdir("%s/%s" % (dstdir,relname), mode)
            except:
                print "can't mkdir %s/%s" % (dstdir,relname)
                raise SystemExit
        else:
            if relname[:2] == "./":
                relname = relname[2:]
            os.symlink("%s/%s" % (srcdir, relname), "%s/%s" % (dstdir,relname))

os.chdir(srcdir)
os.path.walk(".",wanted,None)

Program: lst

# @@INCOMPLETE@@
# @@INCOMPLETE@@