Wdrożenie dotyku za pomocą Pythona?

352

touchto narzędzie uniksowe, które ustawia czasy modyfikacji i dostępu do plików na bieżącą porę dnia. Jeśli plik nie istnieje, jest tworzony z domyślnymi uprawnieniami.

Jak zaimplementowałbyś to jako funkcję Pythona? Staraj się być wieloplatformowy i kompletny.

(Bieżące wyniki Google dla „pliku dotykowego python” nie są tak świetne, ale wskazują na os.utime .)

itsadok
źródło
4
Proszę rozważyć zaktualizowanie zaakceptowanej odpowiedzi teraz, gdy ta funkcja jest wbudowana w stdlib Pythona.
Mil
@Miles Zaakceptowana odpowiedź robi dokładnie to, o co pytano - w rzeczywistości zaimplementowała funkcję w Pythonie zamiast korzystać z biblioteki.
styropian mucha
5
@styrofoamfly Standardowa biblioteka jest częścią Pythona. Jest bardzo prawdopodobne, że to, co pytający naprawdę chce wiedzieć (i większość osób docierających do tego pytania za pośrednictwem Google), to jak osiągnąć touchpodobną funkcjonalność w swoich programach Python, a nie jak ponownie wdrożyć ją od zera; najlepiej obsłużyć tych ludzi, przewijając w dół do pathlibrozwiązania. Mimo że jest teraz wbudowany, ta odpowiedź ma znacznie lepszy ranking Google dla „pliku dotykowego python” niż odpowiednia dokumentacja .
Miles
@miles Python 2 jest (niestety) nadal szerzej wykorzystywany niż 3, więc myślę, że zaakceptowana odpowiedź jest nadal bardziej trafna. Ale twój komentarz dobrze radzi sobie z wskazywaniem drugiej odpowiedzi.
itsadok
6
Python 2 jest EOL pod koniec tego roku.
Max Gasner

Odpowiedzi:

304

Wygląda na to, że jest to nowa wersja Python 3.4 - pathlib.

from pathlib import Path

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

Spowoduje to utworzenie file.txtścieżki.

-

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

Utwórz plik w tej podanej ścieżce. Jeśli podano tryb, jest on łączony z wartością umask procesu w celu określenia trybu pliku i flag dostępu. Jeśli plik już istnieje, funkcja powiedzie się, jeśli parametr istnienie_ok jest prawdą (a jego czas modyfikacji zostanie zaktualizowany do bieżącego czasu), w przeciwnym razie wywoływany jest błąd FileExistsError.

voidnologo
źródło
3
W Python2.7:pip install pathlib
Andre Miras
8
uwaga do samodzielnego użycia: użyj, Path('/some/path').mkdir()jeśli katalog zawierający plik do touch()edycji nie istnieje.
JacobIRR
1
Myślę, że powinniśmy używać pathlib2zamiast, pathlibponieważ teraz pathlibjest tylko poprawka błędów. Dlatego w Pythonie 2.7: pip install pathlib2a następnie from pathlib2 import Path.
Ian Lin
@IanLin Nie ma powodu, aby instalować bibliotekę, aby zrobić coś, co biblioteka standardowa już obsługuje. Czy mylisz bitbucket.org/pitrou/pathlib/src/default z docs.python.org/dev/library/pathlib.html ?
Michael Mrozek
Ten komentarz jest odpowiedzią na komentarz Andre mówiący o Pythonie 2.7, który nie ma tej standardowej biblioteki. Zapraszam do lektury dokumentu na pypi.org/project/pathlib2
Ian Lin
242

To stara się być trochę bardziej wolne od wyścigów niż inne rozwiązania. (Słowo withkluczowe jest nowe w Pythonie 2.5.)

import os
def touch(fname, times=None):
    with open(fname, 'a'):
        os.utime(fname, times)

Z grubsza to odpowiada.

import os
def touch(fname, times=None):
    fhandle = open(fname, 'a')
    try:
        os.utime(fname, times)
    finally:
        fhandle.close()

Teraz, aby naprawdę był wolny od wyścigów, musisz użyć futimesi zmienić znacznik czasu otwartego uchwytu pliku, zamiast otwierać plik, a następnie zmieniać znacznik czasu w nazwie pliku (który mógł zostać zmieniony). Niestety, wydaje się, że Python nie zapewnia sposobu dzwonienia futimesbez przechodzenia przez ctypeslub podobne ...


