使用 Python 实现触摸?
- 2025-03-04 08:25:00
- admin 原创
- 69
问题描述:
touch
是一个 Unix 实用程序,用于将文件的修改和访问时间设置为当前时间。如果文件不存在,则使用默认权限创建该文件。
您将如何将其实现为 Python 函数?尝试跨平台且完整。
(目前 Google 搜索“python touch file”的搜索结果并不理想,但指向os.utime。)
解决方案 1:
看起来这是 Python 3.4 中的新功能 - pathlib
。
from pathlib import Path
Path('path/to/file.txt').touch()
这将file.txt
在路径上创建一个。
--
路径.触摸(模式=0o777,exist_ok=True)
在给定路径下创建一个文件。如果给出了模式,则将其与进程的 umask 值相结合以确定文件模式和访问标志。如果文件已存在,则如果 exist_ok 为真(并且其修改时间更新为当前时间),则该函数成功,否则会引发 FileExistsError。
解决方案 2:
与其他解决方案相比,此解决方案尽量避免竞争。(该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()
现在,要真正实现无竞争,您需要使用并更改打开文件句柄的时间戳,而不是打开文件然后更改文件名(可能已重命名)上的时间戳。不幸的是,Python 似乎没有提供不经过或类似futimes
操作即可调用的方法...futimes
`ctypes`
编辑
正如Nate Parsons所指出的,Python 3.3 将为诸如 之类的函数添加 指定文件描述符(当 时os.supports_fd
)的功能os.utime
,这将使用futimes
syscall 而不是utimes
底层的 syscall。换句话说:
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)
解决方案 3:
def touch(fname):
if os.path.exists(fname):
os.utime(fname, None)
else:
open(fname, 'a').close()
解决方案 4:
为什么不尝试一下呢?:
import os
def touch(fname):
try:
os.utime(fname, None)
except OSError:
open(fname, 'a').close()
我相信这可以消除任何重要的竞争条件。如果文件不存在,则会引发异常。
这里唯一可能的竞争条件是文件是在调用 open() 之前但在 os.utime() 之后创建的。但这并不重要,因为在这种情况下,修改时间将如预期的那样,因为它一定是在调用 touch() 期间发生的。
解决方案 5:
对于更低级的解决方案,可以使用
os.close(os.open("file.txt", os.O_CREAT))
为了进行比较,GNU touch 使用
O_WRONLY | O_CREAT | O_NONBLOCK | O_NOCTTY
其中 为mode
,MODE_RW_UGO
即0o666
。
解决方案 6:
with
此答案与自关键字发布以来的 Python-2.5 的所有版本兼容。
1. 如果不存在则创建文件 + 设置当前时间
(与命令完全相同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
2. 如果不存在则创建文件
(不更新时间)
with open(fname, 'a'): # Create file if does not exist
pass
3. 仅更新文件访问/修改时间
(如果不存在则不创建文件)
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)
解决方案 7:
以下是一些使用 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())
解决方案 8:
简单来说:
def touch(fname):
open(fname, 'a').close()
os.utime(fname, None)
确保
open
那里有一个文件确保
utime
时间戳得到更新
理论上,有人可能会在 之后删除该文件open
,导致 utime 引发异常。但可以说这没问题,因为确实发生了一些不好的事情。
解决方案 9:
with open(file_name,'a') as f:
pass
解决方案 10:
以下内容已足够:
import os
def func(filename):
if os.path.exists(filename):
os.utime(filename)
else:
with open(filename,'a') as f:
pass
如果要为触摸设置特定时间,请使用 os.utime ,如下所示:
os.utime(filename,(atime,mtime))
这里,atime 和 mtime 都应该是 int/float,并且应该等于您想要设置的时间的纪元时间(以秒为单位)。
解决方案 11:
复杂(可能有缺陷):
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 一样。
解决方案 12:
创建一个包含所需变量的字符串并将其传递给 os.system 看起来合乎逻辑:
touch = 'touch ' + dir + '/' + fileName
os.system(touch)
这在很多方面都是不够的(例如,它不处理空格),所以不要这样做。
更为强大的方法是使用 subprocess :
subprocess.call(['touch', os.path.join(dirname, fileName)])
虽然这比使用子shell(带有os.system)要好得多,但它仍然只适用于快速而粗糙的脚本;对于跨平台程序,请使用公认的答案。
解决方案 13:
write_text()
即可pathlib.Path
使用。
>>> from pathlib import Path
>>> Path('aa.txt').write_text("")
0
解决方案 14:
还有一个用于触摸的 Python 模块
>>> from touch import touch
>>> touch(file_name)
您可以使用以下方式安装pip install touch
解决方案 15:
你为什么不尝试一下:newfile.py
#!/usr/bin/env python
import sys
inputfile = sys.argv[1]
with open(inputfile, 'r+') as file:
pass
python 新文件.py foobar.txt
或者
使用子流程:
import subprocess
subprocess.call(["touch", "barfoo.txt"])
解决方案 16:
我有一个用于备份的程序: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 仓库:
https://stromberg.dnsalias.org/svn/touch/trunk
它包含了我尝试过的 4 种触摸方式。
扫码咨询,免费领取项目管理大礼包!