【问题标题】:Implement touch using Python?使用 Python 实现触摸?
【发布时间】:2010-11-12 14:03:15
【问题描述】:

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

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

(目前“python touch file”的谷歌结果不是很好,但指向os.utime。)

【问题讨论】:

  • 请考虑更新已接受的答案,因为此功能已内置到 Python 标准库中。
  • @Miles 接受的答案正是问题所要求的 - 它实际上是在 Python 中实现了该函数,而不是使用库。
  • @styrofoamfly 标准库是 Python 的一部分。提问者真正想知道的(大多数人通过 Google 提出这个问题)很可能是如何在他们的 Python 程序中实现类似touch 的功能,而不是如何从头开始重新实现它;这些人最好通过向下滚动到pathlib 解决方案来服务。尽管它现在是内置的,但这个答案在“python 触摸文件”方面的 Google 排名比 the relevant documentation 好得多。
  • @miles Python 2 (不幸的是)仍然比 3 更广泛地使用,所以我认为接受的答案仍然是更相关的答案。但是您的评论很好地为人们指出了第二个答案。
  • Python 2 将于今年年底 EOL。

标签: python utility


【解决方案1】:

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

from pathlib import Path

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

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

--

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

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

【讨论】:

  • 在 Python2.7 上:pip install pathlib
  • 自我注意:如果包含touch()ed 文件的目录尚不存在,请使用Path('/some/path').mkdir()。
  • 我认为我们应该使用pathlib2 而不是pathlib,因为pathlib 现在只是错误修复。因此,在 Python 2.7 上:pip install pathlib2,然后是 from pathlib2 import Path。
  • @IanLin 没有理由安装一个库来做标准库已经支持的事情。您是否将bitbucket.org/pitrou/pathlib/src/default 与docs.python.org/dev/library/pathlib.html 混淆了?
  • 我很惊讶它默认为777,而不是像644 这样更保守的东西。
【解决方案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()

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


编辑

正如Nate Parsons 所指出的,Python 3.3 将addspecifying a file descriptor(当os.supports_fd)转换为os.utime 等函数,它将使用futimes 系统调用而不是utimes 系统调用下的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)

【讨论】:

  • 这是真正的解决方案——这就是 coreutils 中的 touch(1) 的方式,除非 futimes() 不可用。 futimes 不是一个可移植的功能,甚至不存在于较旧的 2.6 Linux 内核上,因此即使您确实使用它,您也需要处理 ENOSYS 并回退到 utime。
  • (上面的校对错误:"This" = open("a") + futimes。)幸运的是,很难想到不使用 futimes 的竞争条件实际上很重要。您最终可能遇到的“错误”情况是文件在 open() 和 utime() 之间被重命名,在这种情况下,您既不会创建新文件,也不会触及旧文件。这可能很重要,但大多数时候并不重要。
  • cygwin touch 可以在只读文件上发挥它的魔力,但这段代码不能。但是,如果我用 try: except IOError as e: (check e.errno) os.utime(filename, times) 包围它似乎可以工作
  • 仅供参考,似乎 futimes 是在 3.3 中添加的
  • 注意:内置的 file 函数已从 Python 3 中删除,而必须使用 open 代替。我完全错过了这一点,因为我正在使用的编辑器 (gedit) 的语法突出显示仍然针对 Python 2。
【解决方案3】:
def touch(fname):
    if os.path.exists(fname):
        os.utime(fname, None)
    else:
        open(fname, 'a').close()

【讨论】:

  • 同意。正确的解决方案是: def touch(fname): open(fname, 'wa').close()
  • @Greg,虽然它解决了潜在的赛车条件问题,但open(fname, 'a').close() 不会随时更改。
  • @SilentGhost:没错,但这没关系,因为如果文件存在,那么它是刚刚创建的。当然,您可以将电话留给 os.utime() 以获取预先存在的文件。
  • 为什么不直接打开确认它存在,然后调用utime?
  • @itsadok:因为它触发了另一个竞争条件:如果文件在open() 和utime() 调用之间被删除 怎么办?恕我直言,提供了一个强大的解决方案here
【解决方案4】:

为什么不试试这个?:

import os

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

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

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

【讨论】:

    【解决方案5】:

    当关键字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)
    

    【讨论】:

      【解决方案6】:

      对于更底层的解决方案,可以使用

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

      【讨论】:

      • 来自文档os.O_CREAT − create file if it does not exist
      【解决方案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
          

          【讨论】:

          • 失败:with open(fn,'a'): pass 或替代open(fn, 'a').close() 不要在 Red Hat 7 上使用 Python 2.7.5 更改修改时间(文件系统是 XFS)。在我的平台上,如果不存在,这些解决方案只会创建一个空文件。 :-/
          【解决方案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)])

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

              【讨论】:

              • 这不是很安全:当文件名中有空格时会发生什么?
              • subprocess.call(['touch', os.path.join(dirname, fileName)]) 比使用子shell(使用os.system)要好得多。但是,仅将其用于快速而肮脏的脚本,将公认的答案用于跨平台程序。
              • touch 不是跨平台可用命令(例如 Windows)
              【解决方案13】:

              你为什么不试试: 新文件.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"])
              

              【讨论】:

              • 警告:上面的 Python 代码会截断文件。使用"a" 而不是"w"。
              【解决方案14】:

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

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

              【讨论】:

                【解决方案15】:

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

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

                你可以用pip install touch安装它

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-09-03
                  • 2023-03-20
                  • 1970-01-01
                  • 1970-01-01
                  • 2020-05-06
                  • 2019-03-12
                  • 2018-01-19
                  相关资源
                  最近更新 更多