itsadok
itsadok

Reputation: 29342

Implement touch using Python?

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.)

Upvotes: 545

Views: 358845

Answers (16)

heiner
heiner

Reputation: 718

For a more low-level solution one can use

os.close(os.open("file.txt", os.O_CREAT))

For comparison, GNU touch uses

O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY

with a mode of MODE_RW_UGO, which is 0o666.

Upvotes: 23

dstromberg
dstromberg

Reputation: 7177

I have a program that I use for backups: https://stromberg.dnsalias.org/~strombrg/backshift/

I profiled it using vmprof, and identified that touch was by far the most time-consuming part of it.

So I looked into ways of touching files quickly.

I found that on CPython 3.11, this was the fastest:

def touch3(filename, flags=os.O_CREAT | os.O_RDWR):                                                                                  
    """Touch a file using os.open+os.close - fastest on CPython 3.11."""                                                             
    os.close(os.open(filename, flags, 0o644))    

                                                                                

And on Pypy3 7.3.9, this was the fastest:

def touch1(filename):                                                                                                                
    """Touch a file using pathlib - fastest on pypy3, and fastest overall."""                                                        
    Path(filename).touch()                                                                                                           

Of the two, pypy3's best was only slightly faster cpython's best.

I may create a web page about this someday, but for now all I have is a Subversion repo: https://stromberg.dnsalias.org/svn/touch/trunk It includes the 4 ways of doing touches I tried.

Upvotes: 0

suresh Palemoni
suresh Palemoni

Reputation: 1226

Why don't you try: newfile.py

#!/usr/bin/env python
import sys
inputfile = sys.argv[1]

with open(inputfile, 'r+') as file:
    pass

python newfile.py foobar.txt

or

use subprocess:

import subprocess
subprocess.call(["touch", "barfoo.txt"])

Upvotes: 0

GLNB
GLNB

Reputation: 109

There is also a python module for touch

>>> from touch import touch
>>> touch(file_name)

You can install it with pip install touch

Upvotes: 0

SuperNova
SuperNova

Reputation: 27466

write_text() from pathlib.Path can be used.

>>> from pathlib import Path
>>> Path('aa.txt').write_text("")
0

Upvotes: 1

Amar chand Dargad
Amar chand Dargad

Reputation: 19

The following is sufficient:

import os
def func(filename):
    if os.path.exists(filename):
        os.utime(filename)
    else:
        with open(filename,'a') as f:
            pass

If you want to set a specific time for touch, use os.utime as follows:

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

Here, atime and mtime both should be int/float and should be equal to epoch time in seconds to the time which you want to set.

Upvotes: 4

oHo
oHo

Reputation: 54561

This answer is compatible with all versions since Python-2.5 when keyword with has been released.

1. Create file if does not exist + Set current time
(exactly same as command touch)

import os

fname = 'directory/filename.txt'
with open(fname, 'a'):     # Create file if does not exist
    os.utime(fname, None)  # Set access/modified times to now
                           # May raise OSError if file does not exist

A more robust version:

import os

with open(fname, 'a'):
  try:                     # Whatever if file was already existing
    os.utime(fname, None)  # => Set current time anyway
  except OSError:
    pass  # File deleted between open() and os.utime() calls

2. Just create the file if does not exist
(does not update time)

with open(fname, 'a'):  # Create file if does not exist
    pass

3. Just update file access/modified times
(does not create file if not existing)

import os

try:
    os.utime(fname, None)  # Set access/modified times to now
except OSError:
    pass  # File does not exist (or no permission)

Using os.path.exists() does not simplify the code:

from __future__ import (absolute_import, division, print_function)
import os

if os.path.exists(fname):
  try:
    os.utime(fname, None)  # Set access/modified times to now
  except OSError:
    pass  # File deleted between exists() and utime() calls
          # (or no permission)

Bonus: Update time of all files in a directory

from __future__ import (absolute_import, division, print_function)
import os

number_of_files = 0

#   Current directory which is "walked through"
#   |     Directories in root
#   |     |  Files in root       Working directory
#   |     |  |                     |
for root, _, filenames in os.walk('.'):
  for fname in filenames:
    pathname = os.path.join(root, fname)
    try:
      os.utime(pathname, None)  # Set access/modified times to now
      number_of_files += 1
    except OSError as why:
      print('Cannot change time of %r because %r', pathname, why)

