tags:

views:

2430

answers:

4

touch is a Unix utility that sets the modification and access times of files to the current time of day. If the file doesn't exist, it is created with default permissions.

How would you implement it as a Python function? Try to be cross platform and complete.

(Current Google results for "python touch file" are not that great, but point to os.utime.)

Here's the source for GNU touch.

+3  A: 
def touch(fname):
    if os.path.exists(fname):
        os.utime(fname, None)
    else:
        open(fname, 'w').close()
SilentGhost
There's a potential race condition in this solution: If the file doesn't exist, and is created by another process before this function reaches the `open()` call, then the contents of the file will be truncated. Suggest using mode `'a'` instead.
Greg Hewgill
Agreed. Proper solution is just:def touch(fname): open(fname, 'wa').close()
stepancheg
@Greg, while it solves the potential racing condition issue, `open(fname, 'a').close()` won't change atime.
SilentGhost
@SilentGhost: That's true, but that's okay because if the file exists then it was *just* created. Of course you'd leave the call to `os.utime()` in there for pre-existing files.
Greg Hewgill
Why not just open to make sure it exists, then call utime?
itsadok
yeah, that might be the way.
SilentGhost
A: 

Simplistic:

def touch(fname):
    open(fname, 'a').close()
    os.utime(fname, None)
  • The open ensures there is a file there
  • the utime ensures that the timestamps are updated

Theoretically, it's possible someone will delete the file after the open, causing utime to raise an exception. But arguably that's OK, since something bad did happen.

itsadok
`ValueError: must have exactly one of read/write/append mode`
SilentGhost
Guess I should have checked first...
itsadok
`open(fname, 'a').close()` won't change the atime.
SilentGhost
A: 

Complex (possibly buggy):

def utime(fname, atime=None, mtime=None)
    if type(atime) is tuple:
        atime, mtime = atime

    if atime is None or mtime is None:
        statinfo = os.stat(fname)
        if atime is None:
            atime = statinfo.st_atime
        if mtime is None:
            mtime = statinfo.st_mtime

    os.utime(fname, (atime, mtime))


def touch(fname, atime=None, mtime=None):
    if type(atime) is tuple:
        atime, mtime = atime

    open(fname, 'a').close()
    utime(fname, atime, mtime)

This tries to also allow setting the access or modification time, like GNU touch.

itsadok
+11  A: 

This tries to be a little more race-free than the other solutions. (The with keyword is new in Python 2.5.)

import os
def touch(fname, times = None):
    with file(fname, 'a'):
        os.utime(fname, times)

Roughly equivalent to this.

import os
def touch(fname, times = None):
    fhandle = file(fname, 'a')
    try:
        os.utime(fname, times)
    finally:
        fhandle.close()

Now, to really make it race-free, you need to use futimes and change the timestamp of the open filehandle, instead of opening the file and then changing the timestamp on the filename (which may have been renamed). Unfortunately, Python doesn't seem to provide a way to call futimes without going through ctypes or similar...

ephemient
This is the real solution--and this is how touch(1) in coreutils does it, unless futimes() isn't available. futimes isn't a portable function and doesn't even exist on older 2.6 Linux kernels, so you need to deal with ENOSYS and fall back to utime even if you do use it.
Glenn Maynard
(Proofreading error above: "This" = open("a") + futimes.) Fortunately, it's hard to think of a case where the race condition of not using futimes actually matters. The "wrong" case you might end up with is the file being renamed between open() and utime(), in which case you'll neither create a new file nor touch the old one. That can matter, but most of the time it won't.
Glenn Maynard