使用Python实现触摸?


352

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

您如何将其实现为Python函数?尝试跨平台并完善。

(Google当前针对“ python触摸文件”的搜索结果并不理想,但指向os.utime。)


4
由于此功能已内置在Python stdlib中,因此请考虑更新接受的答案。
英里

@Miles接受的答案完全符合问题的要求-它实际上是在Python中实现了该功能,而不是使用库。
发泡胶飞

5
@styrofoamfly标准库 Python的一部分。提问者真正想知道的问题(大多数人通过Google提出此问题)的真正可能是如何touch在其Python程序中实现类似功能,而不是如何从头开始重新实现它。向下滚动到pathlib解决方案可以为那些人提供最好的服务。尽管它是内置的,但与相关文档相比,该答案在Google中对“ python触摸文件”的排名要好得多。
迈尔斯

@miles Python 2(不幸的是)仍然比3更广泛地使用,所以我认为公认的答案仍然是更相关的。但是您的评论可以很好地使人们指向第二个答案。
itsadok

6
Python 2将于今年年底停产。
Max Gasner '19

Answers:


303

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

from pathlib import Path

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

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

-

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

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


3
在Python2.7上:pip install pathlib
Andre Miras

7
自言自语:Path('/some/path').mkdir()如果包含要touch()编辑的文件的目录尚不存在,请使用。
JacobIRR '18

1
我认为我们应该使用pathlib2而不是pathlib因为pathlib现在仅修正错误。因此,在Python 2.7上:pip install pathlib2然后from pathlib2 import Path
伊恩·林

@IanLin没有理由安装库来执行标准库已支持的操作。您是否将docs.python.org/dev/library/pathlib.htmlbitbucket.org/pitrou/pathlib/src/default 混淆
Michael Mrozek

该评论回答了Andre关于Python 2.7的评论,该评论没有该标准库。随时阅读pypi.org/project/pathlib2中
Ian Lin

242

与其他解决方案相比,这要使比赛更加自由。(该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似乎没有提供一种futimes无需经过ctypes任何类似操作即可调用的方法...


编辑

Nate Parsons所述,Python 3.3将为诸如之类的函数添加 指定文件描述符(when os.supports_fdos.utime,该函数将使用futimessyscall而不是内部的utimessyscall。换一种说法:

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)

这是真正的解决方案-这就是coreutils中的touch(1)的工作方式,除非futimes()不可用。futimes不是一个可移植的功能,甚至在较早的2.6 Linux内核中也不存在,因此即使使用ENOSYS,您也需要处理ENOSYS并退回到utime。
格伦·梅纳德

(上面的校对错误:“ This” = open(“ a”)+ futimes。)幸运的是,很难想到不使用futimes的竞争条件确实很重要的情况。您可能最终遇到的“错误”情况是在open()和utime()之间重命名了文件,在这种情况下,您既不会创建新文件,也不会触摸旧文件。这可能很重要,但是在大多数情况下都不重要。
格伦·梅纳德

cygwin touch可以对只读文件发挥作用,但是此代码不能。但是,如果我用try:<code>包围它似乎可行,除了IOError如e :(检查e.errno)os.utime(文件名,时间)
dash-tom-bang

仅供参考,似乎futimes在3.3版中添加了
Nate Parsons

注意:内置file函数已从Python 3中删除,而open必须使用它。我完全错过了这一点,因为语法编辑器的突出我使用(gedit中)仍然是针对Python的2
巴特

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

24
此解决方案中存在潜在的竞争条件:如果文件不存在,并且是在此函数到达open()调用之前由另一个进程创建的,则文件的内容将被截断。建议'a'改为使用模式。
格雷格·休吉尔

7
同意 正确的解决方案是:def touch(fname):open(fname,'wa')。close()
stepancheg

@Greg解决了潜在的赛车状况问题,但open(fname, 'a').close()不会立即改变。
SilentGhost

@SilentGhost:没错,但这没关系,因为如果文件存在,那么它就是创建的。当然,您可以将呼叫保留os.utime()在那里,以获取预先存在的文件。
格雷格·休吉尔

4
为什么不只是打开以确保它存在,然后调用utime?
itsadok

31

为什么不试试呢?:

import os

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

我相信这可以消除任何重要的比赛条件。如果该文件不存在,则将引发异常。

唯一可能的竞争条件是,如果文件是在调用open()之前但在os.utime()之后创建的。但这无关紧要,因为在这种情况下,修改时间将是预期的,因为修改时间必须在调用touch()的过程中发生。


8

以下是一些使用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

自从发布Python-2.5关键字以来,此答案与所有​​版本兼容with

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

一个更强大的版本:

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)

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

失败with open(fn,'a'): pass或者open(fn, 'a').close()不要在Red Hat 7(文件系统为XFS)上使用Python 2.7.5更改修改时间。在我的平台上,这些解决方案仅会创建一个空文件(如果不存在)。:-/
olibre

3

简单化:

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

从理论上讲,有人可能会在之后删除文件open,从而导致utime引发异常。但是可以说是可以的,因为确实发生了一些不好的事情。


1

复杂(可能是越野车):

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。


1

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

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

这在许多方面都是不够的(例如,它不处理空格),因此请不要这样做。

一种更可靠的方法是使用子过程:

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

尽管这比使用子shell(使用os.system)要好得多,但它仍然仅适用于快捷脚本。将接受的答案用于跨平台程序。


这不是很安全:文件名中有空格时会发生什么?
ayke 2012年

5
subprocess.call(['touch', os.path.join(dirname, fileName)])比使用subshel​​l(带有os.system)要好得多。但是,仍然只将它用于快速处理的脚本,将可接受的答案用于跨平台程序。
ayke 2012年

1
touch不是跨平台可用命令(例如Windows)
Mike T

1

“ open(file_name,'a')。close()”在Windows上的Python 2.7中对我不起作用。“ os.utime(file_name,None)”工作得很好。

另外,我还需要递归地触摸目录中某个日期早于某个日期的所有文件。我根据ephemient的非常有用的回复创建了以下关注者。

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)

1

你为什么不尝试:newfile.py

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

with open(inputfile, 'w') as file:
    pass

python newfile.py foobar.txt

要么

使用子过程:

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

0

以下内容就足够了:

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,并且应等于以秒为单位的纪元时间到您要设置的时间。


0

如果您不介意尝试,那么...

def touch_dir(folder_path):
    try:
        os.mkdir(folder_path)
    except FileExistsError:
        pass

不过要注意的一件事是,如果存在一个具有相同名称的文件,则该文件将无法工作,并且将以静默方式失败。


0

write_text()pathlib.Path可以使用。

>>> from pathlib import Path
>>> Path('aa.txt').write_text("")
0
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.