使用 Python 实现触摸?

touch是一个 Unix 实用程序,它将文件的修改和访问时间设置为一天中的当前时间。如果该文件不存在,则使用默认权限创建该文件。

如何将其实现为一个 Python 函数。

(目前 Google 关于“ python touch file”的搜索结果不是很好,但是指向 Os.utime。)

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

简单的:

def touch(fname):
open(fname, 'a').close()
os.utime(fname, None)
  • open确保那里有一个文件
  • utime确保时间戳被更新

理论上,可能有人会在open之后删除文件,导致utime引发异常。但可以说这没什么,因为坏事确实发生了。

复杂(可能有bug):

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)

这也尝试允许设置访问或修改时间,就像GNU touch。

这个方法试图比其他解决方案更无种族限制。(with关键字是Python 2.5中新增的。)

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

大致相当于这个。

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

现在,为了让它真正无竞争,你需要使用futimes并更改打开文件句柄的时间戳,而不是打开文件然后更改文件名上的时间戳(可能已经重命名)。不幸的是,Python似乎没有提供一种不经过ctypes或类似的方法来调用futimes


编辑

正如内特·帕森斯所指出的,Python 3.3将添加 指定文件描述符(当os.supports_fd时)转换为诸如os.utime这样的函数,这些函数将在底层使用futimes系统调用而不是utimes系统调用。换句话说:

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)

为什么不试试呢?:

import os


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

我相信这消除了任何重要的竞争条件。如果文件不存在,则会抛出异常。

这里唯一可能的竞态条件是,文件是在调用open()之前,但在os.utime()之后创建的。但这并不重要,因为在这种情况下,修改时间将如预期的那样,因为它必须发生在调用touch()期间。

下面是一些使用ctypes的代码(仅在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())

创建一个包含所需变量的字符串,并将其传递给os.system似乎是合乎逻辑的:

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

这在很多方面是不够的(例如,它不能处理空白),所以不要这样做。

一个更健壮的方法是使用subprocess:

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

虽然这比使用subshell (os.system)要好得多,但它仍然只适用于快速和肮脏的脚本;使用跨平台程序的公认答案。

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

看起来这是Python 3.4 - pathlib的新功能。

from pathlib import Path


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

这将在路径处创建一个file.txt

--

路径。触摸(模式= 0 o777 exist_ok = True)

在这个给定的路径上创建一个文件。如果给出了mode,它将与进程的umask值相结合,以确定文件模式和访问标志。如果文件已经存在,则如果exist_ok为true(并且其修改时间更新为当前时间),则函数成功,否则将引发FileExistsError。

这个答案兼容自Python-2.5以来的所有版本,当关键字with已经发布。

< p > 1. 如果不存在则创建文件+设置当前时间 < br > (与命令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

一个更健壮的版本:

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
< p > 2. 如果文件不存在,则创建该文件 < br > (不更新时间)

with open(fname, 'a'):  # Create file if does not exist
pass
< p > 3.只需更新文件访问/修改次数 < br > (如果不存在,则不创建文件)

import os


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

使用os.path.exists()并不会简化代码:

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)

奖金:目录下所有文件的更新时间

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)
你为什么不试试: newfile.py < / p >
#!/usr/bin/env python
import sys
inputfile = sys.argv[1]


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

Python newfile.py foobar.txt

使用子流程:

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

以下是充分的:

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

如果你想设置一个特定的触摸时间,使用操作系统。使用时间如下:

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

这里,atime和mtime都应该是int/float,并且应该等于epoch time(以秒为单位)到你想设置的时间。

可以使用pathlib.Path中的write_text()

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

对于更低级的解决方案,可以使用

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

还有一个用于触摸的python模块

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

你可以用pip install touch来安装它

我有一个用于备份的程序:https://stromberg.dnsalias.org/~strombrg/backshift/

我使用vmprof对它进行了分析,发现到目前为止,触摸是最耗时的部分。

所以我研究了快速接触文件的方法。

我发现在CPython 3.11上,这是最快的:

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


                                                                                

在Pypy3 7.3.9上,这是最快的:

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

在这两者中,pypy3的最佳性能仅略快于cpython的最佳性能。

我可能有一天会创建一个关于这个的网页,但现在我所拥有的只是一个Subversion repo: https://stromberg.dnsalias.org/svn/touch/trunk 它包括我尝试过的4种触摸方式