Implementar toque usando Python?

352

touches una utilidad de Unix que establece la modificación y los tiempos de acceso de los archivos a la hora actual del día. Si el archivo no existe, se crea con permisos predeterminados.

¿Cómo lo implementaría como una función de Python? Intenta ser multiplataforma y completa.

(Los resultados actuales de Google para el "archivo táctil de Python" no son tan buenos, pero apuntan a os.utime ).

itsadok
fuente
44
Considere actualizar la respuesta aceptada ahora que esta funcionalidad está integrada en Python stdlib.
Millas el
@Miles La respuesta aceptada hace exactamente lo que pedía la pregunta: en realidad implementó la función en Python en lugar de usar una biblioteca.
espuma de poliestireno volar
55
@styrofoamfly La biblioteca estándar es parte de Python. Es muy probable que lo que el que pregunta realmente quiera saber (y la mayoría de las personas que llegan a esta pregunta a través de Google) es cómo lograr una touchfuncionalidad similar en sus programas Python, no cómo volver a implementarla desde cero; a esas personas se les sirve mejor desplazándose hacia la pathlibsolución. Aunque ahora está integrado, esta respuesta tiene una clasificación de Google mucho mejor para el "archivo táctil de Python" que la documentación relevante .
Miles
@miles Python 2 es (desafortunadamente) aún más ampliamente utilizado que 3, por lo que creo que la respuesta aceptada sigue siendo la más relevante. Pero su comentario hace un buen trabajo al señalar a las personas la segunda respuesta.
itsadok
66
Python 2 es EOL a finales de este año.
Max Gasner

Respuestas:

304

Parece que esto es nuevo a partir de Python 3.4 - pathlib.

from pathlib import Path

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

Esto creará un file.txten el camino.

-

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

Crea un archivo en esta ruta dada. Si se proporciona el modo, se combina con el valor umask del proceso para determinar el modo del archivo y los indicadores de acceso. Si el archivo ya existe, la función tiene éxito si exist_ok es verdadero (y su hora de modificación se actualiza a la hora actual), de lo contrario, se genera FileExistsError.

voidnologo
fuente
3
En Python2.7:pip install pathlib
Andre Miras
8
note a self: utilícelo Path('/some/path').mkdir()si el directorio que contiene el archivo a touch()editar aún no existe.
JacobIRR
1
Creo que deberíamos usarlo en pathlib2lugar de pathlibporque pathlibes solo corrección de errores ahora. Por lo tanto, en Python 2.7: pip install pathlib2y luego from pathlib2 import Path.
Ian Lin
@IanLin Hay pocas razones para instalar una biblioteca para hacer algo que la biblioteca estándar ya admite. ¿Estás confundiendo bitbucket.org/pitrou/pathlib/src/default con docs.python.org/dev/library/pathlib.html ?
Michael Mrozek
Ese comentario responde al comentario de Andre sobre Python 2.7, que no tiene esa biblioteca estándar. Siéntase libre de leer el documento en pypi.org/project/pathlib2
Ian Lin
242

Esto intenta ser un poco más libre de carrera que las otras soluciones. (La withpalabra clave es nueva en Python 2.5.)

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

Aproximadamente equivalente a esto.

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

Ahora, para hacerlo realmente libre de carreras, debe usar futimesy cambiar la marca de tiempo del identificador de archivo abierto, en lugar de abrir el archivo y luego cambiar la marca de tiempo en el nombre de archivo (que puede haber cambiado de nombre). Desafortunadamente, Python no parece proporcionar una forma de llamar futimessin pasar ctypeso similar ...


EDITAR

Como señaló Nate Parsons , Python 3.3 agregará especificando un descriptor de archivo (cuándo os.supports_fd) a funciones tales como os.utime, que utilizará la futimesllamada al sistema en lugar de la utimesllamada al sistema bajo el capó. En otras palabras:

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)
ephemient
fuente
Esta es la solución real, y así es como touch (1) en coreutils lo hace, a menos que futimes () no esté disponible. futimes no es una función portátil y ni siquiera existe en los núcleos Linux 2.6 más antiguos, por lo que debe lidiar con ENOSYS y recurrir al tiempo, incluso si lo usa.
Glenn Maynard
(Error de revisión anterior: "This" = open ("a") + futimes.) Afortunadamente, es difícil pensar en un caso en el que la condición de la carrera de no usar futimes realmente importe. El caso "incorrecto" con el que podría terminar es el nombre del archivo entre open () y utime (), en cuyo caso no creará un archivo nuevo ni tocará el anterior. Eso puede importar, pero la mayoría de las veces no lo hará.
Glenn Maynard
cygwin touch puede hacer su magia en archivos de solo lectura, pero este código no puede. Sin embargo, parece funcionar si lo rodeo con try: <code> excepto IOError como e: (verifique e.errno) os.utime (nombre de archivo, horas)
dash-tom-bang
Para su información, parece que se agregaron futuros en 3.3
Nate Parsons
Nota: la filefunción incorporada se eliminó de Python 3 y opendebe usarse en su lugar. Extrañé totalmente esto porque el resaltado de sintaxis del editor que estoy usando (gedit) todavía está dirigido a Python 2.
Bart
42
def touch(fname):
    if os.path.exists(fname):
        os.utime(fname, None)
    else:
        open(fname, 'a').close()
