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.)
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.
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)
def touch(fname):
if os.path.exists(fname):
os.utime(fname, None)
else:
open(fname, 'a').close()
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().
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())
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)
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.
with open(file_name,'a') as f:
pass
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.
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.
"open(file_name, 'a').close()" did not work for me in Python 2.7 on Windows. "os.utime(file_name, None)" worked just fine.
Also, I had a need to recursively touch all files in a directory with a date older than some date. I created hte following based on ephemient's very helpful response.
def touch(file_name):
# Update the modified timestamp of a file to now.
if not os.path.exists(file_name):
return
try:
os.utime(file_name, None)
except Exception:
open(file_name, 'a').close()
def midas_touch(root_path, older_than=dt.now(), pattern='**', recursive=False):
'''
midas_touch updates the modified timestamp of a file or files in a
directory (folder)
Arguements:
root_path (str): file name or folder name of file-like object to touch
older_than (datetime): only touch files with datetime older than this
datetime
pattern (str): filter files with this pattern (ignored if root_path is
a single file)
recursive (boolean): search sub-diretories (ignored if root_path is a
single file)
'''
# if root_path NOT exist, exit
if not os.path.exists(root_path):
return
# if root_path DOES exist, continue.
else:
# if root_path is a directory, touch all files in root_path
if os.path.isdir(root_path):
# get a directory list (list of files in directory)
dir_list=find_files(root_path, pattern='**', recursive=False)
# loop through list of files
for f in dir_list:
# if the file modified date is older thatn older_than, touch the file
if dt.fromtimestamp(os.path.getmtime(f)) < older_than:
touch(f)
print "Touched ", f
# if root_path is a file, touch the file
else:
# if the file modified date is older thatn older_than, touch the file
if dt.fromtimestamp(os.path.getmtime(f)) < older_than:
touch(root_path)
来源:https://stackoverflow.com/questions/1158076/implement-touch-using-python