Touch mit Python implementieren?

352

touchist ein Unix-Dienstprogramm, das die Änderungs- und Zugriffszeiten von Dateien auf die aktuelle Tageszeit festlegt. Wenn die Datei nicht vorhanden ist, wird sie mit Standardberechtigungen erstellt.

Wie würden Sie es als Python-Funktion implementieren? Versuchen Sie plattformübergreifend und vollständig zu sein.

(Die aktuellen Google-Ergebnisse für "Python Touch-Datei" sind nicht so gut, verweisen jedoch auf os.utime .)

itsadok
quelle
4
Bitte erwägen Sie, die akzeptierte Antwort jetzt zu aktualisieren, da diese Funktionalität in die Python-stdlib integriert ist.
Meilen
@Miles Die akzeptierte Antwort macht genau das, wonach die Frage gestellt wurde - sie hat die Funktion tatsächlich in Python implementiert, anstatt eine Bibliothek zu verwenden.
Styropor fliegen
5
@styrofoamfly Die Standardbibliothek ist Teil von Python. Es ist sehr wahrscheinlich, dass der Fragesteller (und die meisten Leute, die über Google zu dieser Frage gelangen) wissen möchte, wie sie touchähnliche Funktionen in ihren Python-Programmen erreichen und nicht wie sie von Grund auf neu implementiert werden. Diese Personen werden am besten bedient, indem Sie zur pathlibLösung scrollen . Obwohl diese Antwort jetzt integriert ist, hat sie ein viel besseres Google-Ranking für "Python Touch-Datei" als die entsprechende Dokumentation .
Meilen
@miles Python 2 wird (leider) immer noch häufiger verwendet als 3, daher denke ich, dass die akzeptierte Antwort immer noch die relevantere ist. Aber Ihr Kommentar macht einen guten Job, indem er die Leute auf die zweite Antwort hinweist.
Itsadok
6
Python 2 ist Ende dieses Jahres EOL.
Max Gasner

Antworten:

304

Sieht so aus, als wäre dies ab Python 3.4 - neu pathlib.

from pathlib import Path

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

Dadurch wird ein file.txtam Pfad erstellt.

- -

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

Erstellen Sie eine Datei unter diesem angegebenen Pfad. Wenn der Modus angegeben ist, wird er mit dem Umask-Wert des Prozesses kombiniert, um den Dateimodus und die Zugriffsflags zu bestimmen. Wenn die Datei bereits vorhanden ist, ist die Funktion erfolgreich, wenn exist_ok true ist (und die Änderungszeit auf die aktuelle Zeit aktualisiert wird), andernfalls wird FileExistsError ausgelöst.

voidnologo
quelle
3
Auf Python2.7:pip install pathlib
Andre Miras
8
Hinweis für sich selbst: Verwenden Sie diese Option, Path('/some/path').mkdir()wenn das Verzeichnis mit der zu touch()bearbeitenden Datei noch nicht vorhanden ist.
JacobIRR
1
Ich denke, wir sollten pathlib2anstelle von verwenden, pathlibweil pathlibjetzt nur Bugfix ist. Deshalb auf Python 2.7: pip install pathlib2und dann from pathlib2 import Path.
Ian Lin
@IanLin Es gibt wenig Grund, eine Bibliothek zu installieren, um etwas zu tun, das die Standardbibliothek bereits unterstützt. Verwechseln Sie bitbucket.org/pitrou/pathlib/src/default mit docs.python.org/dev/library/pathlib.html ?
Michael Mrozek
Dieser Kommentar antwortet auf Andres Kommentar über Python 2.7, das diese Standardbibliothek nicht hat. Fühlen Sie sich frei, das Dokument in pypi.org/project/pathlib2
Ian Lin
242

Dies versucht, etwas rennfreier zu sein als die anderen Lösungen. (Das withSchlüsselwort ist neu in Python 2.5.)

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

Etwa gleichwertig.

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

Um es wirklich rennfrei zu machen, müssen Sie futimesden Zeitstempel des geöffneten Dateihandles verwenden und ändern, anstatt die Datei zu öffnen und dann den Zeitstempel des Dateinamens (der möglicherweise umbenannt wurde) zu ändern. Leider scheint Python keine Möglichkeit zum Anrufen zu bieten, futimesohne durchzugehen ctypesoder ähnliches ...


