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


352

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

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

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


4
Пожалуйста, рассмотрите возможность обновления принятого ответа сейчас, когда эта функциональность встроена в стандартную библиотеку Python.
Майлз

@Miles Принятый ответ делает именно то, что задал вопрос - он фактически реализовал функцию в Python вместо использования библиотеки.
пенополистирол летать

5
@styrofoamfly Стандартная библиотека является частью Python. Вполне вероятно, что то, что действительно хочет знать задающий вопрос (и большинство людей приходят к этому вопросу через Google), - это как добиться touchфункциональности, подобной их программам на Python, а не как заново реализовать ее с нуля; этих людей лучше всего обслуживать, прокручивая до pathlibрешения. Несмотря на то, что он теперь встроен, этот ответ имеет гораздо лучший рейтинг в Google для «файла касания Python», чем в соответствующей документации .
Майлз

@miles Python 2 (к сожалению) все еще более широко используется, чем 3, поэтому я думаю, что принятый ответ все еще более актуален. Но ваш комментарий хорошо указывает людям на второй ответ.
itadok

6
Python 2 - это EOL в конце этого года.
Макс Гаснер

Ответы:


304

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

from pathlib import Path

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

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

-

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

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


3
На Python2.7:pip install pathlib
Андре Мирас

8
примечание к себе: используйте, Path('/some/path').mkdir()если каталог, содержащий файл для touch()редактирования, еще не существует.
JacobIRR

1
Я думаю, что мы должны использовать 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 ?
Михаил Мрозек

Этот комментарий отвечает на комментарий Андрея о Python 2.7, который не имеет этой стандартной библиотеки. Не стесняйтесь читать документ в pypi.org/project/pathlib2
Ян Лин

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или аналогичные ...


РЕДАКТИРОВАТЬ

Как отметил Нейт Парсонс , 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)

Это реальное решение - и именно так touch (1) в coreutils делает это, если только futimes () недоступен. futimes не является переносимой функцией и даже не существует в старых ядрах Linux версии 2.6, поэтому вам нужно иметь дело с ENOSYS и вернуться к времени, даже если вы его используете.
Гленн Мейнард

(Ошибка вычитки выше: «This» = open («a») + futimes.) К счастью, трудно представить себе случай, когда условие гонки, не использующее futimes, действительно имеет значение. «Неправильный» случай, с которым вы можете столкнуться, - это переименование файла между open () и utime (), в этом случае вы не создадите новый файл и не коснетесь старого. Это может иметь значение, но в большинстве случаев это не так.
Гленн Мейнард

Cygwin Touch может творить чудеса с файлами только для чтения, но этот код не может. Однако, кажется, что это работает, если я окружаю его try: <code>, за исключением IOError как e: (проверьте e.errno) os.utime (имя файла, времена)
dash-tom-bang

К вашему сведению, кажется, что futimes был добавлен в 3.3
Нейт Парсонс

Примечание: встроенная 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?
itadok

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. Создать файл, если он не существует + Установить текущее время
(точно так же, как команда 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()не изменяйте измененное время, используя Python 2.7.5 в Red Hat 7 (файловая система - XFS). На моей платформе эти решения просто создают пустой файл, если он не существует. : - /
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)])

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


Это не очень безопасно: что происходит, когда в имени файла есть пробел?
айке

5
subprocess.call(['touch', os.path.join(dirname, fileName)])это гораздо лучше, чем использовать подоболочку (с os.system). Но все же, используйте это только для быстрых и грязных скриптов, используйте принятый ответ для кроссплатформенных программ.
ayke

1
touchне является кроссплатформенной доступной командой (например, Windows)
Майк Т

1

"open (file_name, 'a'). close ()" не работал для меня в Python 2.7 в Windows. "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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.