EDYTOWAĆ

Jak zauważył Nate Parsons , Python 3.3 doda określenie deskryptora pliku (kiedy os.supports_fd) do funkcji takich jak os.utime, które będą używać futimessyscall zamiast utimessyscall pod maską. Innymi słowy:

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)
efhemient
źródło
To jest prawdziwe rozwiązanie - i tak robi touch (1) w coreutils, chyba że futimes () nie jest dostępny. futimes nie jest funkcją przenośną i nawet nie istnieje na starszych jądrach Linuksa 2.6, więc musisz poradzić sobie z ENOSYS i wrócić do stanu gotowości, nawet jeśli go używasz.
Glenn Maynard
(Błąd korekty powyżej: „This” = open („a”) + futimes.) Na szczęście ciężko jest wymyślić przypadek, w którym warunki wyścigu związane z niestosowaniem futimes mają znaczenie. „Niewłaściwym” przypadkiem, który może się skończyć, jest zmiana nazwy pliku między open () a utime (), w którym to przypadku nie utworzysz nowego pliku ani nie dotkniesz starego. To może mieć znaczenie, ale przez większość czasu nie będzie.
Glenn Maynard
Cygwin touch potrafi działać na plikach tylko do odczytu, ale ten kod nie. Jednak wydaje się, że działa, jeśli otoczę go za pomocą try: <kod> z wyjątkiem IOError jako e: (sprawdź e.errno) os.utime (nazwa pliku, czasy)
dash-tom-bang
Do twojej wiadomości, wygląda na to, że przyszłość została dodana w 3.3
Nate Parsons
Uwaga: wbudowana filefunkcja została usunięta z Python 3 i opennależy jej użyć. Całkowicie mi tego brakowało, ponieważ podświetlanie składni edytora, którego używam (gedit), wciąż jest skierowane do Pythona 2.
Bart
42
def touch(fname):
    if os.path.exists(fname):
        os.utime(fname, None)
    else:
        open(fname, 'a').close()
SilentGhost
źródło
24
W tym rozwiązaniu istnieje potencjalny warunek wyścigu: jeśli plik nie istnieje i zostanie utworzony przez inny proces przed osiągnięciem open()wywołania przez tę funkcję , zawartość pliku zostanie obcięta. 'a'Zamiast tego sugeruj użycie trybu .
Greg Hewgill
7
Zgoda. Prawidłowe rozwiązanie to: def touch (fname): open (fname, 'wa'). Close ()
stepancheg
@Greg, chociaż rozwiązuje potencjalny problem warunków wyścigowych, open(fname, 'a').close()nie zmieni się w tym momencie.
SilentGhost
@SilentGhost: To prawda, ale nic nie szkodzi, ponieważ jeśli plik istnieje, to został właśnie utworzony. Oczywiście zostawiłbyś tam połączenie os.utime()dla wcześniej istniejących plików.
Greg Hewgill
4
Dlaczego nie po prostu otworzyć, aby upewnić się, że istnieje, a następnie zadzwonić do utime?
itsadok
31

Dlaczego nie spróbować ?:

import os

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

Wierzę, że to eliminuje wszelkie ważne warunki wyścigu. Jeśli plik nie istnieje, zostanie zgłoszony wyjątek.

Jedynym możliwym warunkiem wyścigu jest to, że plik zostanie utworzony przed wywołaniem open (), ale po os.utime (). Nie ma to jednak znaczenia, ponieważ w tym przypadku czas modyfikacji będzie zgodny z oczekiwaniami, ponieważ musiał nastąpić podczas wywołania touch ().

jcoffland
źródło
8

Oto kod używający ctypów (testowany tylko w systemie 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())
eug
źródło
8

Ta odpowiedź jest kompatybilna ze wszystkimi wersjami od wersji Python-2.5, kiedy słowo kluczowe withzostało zwolnione.

