Реализовать сенсорный с помощью Python?

touch утилита Unix, которая устанавливает время модификации и доступа к файлам в соответствии с текущим временем дня Если файл не существует, он создается с разрешениями по умолчанию.

Как бы вы реализовали это как функцию Python? Постарайтесь быть кроссплатформенным и завершенным.

(Текущие результаты Google для "Python Touch File" не так уж хороши, но указывают на os.utime.)

18 ответов

Решение

Это пытается быть немного более свободным от гонок, чем другие решения. (The 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 добавит указание файлового дескриптора (когда 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)

Похоже, что это новый с Python 3.4 - pathlib,

from pathlib import Path

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

Это создаст file.txt на пути.

-

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

Создайте файл по указанному пути. Если задан режим, он объединяется со значением umask процесса для определения режима файла и флагов доступа. Если файл уже существует, функция завершается успешно, если exist_ok имеет значение true (и его время модификации обновляется до текущего времени), в противном случае вызывается FileExistsError.

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

Почему бы не попробовать это?

import os

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

Я верю, что это устраняет любые расы, которые имеют значение. Если файл не существует, то будет выдано исключение.

Единственное возможное условие состязания здесь - это если файл создается до вызова open(), но после os.utime(). Но это не имеет значения, потому что в этом случае время модификации будет таким, как ожидалось, так как оно должно было произойти во время вызова touch().

Для более низкоуровневого решения можно использовать

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

Этот ответ совместим со всеми версиями начиная с Python-2.5, когда ключевое словоwithбыл выпущен.

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)

Вот некоторый код, который использует 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())

Упрощенные:

def touch(fname):
    open(fname, 'a').close()
    os.utime(fname, None)
  • open обеспечивает наличие файла
  • utime гарантирует, что метки времени обновляются

Теоретически, возможно, кто-то удалит файл после open, в результате чего Utime поднять исключение. Но, возможно, это нормально, потому что случилось что-то плохое.

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

Достаточно следующего:

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 и должны быть равны времени эпохи в секундах времени, которое вы хотите установить.

write_text() из pathlib.Path может быть использован.

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

Сложный (возможно глючный):

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.

Может показаться логичным создать строку с нужными переменными и передать ее в os.system:

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

Это неадекватно по ряду причин (например, он не обрабатывает пробелы), поэтому не делайте этого.

Более надежный метод - использовать подпроцесс:

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

Хотя это гораздо лучше, чем использовать подоболочку (с os.system), она по-прежнему подходит только для быстрых и грязных скриптов; используйте принятый ответ для кроссплатформенных программ.

"open(file_name, 'a').close()" не работал для меня в Python 2.7 в Windows. "os.utime(имя_файла, нет)" работал просто отлично.

Кроме того, мне нужно было рекурсивно коснуться всех файлов в каталоге с датой старше некоторой даты. Я создал следующее на основе очень полезного ответа 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)

Почему бы вам не попробовать: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"])

У меня есть программа, которую я использую для резервного копирования: 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 способа выполнения прикосновений, которые я пробовал.

Если вы не возражаете против попытки - кроме того...

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

Однако следует отметить одну вещь: если существует файл с таким же именем, он не будет работать и завершится ошибкой.

Также есть модуль python для сенсорного ввода.

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

Вы можете установить его с помощью pip install touch

Другие вопросы по тегам