Implementar toque usando Python?

352

touché um utilitário Unix que define os horários de modificação e acesso dos arquivos para a hora atual do dia. Se o arquivo não existir, ele será criado com permissões padrão.

Como você o implementaria como uma função Python? Tente ser multiplataforma e completo.

(Os resultados atuais do Google para "python touch file" não são tão bons assim, mas apontam para os.utime .)

itsadok
fonte
4
Por favor, considere atualizar a resposta aceita agora que essa funcionalidade está incorporada no stdlib do Python.
Miles
@Miles A resposta aceita faz exatamente o que a pergunta foi feita - ela realmente implementou a função em Python em vez de usar uma biblioteca.
isyrofo fly
5
@styrofoamfly A biblioteca padrão faz parte do Python. É realmente provável que o que o perguntador realmente deseja saber (e a maioria das pessoas que chega a essa pergunta pelo Google) é como obter touchfuncionalidades semelhantes em seus programas Python, não como reimplementá-las do zero; essas pessoas são melhor atendidas rolando para baixo até a pathlibsolução. Embora agora esteja integrada, esta resposta tem uma classificação do Google muito melhor para "arquivo de toque python" do que a documentação relevante .
Miles
O @miles Python 2 (infelizmente) ainda é mais amplamente usado que o 3, então acho que a resposta aceita ainda é a mais relevante. Mas seu comentário faz um bom trabalho em apontar as pessoas para a segunda resposta.
Itsadok
6
Python 2 é EOL no final deste ano.
Max Gasner

Respostas:

304

Parece que isso é novo a partir do Python 3.4 - pathlib.

from pathlib import Path

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

Isso criará um file.txtno caminho.

-

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

Crie um arquivo nesse caminho especificado. Se o modo for fornecido, ele será combinado com o valor umask do processo para determinar o modo do arquivo e os sinalizadores de acesso. Se o arquivo já existir, a função terá êxito se existir_ok for verdadeiro (e seu horário de modificação for atualizado para o horário atual), caso contrário, FileExistsError será gerado.

voidnologo
fonte
3
No Python2.7:pip install pathlib
Andre Miras 24/10
8
note to self: use Path('/some/path').mkdir()se o diretório que contém o arquivo a ser touch()editado ainda não existir.
JacobIRR
11
Eu acho que devemos usar em pathlib2vez de pathlibporque pathlibé apenas correção de bugs agora. Portanto, no Python 2.7: pip install pathlib2e depois from pathlib2 import Path.
23618 Ian Ian
@IanLin Há poucas razões para instalar uma biblioteca para fazer algo que a biblioteca padrão já suporta. Você está confundindo bitbucket.org/pitrou/pathlib/src/default com docs.python.org/dev/library/pathlib.html ?
Michael Mrozek
Esse comentário está respondendo ao comentário de Andre falando sobre o Python 2.7, que não possui essa biblioteca padrão. Sinta-se livre para ler o documento em pypi.org/project/pathlib2
Ian Lin
242

Isso tenta ser um pouco mais livre de corrida do que as outras soluções. (A withpalavra-chave é nova no Python 2.5.)

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

Aproximadamente equivalente a isso.

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

Agora, para realmente torná-lo livre de corrida, você precisa usar futimese alterar o carimbo de data / hora do identificador de arquivo aberto, em vez de abrir o arquivo e depois alterar o carimbo de data / hora no nome do arquivo (que pode ter sido renomeado). Infelizmente, o Python não parece fornecer uma maneira de ligar futimessem passar por algo ctypessemelhante ...


EDITAR

Conforme observado por Nate Parsons , o Python 3.3 adicionará a especificação de um descritor de arquivo (quando os.supports_fd) a funções como os.utime, que usará o futimessyscall em vez do utimessyscall sob o capô. Em outras palavras:

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)
efêmero
fonte
Essa é a solução real - e é assim que o toque (1) no coreutils faz isso, a menos que o futimes () não esteja disponível. futimes não é uma função portátil e nem existe em kernels 2.6 mais antigos do Linux, então você precisa lidar com o ENOSYS e voltar ao utime, mesmo que o utilize.
Glenn Maynard
(Erro de revisão acima: "This" = open ("a") + futimes.) Felizmente, é difícil pensar em um caso em que a condição de corrida de não usar futimes realmente importe. O caso "errado" com o qual você pode terminar é o arquivo sendo renomeado entre open () e utime (); nesse caso, você não criará um novo arquivo nem tocará no antigo. Isso pode importar, mas na maioria das vezes não.
Glenn Maynard
O cygwin touch pode fazer sua mágica em arquivos somente leitura, mas esse código não. No entanto, parece que o trabalho se eu coloque-a tentativa: <code> exceto IOError como e: (e.errno cheque) os.utime (filename, vezes)
traço-tom-bang
Para sua informação, parece que o futimes foi adicionado em 3.3
Nate Parsons
Nota: a filefunção interna foi removida do Python 3 e opendeve ser usada. Eu perdi totalmente isto porque o destaque de sintaxe do editor Eu estou usando (gedit) ainda é alvo Python 2.
Bart
42
def touch(fname):
    if os.path.exists(fname):
        os.utime(fname, None)
    else:
        open(fname, 'a').close()