1. Utwórz plik, jeśli nie istnieje + Ustaw aktualny czas
(dokładnie taki sam jak polecenie 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

Bardziej niezawodna wersja:

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. Wystarczy utworzyć plik, jeśli nie istnieje
(nie aktualizuje czasu)

with open(fname, 'a'):  # Create file if does not exist
    pass

3. Po prostu zaktualizuj czasy dostępu do pliku / modyfikacji
(nie tworzy pliku, jeśli nie istnieje)

import os

try:
    os.utime(fname, None)  # Set access/modified times to now
except OSError:
    pass  # File does not exist (or no permission)

Korzystanie os.path.exists()nie upraszcza kodu:

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)

Bonus: Czas aktualizacji wszystkich plików w katalogu

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)
olibre
źródło
4
with open(file_name,'a') as f: 
    pass
Matt
źródło
Błąd : with open(fn,'a'): passlub alternatywnie open(fn, 'a').close()nie zmieniaj zmodyfikowanego czasu za pomocą Pythona 2.7.5 na Red Hat 7 (system plików to XFS). Na mojej platformie rozwiązania te tworzą po prostu pusty plik, jeśli nie istnieje. : - /
olibre
3

Uproszczony:

def touch(fname):
    open(fname, 'a').close()
    os.utime(fname, None)
  • W openeliminuje ryzyko istnieje plik
  • to utimegwarantuje, że znaczniki czasu są aktualizowane

Teoretycznie możliwe jest, że ktoś usunie plik po open, powodując, że utime zgłosi wyjątek. Ale to chyba w porządku, ponieważ stało się coś złego.

itsadok
źródło
1

Złożony (prawdopodobnie wadliwy):

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)

Próbuje to również umożliwić ustawienie czasu dostępu lub modyfikacji, takich jak GNU touch.

itsadok
źródło
1

Logiczne może się wydawać utworzenie łańcucha z pożądanymi zmiennymi i przekazanie go do os.system:

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

Jest to niewystarczające na wiele sposobów (np. Nie obsługuje białych znaków), więc nie rób tego.

Bardziej niezawodną metodą jest użycie podprocesu:

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

Chociaż jest to znacznie lepsze niż używanie podpowłoki (z os.system), nadal nadaje się tylko do szybkich i brudnych skryptów; użyj przyjętej odpowiedzi dla programów wieloplatformowych.

belacqua
źródło
To nie jest bardzo bezpieczne: co się stanie, gdy w nazwie pliku będzie spacja?
ayke
5
subprocess.call(['touch', os.path.join(dirname, fileName)])jest znacznie lepsze niż używanie podpowłoki (z os.system). Ale nadal używaj tego tylko do szybkich i brudnych skryptów, użyj akceptowanej odpowiedzi dla programów wieloplatformowych.
ayke
1
touchnie jest poleceniem dostępnym dla wielu platform (np. Windows)
Mike T
1

„open (nazwa_pliku,„ a ”). close ()” nie działało dla mnie w Pythonie 2.7 w systemie Windows. „os.utime (nazwa_pliku, Brak)” działało dobrze.

Musiałem też rekursywnie dotykać wszystkich plików w katalogu z datą starszą niż jakaś data. Stworzyłem następujące na podstawie bardzo pomocnej odpowiedzi 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)
Cadvena
źródło
1

Dlaczego nie spróbujesz: newfile.py

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

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

python newfile.py foobar.txt

lub

użyj podprocesu:

import subprocess
subprocess.call(["touch", "barfoo.txt"])
suresh Palemoni
źródło
0

Wystarczy:

import os
def func(filename):
    if os.path.exists(filename):
        os.utime(filename)
    else:
        with open(filename,'a') as f:
            pass

Jeśli chcesz ustawić określony czas dotyku, użyj os.utime w następujący sposób:

os.utime(filename,(atime,mtime))

Tutaj atime i mtime powinny być int / float i powinny być równe czasowi epoki w sekundach do czasu, który chcesz ustawić.

Amar Chand Dargad
źródło
0

Jeśli nie przeszkadza ci próba, z wyjątkiem ...

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

Należy jednak zauważyć, że jeśli plik o tej samej nazwie istnieje, nie będzie działał i po cichu zawiedzie.

Jaskiniowiec
źródło
0

write_text()z pathlib.Pathmożna użyć.

>>> from pathlib import Path
>>> Path('aa.txt').write_text("")
0
SuperNova
źródło