Implementar touch usando Python?


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

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

(Los resultados actuales de Google para "python touch file" no son tan buenos, pero apuntan a os.utime .)

Author: itsadok, 2009-07-21

11 answers

Esto intenta ser un poco más libre de raza que las otras soluciones. (La palabra clave with 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 que realmente sea libre de carreras, debe usar futimes y cambie la marca de tiempo del gestor de archivos abierto, en lugar de abrir el archivo y luego cambiar la marca de tiempo en el nombre del archivo (que puede haber sido renombrado). Desafortunadamente, Python no parece proporcionar una forma de llamar a futimes sin pasar por ctypes o similar...


EDITAR

Como señaló Nathan Parsons, Python 3.3 se añadir la especificación de un descriptor de archivo (cuando os.supports_fd) funciones tales como os.utime, que va a utilizar la futimes syscall en lugar de utimes syscall 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)
 218
Author: ephemient,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:33:26

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.txt en la ruta.

--

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

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

 128
Author: voidnologo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-22 16:16:42
def touch(fname):
    if os.path.exists(fname):
        os.utime(fname, None)
    else:
        open(fname, 'a').close()
 34
Author: SilentGhost,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-21 14:01:28

¿Por qué no probar 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 raza que importe. Si el archivo no existe, se lanzará 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 del sistema operativo.utime (). Pero esto no importa porque en este caso el tiempo de modificación será el esperado ya que debe haber ocurrido durante la llamada a touch ().

 25
Author: jcoffland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-18 16:16:55

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())
 8
Author: eug,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-14 07:44:18

Simplista:

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

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

 3
Author: itsadok,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-21 14:08:59

Esta respuesta es compatible con todas las versiones desde Python-2.5 cuando palabra clave with ha sido liberado.

1. Crear archivo si no existe + Establecer hora actual
(exactamente igual que command 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 crea el archivo si no existe
(no actualiza la hora)

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 existentes)

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)

Bonus: 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)
 3
Author: olibre,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-11 12:41:41
with open(file_name,'a') as f: 
    pass
 2
Author: Matt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-07 16:28:21

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 también intenta permitir establecer el tiempo de acceso o modificación, como GNU touch.

 1
Author: itsadok,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-21 14:10:20

Puede parecer lógico crear una cadena con las variables deseadas y pasarla al sistema operativo.sistema:

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

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

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

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

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

 1
Author: belacqua,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-11 19:39:43

" open(file_name, 'a').close()" no funcionó para mí en Python 2.7 en Windows. "operativo.utime (file_name, None)" funcionó muy bien.

Además, tuve la necesidad de tocar recursivamente todos los archivos en un directorio con una fecha anterior a alguna fecha. Creé hte siguiente 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)
 1
Author: cadvena,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-22 21:19:35