SilentGhost
fonte
24
Existe uma condição de corrida em potencial nesta solução: se o arquivo não existir e for criado por outro processo antes que essa função atinja a open()chamada, o conteúdo do arquivo será truncado. Sugira usar o modo 'a'.
Greg Hewgill 21/07/2009
7
Acordado. Solução adequada é apenas: toque def (fname): open (fname, 'wa') close ().
stepancheg
O @Greg, embora resolva o possível problema das condições de corrida, open(fname, 'a').close()não mudará no momento.
SilentGhost 21/07/2009
@SilentGhost: Isso é verdade, mas isso é bom porque se o arquivo existe, então ele foi apenas criado. É claro que você deixaria a ligação os.utime()lá para arquivos pré-existentes.
21711 Greg Hewgill
4
Por que não abrir apenas para garantir que exista e ligar para utime?
itsadok 21/07/2009
31

Por que não tentar isso ?:

import os

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

Eu acredito que isso elimina qualquer condição de corrida que importa. Se o arquivo não existir, uma exceção será lançada.

A única condição de corrida possível aqui é se o arquivo for criado antes que open () seja chamado, mas após os.utime (). Mas isso não importa, pois, neste caso, o tempo de modificação será o esperado, pois deve ter ocorrido durante a chamada para tocar em ().

jcoffland
fonte
8

Aqui está um código que usa ctypes (testado apenas no 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
fonte
8

Esta resposta é compatível com todas as versões desde o Python-2.5 quando a palavra with- chave foi lançada.

1. Crie um arquivo se ele não existir + Defina a hora atual
(exatamente o mesmo que o 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

Uma versão mais 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 criar o arquivo se ele não existir
(não atualiza a hora)

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

3. Basta atualizar o acesso ao arquivo / horários modificados
(não cria arquivo, se não existir)

import os

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

O uso os.path.exists()não simplifica o 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)

Bônus: hora da atualização de todos os arquivos em um diretório

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)
rots olibre
fonte
4
with open(file_name,'a') as f: 
    pass
Matt
fonte
Falha : with open(fn,'a'): passou alternativa open(fn, 'a').close(), não altere o horário modificado usando o Python 2.7.5 no Red Hat 7 (o sistema de arquivos é XFS). Na minha plataforma, essas soluções apenas criam um arquivo vazio, se não existir. : - /
olibre 6/06/17
3

Simplista:

def touch(fname):
    open(fname, 'a').close()
    os.utime(fname, None)
  • O opengarante que haja um arquivo lá
  • os utimegarante que o timestamps são atualizados

Teoricamente, é possível que alguém exclua o arquivo após o open, fazendo com que o utime gere uma exceção. Mas sem dúvida está tudo bem, já que algo ruim aconteceu.

itsadok
fonte
1

Complexo (possivelmente com 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)

Isso tenta também permitir definir o horário de acesso ou modificação, como o GNU touch.

itsadok
fonte
1

Pode parecer lógico criar uma string com as variáveis ​​desejadas e passá-la para os.system:

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

Isso é inadequado de várias maneiras (por exemplo, ele não lida com espaços em branco), portanto, não faça isso.

Um método mais robusto é usar o subprocesso:

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

Embora isso seja muito melhor do que usar um subshell (com os.system), ele ainda é adequado apenas para scripts rápidos e sujos; use a resposta aceita para programas de plataforma cruzada.

belacqua
fonte
Isso não é muito seguro: o que acontece quando há um espaço no nome do arquivo?
Ayke
5
subprocess.call(['touch', os.path.join(dirname, fileName)])é muito melhor do que usar um subshell (com os.system). Ainda assim, use isso apenas para scripts rápidos e sujos, use a resposta aceita para programas de plataforma cruzada.
Ayke
11
touchnão é um cross-platform comando disponível (por exemplo, Windows)
Mike T
1

"open (file_name, 'a'). close ()" não funcionou para mim no Python 2.7 no Windows. "os.utime (nome_do_arquivo, Nenhum)" funcionou bem.

Além disso, eu precisava tocar recursivamente todos os arquivos em um diretório com uma data anterior a outra. Criei o seguinte com base na resposta muito útil do 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
fonte
1

Por que você não tenta: newfile.py

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

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

python newfile.py foobar.txt

ou

use subprocesso:

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

O seguinte é suficiente:

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

Se você deseja definir um horário específico para o toque, use os.utime da seguinte maneira:

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

Aqui, atime e mtime devem ser int / float e devem ser iguais ao tempo da época, em segundos, ao tempo que você deseja definir.

Amar chand Dargad
fonte
0

Se você não se importa com a tentativa, exceto ...

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

Porém, se um arquivo existir com o mesmo nome, ele não funcionará e falhará silenciosamente.

homem das cavernas
fonte
0

write_text()de pathlib.Pathpode ser usado.

>>> from pathlib import Path
>>> Path('aa.txt').write_text("")
0
Super Nova
fonte