Реалізувати touch за допомогою Python?


352

touch- це утиліта Unix, яка встановлює час модифікації та доступ до файлів до поточного часу доби. Якщо файл не існує, він створюється з дозволами за замовчуванням.

Як би ти реалізував це як функцію Python? Спробуйте бути крос-платформою та повною.

(Поточні результати Google для "файлу сенсорного пітона" не дуже великі, але вказують на os.utime .)


4
Будь ласка, подумайте про оновлення прийнятої відповіді тепер, коли ця функціональність вбудована в stdlib Python.
Майлз

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

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

@miles Python 2, на жаль, все ще широко використовується, ніж 3, тому я вважаю, що прийнята відповідь все-таки є більш актуальною. Але ваш коментар добре допомагає вказувати людей на другу відповідь.
ітадок

6
Python 2 - EOL наприкінці цього року.
Макс Гаснер

Відповіді:


304

Схоже, це нове, як у Python 3.4 - pathlib.

from pathlib import Path

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

Це створить file.txtна шляху.

-

Path.touch (режим = 0o777, існує_ок = Істинно)

Створіть файл на цьому заданому шляху. Якщо заданий режим, він поєднується зі значенням umask процесу для визначення режиму файлу та прапорів доступу. Якщо файл вже існує, функція успішна, якщо true_ok є істинним (і час його модифікації оновлено до поточного часу), інакше 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
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або подібне ...


EDIT

Як відмітив Nate Parsons , Python 3.3 додасть вказівку дескриптора файлу (коли os.supports_fd) до таких функцій, як os.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)

Це справжнє рішення - і ось як дотик (1) в coreutils робить це, якщо футвіймс () недоступний. futimes не є портативною функцією і не існує навіть на старих ядрах 2.6 Linux, тому вам потрібно мати справу з ENOSYS і повертатися до часу, навіть якщо ви цим користуєтеся.
Гленн Мейнард

(Помилка коректування вище: "This" = open ("a") + futimes.) На щастя, важко придумати випадок, коли умова гонки щодо невикористання футлуменів насправді має значення. "Неправильний" випадок, з яким ви можете опинитися, - це перейменування файлу між open () та utime (), і в цьому випадку ви не створите новий файл і не торкнетесь старого. Це може мати значення, але більшість часу це не стане.
Гленн Мейнард

cygwin touch може зробити свою магію на файлах, які лише для читання, але цей код не може. Однак це, здається, спрацює, якщо я оточую його спробу: <code>, крім IOError як e: (перевірити e.errno) os.utime (ім'я файлу, разів)
dash-tom-bang

FYI, схоже, що час був доданий у 3.3
Нейт Парсонс

Примітка: вбудована fileфункція була видалена з Python 3, і openїї потрібно використовувати замість неї. Я цілком пропустив це, оскільки виділення синтаксису редактора, який я використовую (gedit), все ще націлений на Python 2.
Bart

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

@Greg, хоча вирішує потенційну проблему перегонів, open(fname, 'a').close()не зміниться.
SilentGhost

@SilentGhost: Це правда, але це нормально, тому що якщо файл існує, то він був просто створений. Звичайно, ви можете залишити дзвінок os.utime()там для вже існуючих файлів.
Грег Х'югілл

4
Чому б просто не відкритись, щоб переконатися, що він існує, а потім зателефонувати до часу?
itadok

31

Чому б не спробувати це ?:

import os

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

Я вважаю, що це усуває будь-які умови раси, які мають значення. Якщо файлу не існує, буде викинуто виняток.

Єдиною можливою умовою гонки тут є, якщо файл створюється перед тим, як викликати open (), але після os.utime (). Але це не має значення, оскільки в цьому випадку час модифікації буде таким, як очікувалося, оскільки це повинно відбутися під час дзвінка на дотик ().


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

Fail : 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, викликаючи тимчасове значення, щоб створити виняток. Але, мабуть, це нормально, оскільки сталося щось погане.


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). Але все-таки, використовуйте це лише для швидких та брудних сценаріїв, використовуйте прийняту відповідь для міжплатформних програм.
айке

1
touch- це не доступна міжплатформна команда (наприклад, Windows)
Mike T

1

"open (ім'я_файлу, 'a"). close () "не працював для мене в Python 2.7 у Windows. "os.utime (ім'я_файлу, None)" працював чудово.

Також у мене була необхідність рекурсивно торкатися всіх файлів у каталозі з датою, старшою за якусь дату. Я створив hte, виходячи з дуже корисної відповіді ефемії.

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.