Implementare il tocco usando Python?


352

touchè un'utilità Unix che imposta i tempi di modifica e accesso dei file sull'ora corrente del giorno. Se il file non esiste, viene creato con autorizzazioni predefinite.

Come lo implementeresti come una funzione Python? Cerca di essere multipiattaforma e completo.

(I risultati attuali di Google per "file touch Python" non sono così grandi, ma puntano a os.utime .)


4
Si prega di considerare l'aggiornamento della risposta accettata ora che questa funzionalità è integrata nello stdlib di Python.
Miglia,

@Miles La risposta accettata fa esattamente quello che ha posto la domanda: ha effettivamente implementato la funzione in Python invece di usare una libreria.
polistirolo vola

5
@styrofoamfly La libreria standard fa parte di Python. È molto probabile che ciò che la domanda che il richiedente vuole davvero sapere (e la maggior parte delle persone che arrivano a questa domanda tramite Google) sia come ottenere touchfunzionalità simili nei loro programmi Python, non come reimplementarlo da zero; quelle persone sono meglio servite scorrendo verso il basso fino alla pathlibsoluzione. Anche se ora è integrato, questa risposta ha un ranking Google molto migliore per "file touch Python" rispetto alla relativa documentazione .
Miglia

@miles Python 2 è (purtroppo) ancora più ampiamente usato di 3, quindi penso che la risposta accettata sia ancora quella più pertinente. Ma il tuo commento fa un buon lavoro nell'indicare le persone alla seconda risposta.
Il

6
Python 2 è EOL alla fine di quest'anno.
Max Gasner,

Risposte:


304

Sembra che questo sia nuovo a partire da Python 3.4 - pathlib.

from pathlib import Path

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

Questo creerà un file.txtpercorso.

-

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

Crea un file in questo percorso specificato. Se viene fornita la modalità, viene combinata con il valore umask del processo per determinare la modalità file e i flag di accesso. Se il file esiste già, la funzione ha esito positivo se exist_ok è true (e l'ora di modifica viene aggiornata all'ora corrente), altrimenti viene generato FileExistsError.


3
Su Python2.7:pip install pathlib
Andre Miras,

8
nota per sé: utilizzare Path('/some/path').mkdir()se la directory contenente il file da modificare touch()non esiste ancora.
JacobIRR,

1
Penso che dovremmo usare pathlib2invece di pathlibperché ora pathlibè solo bugfix. Pertanto, su Python 2.7: pip install pathlib2e quindi from pathlib2 import Path.
Ian Lin,

@IanLin Ci sono pochi motivi per installare una libreria per fare qualcosa che la libreria standard già supporta. Stai confondendo bitbucket.org/pitrou/pathlib/src/default con docs.python.org/dev/library/pathlib.html ?
Michael Mrozek,

Quel commento sta rispondendo al commento di Andre parlando di Python 2.7, che non ha quella libreria standard. Sentiti libero di leggere il documento in pypi.org/project/pathlib2
Ian Lin

242

Questo cerca di essere un po 'più libero da gara rispetto alle altre soluzioni. (La withparola chiave è nuova in Python 2.5.)

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

Praticamente equivalente a questo.

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

Ora, per renderlo davvero privo di razza, è necessario utilizzare futimese modificare il timestamp del filehandle aperto, invece di aprire il file e quindi modificare il timestamp sul nome del file (che potrebbe essere stato rinominato). Sfortunatamente, Python non sembra fornire un modo per chiamare futimessenza passare attraverso ctypeso simili ...


MODIFICARE

Come notato da Nate Parsons , Python 3.3 aggiungerà specificando un descrittore di file (quando os.supports_fd) a funzioni come os.utime, che utilizzerà la futimessyscall invece della utimessyscall sotto il cofano. In altre parole:

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)

Questa è la vera soluzione - ed è così che touch (1) in coreutils lo fa, a meno che futimes () non sia disponibile. futimes non è una funzione portatile e non esiste nemmeno sui kernel Linux 2.6 precedenti, quindi è necessario avere a che fare con ENOSYS e tornare all'utime anche se lo si utilizza.
Glenn Maynard,

(Errore di correzione sopra: "Questo" = open ("a") + futimes.) Fortunatamente, è difficile pensare a un caso in cui le condizioni di gara di non usare futimes contano davvero. Il caso "sbagliato" con cui potresti finire è il file che viene rinominato tra open () e utime (), nel qual caso non creerai un nuovo file né toccherai quello vecchio. Ciò può importare, ma il più delle volte non lo farà.
Glenn Maynard,

cygwin touch può fare la sua magia su file di sola lettura, ma questo codice non può. Tuttavia sembra funzionare se lo circondo con try: <code> tranne IOError come e: (controlla e.errno) os.utime (nome file, orari)
dash-tom-bang

Cordiali saluti, sembra che futimes sia stato aggiunto in 3.3
Nate Parsons il