BEARBEITEN

Wie von Nate Parsons bemerkt , fügt Python 3.3 Funktionen wie z. B. einen Dateideskriptor (wann os.supports_fd) hinzu os.utime, der den futimesSyscall anstelle des utimesSyscall unter der Haube verwendet. Mit anderen Worten:

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)
kurzlebig
quelle
Dies ist die eigentliche Lösung - und so funktioniert es mit touch (1) in coreutils, es sei denn, futimes () ist nicht verfügbar. futimes ist keine portable Funktion und existiert nicht einmal auf älteren 2.6 Linux-Kerneln. Sie müssen sich also mit ENOSYS befassen und auf utime zurückgreifen, selbst wenn Sie es verwenden.
Glenn Maynard
(Korrekturfehler oben: "This" = open ("a") + futimes.) Glücklicherweise ist es schwer, sich einen Fall vorzustellen, in dem die Rennbedingung, keine Futimes zu verwenden, tatsächlich eine Rolle spielt. Der "falsche" Fall, den Sie möglicherweise haben, ist die Umbenennung der Datei zwischen open () und utime (). In diesem Fall erstellen Sie weder eine neue Datei noch berühren Sie die alte. Das kann wichtig sein, wird es aber meistens nicht.
Glenn Maynard
cygwin touch kann seine Wirkung auf schreibgeschützte Dateien ausüben, dieser Code jedoch nicht. Es scheint jedoch zu funktionieren, wenn ich es mit try: <code> außer IOError als e: (check e.errno) os.utime (Dateiname, Zeiten)
dash-tom-bang
Zu Ihrer Information, es scheint, als ob Futimes in 3.3
Nate Parsons
Hinweis: Die integrierte fileFunktion wurde aus Python 3 entfernt und openmuss stattdessen verwendet werden. Ich habe das total verpasst, weil die Syntaxhervorhebung des von mir verwendeten Editors (gedit) immer noch auf Python 2 abzielt.
Bart
42
def touch(fname):
    if os.path.exists(fname):
        os.utime(fname, None)
    else:
        open(fname, 'a').close()
SilentGhost
quelle
24
In dieser Lösung gibt es eine mögliche Race-Bedingung: Wenn die Datei nicht vorhanden ist und von einem anderen Prozess erstellt wird, bevor diese Funktion den open()Aufruf erreicht, wird der Inhalt der Datei abgeschnitten. Schlagen Sie 'a'stattdessen die Verwendung des Modus vor.
Greg Hewgill
7
Einverstanden. Die richtige Lösung ist nur: def touch (fname): open (fname, 'wa'). Close ()
stepancheg
@Greg löst zwar das potenzielle Problem mit den Rennbedingungen, open(fname, 'a').close()ändert sich aber zeitweise nicht.
SilentGhost
@ SilentGhost: Das stimmt, aber das ist in Ordnung, denn wenn die Datei existiert, wurde sie gerade erstellt. Natürlich würden Sie den Anruf os.utime()dort für bereits vorhandene Dateien belassen.
Greg Hewgill
4
Warum nicht einfach öffnen, um sicherzustellen, dass es existiert, und dann utime aufrufen?
itsadok
31

Warum nicht mal probieren?:

import os

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

Ich glaube, dies beseitigt alle wichtigen Rennbedingungen. Wenn die Datei nicht vorhanden ist, wird eine Ausnahme ausgelöst.

Die einzig mögliche Racebedingung ist hier, wenn die Datei vor dem Aufruf von open (), aber nach os.utime () erstellt wird. Dies spielt jedoch keine Rolle, da in diesem Fall die Änderungszeit wie erwartet ist, da sie während des Aufrufs von touch () aufgetreten sein muss.

jcoffland
quelle
8

Hier ist ein Code, der ctypes verwendet (nur unter Linux getestet):

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())
EU G
quelle
8

Diese Antwort ist mit allen Versionen seit Python-2.5 kompatibel, wenn das Schlüsselwort withveröffentlicht wurde.