print('Changed time of %i files', number_of_files)

Upvotes: 19

jcoffland
jcoffland

Reputation: 5431

Why not try this?:

import os

def touch(fname):
    try:
        os.utime(fname, None)
    except OSError:
        open(fname, 'a').close()

I believe this eliminates any race condition that matters. If the file does not exist then an exception will be thrown.

The only possible race condition here is if the file is created before open() is called but after os.utime(). But this does not matter because in this case the modification time will be as expected since it must have happened during the call to touch().

Upvotes: 37

voidnologo
voidnologo

Reputation: 1065

Looks like this is new as of Python 3.4 - pathlib.

from pathlib import Path

Path('path/to/file.txt').touch()

This will create a file.txt at the path.

--

Path.touch(mode=0o777, exist_ok=True)

Create a file at this given path. If mode is given, it is combined with the process’ umask value to determine the file mode and access flags. If the file already exists, the function succeeds if exist_ok is true (and its modification time is updated to the current time), otherwise FileExistsError is raised.

Upvotes: 618

Matt
Matt

Reputation: 3642

with open(file_name,'a') as f: 
    pass

Upvotes: 5

ephemient
ephemient

Reputation: 204718

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 open(fname, 'a'):
        os.utime(fname, times)

Roughly equivalent to this.

import os
def touch(fname, times=None):
    fhandle = open(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...


EDIT

As noted by Nate Parsons, Python 3.3 will add specifying a file descriptor (when os.supports_fd) to functions such as os.utime, which will use the futimes syscall instead of the utimes syscall under the hood. In other words:

import os
def touch(fname, mode=0o666, dir_fd=None, **kwargs):
    flags = os.O_CREAT | os.O_APPEND
    with os.fdopen(os.open(fname, flags=flags, mode=mode, dir_fd=dir_fd)) as f:
        os.utime(f.fileno() if os.utime in os.supports_fd else fname,
            dir_fd=None if os.supports_fd else dir_fd, **kwargs)

Upvotes: 256

SilentGhost
SilentGhost

Reputation: 319601

def touch(fname):
    if os.path.exists(fname):
        os.utime(fname, None)
    else:
        open(fname, 'a').close()

Upvotes: 53

Manoj
Manoj

Reputation: 1

It might seem logical to create a string with the desired variables, and pass it to os.system:

touch = 'touch ' + dir + '/' + fileName
os.system(touch)

This is inadequate in a number of ways (e.g.,it doesn't handle whitespace), so don't do it.

A more robust method is to use subprocess :

subprocess.call(['touch', os.path.join(dirname, fileName)])

While this is much better than using a subshell (with os.system), it is still only suitable for quick-and-dirty scripts; use the accepted answer for cross-platform programs.

Upvotes: 1

eug
eug

Reputation: 1158

Here's some code that uses ctypes (only tested on Linux):

from ctypes import *
libc = CDLL("libc.so.6")

#  struct timespec {
#             time_t tv_sec;        /* seconds */
#             long   tv_nsec;       /* nanoseconds */
#         };
# int futimens(int fd, const struct timespec times[2]);

class c_timespec(Structure):
    _fields_ = [('tv_sec', c_long), ('tv_nsec', c_long)]

class c_utimbuf(Structure):
    _fields_ = [('atime', c_timespec), ('mtime', c_timespec)]

utimens = CFUNCTYPE(c_int, c_char_p, POINTER(c_utimbuf))
futimens = CFUNCTYPE(c_int, c_char_p, POINTER(c_utimbuf)) 

# from /usr/include/i386-linux-gnu/bits/stat.h
UTIME_NOW  = ((1l << 30) - 1l)
UTIME_OMIT = ((1l << 30) - 2l)
now  = c_timespec(0,UTIME_NOW)
omit = c_timespec(0,UTIME_OMIT)

# wrappers
def update_atime(fileno):
        assert(isinstance(fileno, int))
        libc.futimens(fileno, byref(c_utimbuf(now, omit)))
def update_mtime(fileno):
        assert(isinstance(fileno, int))
        libc.futimens(fileno, byref(c_utimbuf(omit, now)))

# usage example:
#
# f = open("/tmp/test")
# update_mtime(f.fileno())

Upvotes: 8

itsadok
itsadok

Reputation: 29342

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.

Upvotes: 2

itsadok
itsadok

Reputation: 29342

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.

Upvotes: 6

Related Questions