Nota: la filefunzione integrata è stata rimossa da Python 3 e opendeve essere invece utilizzata. L'ho perso del tutto perché l'evidenziazione della sintassi dell'editor che sto usando (gedit) sta ancora prendendo di mira Python 2.
Bart

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

24
Esiste una potenziale condizione di competizione in questa soluzione: se il file non esiste e viene creato da un altro processo prima che questa funzione raggiunga la open()chiamata, il contenuto del file verrà troncato. Suggerisci 'a'invece di utilizzare la modalità .
Greg Hewgill,

7
Concordato. La soluzione corretta è solo: def touch (fname): open (fname, 'wa'). Close ()
stepancheg

@Greg, mentre risolve il potenziale problema delle condizioni di gara, open(fname, 'a').close()non cambierà il tempo.
SilentGhost,

@SilentGhost: è vero, ma va bene perché se il file esiste, è stato appena creato. Naturalmente lasceresti la chiamata os.utime()lì dentro per file preesistenti.
Greg Hewgill,

4
Perché non solo aprire per assicurarsi che esista, quindi chiamare utime?
entrato il

31

Perché non provarlo ?:

import os

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

Credo che questo elimini tutte le condizioni di gara che contano. Se il file non esiste, verrà generata un'eccezione.

L'unica possibile condizione di competizione qui è se il file viene creato prima che venga chiamato open () ma dopo os.utime (). Ma questo non importa perché in questo caso il tempo di modifica sarà come previsto poiché deve essere accaduto durante la chiamata a touch ().


8

Ecco un po 'di codice che utilizza i tipi (testato solo su 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

Questa risposta è compatibile con tutte le versioni da Python-2.5 quando la parola chiave withè stata rilasciata.

1. Crea il file se non esiste + Imposta l'ora corrente
(esattamente come il comando 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

Una versione più robusta:

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. Basta creare il file se non esiste
(non aggiorna l'ora)

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

3. Aggiorna semplicemente l'accesso al file / i tempi modificati
(non crea il file se non esistente)

import os

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

L'uso os.path.exists()non semplifica il codice:

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: tempo di aggiornamento di tutti i file in una directory

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

Fallito : with open(fn,'a'): passo in alternativa open(fn, 'a').close()non modificare l'ora modificata usando Python 2.7.5 su Red Hat 7 (il filesystem è XFS). Sulla mia piattaforma, queste soluzioni creano semplicemente un file vuoto se non esiste. : - /
olibre

3

Semplicistico:

def touch(fname):
    open(fname, 'a').close()
    os.utime(fname, None)
  • Le opengarantisce che non v'è un file lì
  • le utimeassicura che i timestamp vengono aggiornate

Teoricamente, è possibile che qualcuno elimini il file dopo il open, causando utime per sollevare un'eccezione. Ma probabilmente va bene, dal momento che è successo qualcosa di brutto.


1

Complesso (possibilmente buggy):

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)

Questo tenta di consentire anche l'impostazione del tempo di accesso o modifica, come GNU touch.


1

Potrebbe sembrare logico creare una stringa con le variabili desiderate e passarla a os.system:

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

Questo è inadeguato in diversi modi (ad esempio, non gestisce gli spazi bianchi), quindi non farlo.

Un metodo più efficace consiste nell'utilizzare il sottoprocesso:

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

Sebbene sia molto meglio dell'uso di una subshell (con os.system), è comunque adatto solo per gli script quick-and-dirty; utilizzare la risposta accettata per i programmi multipiattaforma.


Questo non è molto sicuro: cosa succede quando c'è uno spazio nel nome del file?
ayke,

5
subprocess.call(['touch', os.path.join(dirname, fileName)])è molto meglio dell'uso di una subshell (con os.system). Tuttavia, usa questo solo per script rapidi e sporchi, usa la risposta accettata per i programmi multipiattaforma.
ayke,

1
touchnon è un comando disponibile multipiattaforma (ad esempio Windows)
Mike T

1

"open (file_name, 'a'). close ()" non ha funzionato per me in Python 2.7 su Windows. "os.utime (file_name, None)" ha funzionato bene.

Inoltre, avevo la necessità di toccare ricorsivamente tutti i file in una directory con una data più vecchia di una data. Ho creato il seguente follower basato sulla risposta molto utile di effemerente.

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

Perché non provi: newfile.py

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

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

python newfile.py foobar.txt

o

usa sottoprocesso:

import subprocess
subprocess.call(["touch", "barfoo.txt"])

0

È sufficiente:

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

Se si desidera impostare un orario specifico per il tocco, utilizzare os.utime come segue:

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

Qui, atime e mtime dovrebbero essere entrambi int / float e dovrebbero essere uguali al tempo di epoca in secondi al tempo che si desidera impostare.


0

Se non ti dispiace provare, tranne ...

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

Una cosa da notare, tuttavia, se esiste un file con lo stesso nome, allora non funzionerà e fallirà silenziosamente.


0

write_text()da pathlib.Pathpuò essere utilizzato.

>>> from pathlib import Path
>>> Path('aa.txt').write_text("")
0
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.