SilentGhost
fuente
24
Hay una posible condición de carrera en esta solución: si el archivo no existe y otro proceso lo crea antes de que esta función llegue a la open()llamada, el contenido del archivo se truncará. Sugiera usar el modo en su 'a'lugar.
Greg Hewgill
77
Convenido. La solución adecuada es solo: def touch (fname): open (fname, 'wa').
Close
@Greg, aunque resuelve el problema potencial de las condiciones de carrera, open(fname, 'a').close()no cambiará en ningún momento.
SilentGhost
@SilentGhost: Eso es cierto, pero está bien porque si el archivo existe, se acaba de crear. Por supuesto, dejaría la llamada os.utime()allí para archivos preexistentes.
Greg Hewgill
44
¿Por qué no simplemente abrir para asegurarse de que existe y luego llamar a utime?
itsadok
31

¿Por qué no pruebas esto ?:

import os

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

Creo que esto elimina cualquier condición de carrera que importe. Si el archivo no existe, se generará una excepción.

La única condición de carrera posible aquí es si el archivo se crea antes de que se llame a open () pero después de os.utime (). Pero esto no importa porque en este caso el tiempo de modificación será el esperado, ya que debe haber sucedido durante la llamada a touch ().

jcoffland
fuente
8

Aquí hay un código que usa ctypes (solo probado en 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())
EU G
fuente
8

Esta respuesta es compatible con todas las versiones desde Python-2.5 cuando withse lanzó la palabra clave .

1. Crear archivo si no existe + Establecer hora actual
(exactamente igual que el 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 versión más 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. Simplemente cree el archivo si no existe
(no actualiza el tiempo)

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

3. Simplemente actualice el acceso al archivo / los tiempos modificados
(no crea el archivo si no existe)

import os

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

Usar os.path.exists()no simplifica el código:

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)

Bonificación: tiempo de actualización de todos los archivos en un directorio

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
fuente
4
with open(file_name,'a') as f: 
    pass
Matt
fuente
Error : with open(fn,'a'): passo alternativa open(fn, 'a').close(), no cambie la hora modificada utilizando Python 2.7.5 en Red Hat 7 (el sistema de archivos es XFS). En mi plataforma, estas soluciones solo crean un archivo vacío si no existe. : - /
olibre
3

Simplista:

def touch(fname):
    open(fname, 'a').close()
    os.utime(fname, None)
  • El openasegura que hay un archivo allí
  • el utimeasegura que las marcas de tiempo se actualizan

Teóricamente, es posible que alguien elimine el archivo después del archivo open, lo que provocará que el tiempo genere una excepción. Pero podría decirse que está bien, ya que sucedió algo malo.

itsadok
fuente
1

Complejo (posiblemente con errores):

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)

Esto intenta permitir también establecer el tiempo de acceso o modificación, como GNU touch.

itsadok
fuente
1

Puede parecer lógico crear una cadena con las variables deseadas y pasarla a os.system:

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

Esto es inadecuado de varias maneras (por ejemplo, no maneja espacios en blanco), así que no lo haga.

Un método más robusto es usar subprocesos:

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

Si bien esto es mucho mejor que usar un subshell (con sistema os.), todavía es adecuado solo para scripts rápidos y sucios; use la respuesta aceptada para programas multiplataforma.

belacqua
fuente
Esto no es muy seguro: ¿qué sucede cuando hay un espacio en el nombre del archivo?
ayke
55
subprocess.call(['touch', os.path.join(dirname, fileName)])es mucho mejor que usar un subshell (con os.system). Pero aún así, use esto solo para scripts rápidos y sucios, use la respuesta aceptada para programas multiplataforma.
ayke
1
touchno es un comando disponible multiplataforma (por ejemplo, Windows)
Mike T
1

"abrir (nombre_archivo, 'a'). cerrar ()" no funcionó para mí en Python 2.7 en Windows. "os.utime (nombre_archivo, Ninguno)" funcionó bien.

Además, tuve la necesidad de tocar recursivamente todos los archivos en un directorio con una fecha anterior a alguna fecha. Creé el seguimiento basado en la respuesta muy útil de 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
fuente
1

¿Por qué no lo intentas: 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

utilizar subproceso:

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

Lo siguiente es suficiente:

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

Si desea establecer una hora específica para el tacto, use os.utime de la siguiente manera:

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

Aquí, atime y mtime deben ser int / float y deben ser iguales al tiempo de la época en segundos al tiempo que desea establecer.

Amar chand Dargad
fuente
0

Si no te importa el intento, excepto entonces ...

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

Sin embargo, una cosa a tener en cuenta es que si existe un archivo con el mismo nombre, no funcionará y fallará en silencio.

Cavernícola
fuente
0

write_text()de pathlib.Pathpuede ser utilizado.

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