1. Datei erstellen, falls nicht vorhanden + Aktuelle Zeit einstellen
(genau wie Befehl 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

Eine robustere Version:

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. Erstellen Sie einfach die Datei, wenn sie nicht vorhanden ist
(aktualisiert die Zeit nicht).

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

3. Aktualisieren Sie einfach den Dateizugriff / die geänderten Zeiten
(erstellt keine Datei, wenn diese nicht vorhanden ist).

import os

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

Die Verwendung os.path.exists()vereinfacht den Code nicht:

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: Aktualisierungszeit aller Dateien in einem Verzeichnis

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
quelle
4
with open(file_name,'a') as f: 
    pass
Matt
quelle
Fehler : with open(fn,'a'): passoder alternativ open(fn, 'a').close()Ändern Sie die geänderte Zeit nicht mit Python 2.7.5 unter Red Hat 7 (Dateisystem ist XFS). Auf meiner Plattform erstellen diese Lösungen nur eine leere Datei, wenn sie nicht vorhanden ist. : - /
olibre
3

Simpel:

def touch(fname):
    open(fname, 'a').close()
    os.utime(fname, None)
  • Das openstellt sicher, dass dort eine Datei vorhanden ist
  • das utimegewährleistet , dass die Zeitstempel aktualisiert werden

Theoretisch ist es möglich, dass jemand die Datei nach dem löscht open, wodurch utime eine Ausnahme auslöst. Aber wohl ist das in Ordnung, da etwas Schlimmes passiert ist.

itsadok
quelle
1

Komplex (möglicherweise fehlerhaft):

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)

Dies versucht auch das Einstellen der Zugriffs- oder Änderungszeit zu ermöglichen, wie z. B. GNU touch.

itsadok
quelle
1

Es mag logisch erscheinen, eine Zeichenfolge mit den gewünschten Variablen zu erstellen und an os.system zu übergeben:

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

Dies ist in vielerlei Hinsicht unzureichend (z. B. wird kein Leerzeichen verarbeitet). Tun Sie es also nicht.

Eine robustere Methode ist die Verwendung eines Unterprozesses:

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

Dies ist zwar viel besser als die Verwendung einer Subshell (mit os.system), eignet sich jedoch nur für Quick-and-Dirty-Skripte. Verwenden Sie die akzeptierte Antwort für plattformübergreifende Programme.

belacqua
quelle
Dies ist nicht sehr sicher: Was passiert, wenn der Dateiname ein Leerzeichen enthält?
Ayke
5
subprocess.call(['touch', os.path.join(dirname, fileName)])ist viel besser als die Verwendung einer Subshell (mit os.system). Verwenden Sie dies jedoch nur für schnelle und schmutzige Skripte. Verwenden Sie die akzeptierte Antwort für plattformübergreifende Programme.
Ayke
1
touchist kein plattformübergreifender Befehl (z. B. Windows)
Mike T
1

"open (Dateiname, 'a'). close ()" funktionierte in Python 2.7 unter Windows nicht. "os.utime (Dateiname, Keine)" funktionierte einwandfrei.

Außerdem musste ich alle Dateien in einem Verzeichnis mit einem Datum, das älter als ein Datum ist, rekursiv berühren. Ich habe das Folgende basierend auf der sehr hilfreichen Antwort von Ephemient erstellt.

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
quelle
1

Warum versuchst du es nicht: newfile.py

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

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

python newfile.py foobar.txt

oder

Unterprozess verwenden:

import subprocess
subprocess.call(["touch", "barfoo.txt"])
suresh Palemoni
quelle
0

Folgendes ist ausreichend:

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

Wenn Sie eine bestimmte Berührungszeit festlegen möchten, verwenden Sie os.utime wie folgt:

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

Hier sollten atime und mtime beide int / float sein und der Epochenzeit in Sekunden entsprechen, die Sie einstellen möchten.

Amar chand Dargad
quelle
0

Wenn Ihnen der Versuch nichts ausmacht - außer dann ...

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

Beachten Sie jedoch, dass eine Datei mit demselben Namen nicht funktioniert und im Hintergrund fehlschlägt.

Höhlenmensch
quelle
0

write_text()von pathlib.Pathkann verwendet werden.

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