ติดตั้งระบบสัมผัสโดยใช้ Python หรือไม่


352

touchเป็นยูทิลิตี้ Unix ที่ตั้งค่าการแก้ไขและการเข้าถึงเวลาของไฟล์เป็นเวลาปัจจุบันของวัน หากไฟล์นั้นไม่มีอยู่ไฟล์นั้นจะถูกสร้างขึ้นด้วยการอนุญาตเริ่มต้น

คุณจะนำมันไปใช้เป็นฟังก์ชั่น Python ได้อย่างไร? พยายามที่จะข้ามแพลตฟอร์มและเสร็จสมบูรณ์

(ผลลัพธ์ปัจจุบันของ Google สำหรับ "python touch file" นั้นยอดเยี่ยม แต่ชี้ไปที่os.utime )


4
โปรดพิจารณาการปรับปรุงคำตอบที่ได้รับการยอมรับในขณะนี้ว่าฟังก์ชั่นนี้มีอยู่ใน Python stdlib
ไมล์ส

@Miles คำตอบที่ได้รับการยอมรับทำในสิ่งที่คำถามขอมาจริง ๆ แล้วมันใช้งานฟังก์ชันใน Python แทนที่จะใช้ไลบรารี
ไตโรโฟมบิน

5
@styrofoamfly ห้องสมุดมาตรฐานเป็นส่วนหนึ่งของ Python เป็นไปได้มากว่าสิ่งที่ผู้ถามคำถามต้องการรู้ (และคนส่วนใหญ่ที่มาถึงคำถามนี้ผ่านทาง Google) เป็นวิธีการtouchใช้งานฟังก์ชั่นเหมือนในโปรแกรม Python ของพวกเขาไม่ใช่วิธีการนำมาใช้ใหม่ตั้งแต่ต้น คนเหล่านั้นจะได้รับบริการที่ดีที่สุดโดยเลื่อนลงไปที่pathlibโซลูชัน แม้ว่าจะสร้างขึ้นในตอนนี้คำตอบนี้มีมากดีจัดอันดับของ Google สำหรับ "หลามไฟล์สัมผัส" กว่าเอกสารที่เกี่ยวข้อง
ไมล์

@miles Python 2 คือ (น่าเสียดาย) ยังใช้กันอย่างแพร่หลายมากกว่า 3 ดังนั้นฉันคิดว่าคำตอบที่ยอมรับยังคงเป็นคำตอบที่เกี่ยวข้องมากกว่า แต่ความคิดเห็นของคุณใช้การชี้ผู้คนไปที่คำตอบที่สองได้ดี
itsadok

6
Python 2 เป็น EOL ในปลายปีนี้
Max Gasner

คำตอบ:


304

ลักษณะเช่นนี้เป็นของใหม่เป็นของงูใหญ่ 3.4 pathlib-

from pathlib import Path

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

นี้จะสร้างfile.txtที่เส้นทาง

-

Path.touch (โหมด = 0o777, exist_ok = จริง)

สร้างไฟล์ที่เส้นทางที่กำหนดนี้ หากกำหนดโหมดไว้จะรวมกับค่า umask ของกระบวนการเพื่อกำหนดโหมดไฟล์และแฟล็กการเข้าถึง หากไฟล์มีอยู่แล้วฟังก์ชั่นจะประสบความสำเร็จถ้า exist_ok เป็นจริง (และเวลาในการแก้ไขถูกปรับปรุงเป็นเวลาปัจจุบัน) มิฉะนั้น FileExistsError จะเพิ่มขึ้น


3
ใน Python2.7:pip install pathlib
Andre Miras

8
หมายเหตุถึงตัวเอง: ใช้Path('/some/path').mkdir()หากไดเรกทอรีที่มีไฟล์ที่จะtouch()แก้ไขยังไม่มีอยู่
JacobIRR

1
ฉันคิดว่าเราควรใช้pathlib2แทนpathlibเพราะpathlibเป็นข้อผิดพลาดเท่านั้นตอนนี้ ดังนั้นในวันที่หลาม 2.7: แล้วpip install pathlib2 from pathlib2 import Path
Ian Lin

@IanLin มีเหตุผลเล็กน้อยที่จะติดตั้งไลบรารีเพื่อทำสิ่งที่ห้องสมุดมาตรฐานรองรับอยู่แล้ว คุณสับสนbitbucket.org/pitrou/pathlib/src/defaultด้วยdocs.python.org/dev/library/pathlib.htmlหรือไม่
Michael Mrozek

ความคิดเห็นนั้นเป็นการตอบกลับความคิดเห็นของ Andre ที่พูดถึง Python 2.7 ซึ่งไม่มีไลบรารี่มาตรฐานดังกล่าว อย่าลังเลที่จะอ่านเอกสาร pypi.org/project/pathlib2
Ian Lin

242

สิ่งนี้พยายามที่จะไร้คู่แข่งมากกว่าโซลูชันอื่น ๆ เล็กน้อย ( withคำหลักนั้นใหม่ใน Python 2.5)

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

เทียบเท่ากับสิ่งนี้

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

ตอนนี้เพื่อให้ปราศจากการแข่งขันคุณต้องใช้futimesและเปลี่ยนการประทับเวลาของ filehandle ที่เปิดแทนการเปิดไฟล์แล้วเปลี่ยนการประทับเวลาในชื่อไฟล์ (ซึ่งอาจถูกเปลี่ยนชื่อ) น่าเสียดายที่ Python ไม่ได้ให้วิธีการโทรfutimesโดยไม่ผ่านctypesหรือคล้ายกัน ...


แก้ไข

ตามที่ระบุไว้โดยNate Parsons , Python 3.3 จะเพิ่มการ ระบุไฟล์ descriptor (เมื่อos.supports_fd ) ไปยังฟังก์ชั่นเช่นos.utimeซึ่งจะใช้futimessyscall แทนutimessyscall ภายใต้ประทุน ในคำอื่น ๆ :

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)

นี่คือทางออกที่แท้จริง - และนี่คือวิธีที่ touch (1) ใน coreutils ทำยกเว้นว่า futimes () ไม่สามารถใช้ได้ Futimes ไม่ได้เป็นฟังก์ชั่นแบบพกพาและไม่มีแม้แต่ในเคอร์เนล 2.6 อันเก่าดังนั้นคุณต้องจัดการกับ ENOSYS และถอยกลับไปสู่ช่วงเวลาที่ดีแม้ว่าคุณจะใช้มัน
Glenn Maynard

(ข้อผิดพลาดในการพิสูจน์อักษรข้างต้น: "This" = open ("a") + futimes) โชคดีที่มันยากที่จะคิดถึงกรณีที่สภาพการแข่งขันที่ไม่ได้ใช้ฟิวเจอร์สนั้นสำคัญ กรณี "ผิด" ที่คุณอาจพบคือไฟล์ที่ถูกเปลี่ยนชื่อระหว่าง open () และ utime () ซึ่งในกรณีนี้คุณจะไม่ได้สร้างไฟล์ใหม่หรือสัมผัสไฟล์เก่า นั่นอาจเป็นเรื่องสำคัญ แต่ส่วนใหญ่แล้วจะไม่เกิดขึ้น
Glenn Maynard

cygwin touch สามารถใช้เวทมนตร์กับไฟล์แบบอ่านอย่างเดียวได้ แต่รหัสนี้ไม่สามารถทำได้ อย่างไรก็ตามดูเหมือนว่าจะทำงานถ้าฉันล้อมรอบด้วยลอง: <code> ยกเว้น IOError เป็น e: (ตรวจสอบ e.errno) os.utime (ชื่อไฟล์, เวลา)
dash-tom-bang

FYI ดูเหมือนว่าจะมีการเพิ่มฟิวเจอร์สใน 3.3
Nate Parsons

หมายเหตุ: fileฟังก์ชันในตัวถูกลบออกจาก Python 3 และopenจะต้องใช้แทน ฉันพลาดทั้งหมดนี้เนื่องจากการเน้นไวยากรณ์ของโปรแกรมแก้ไขที่ฉันใช้อยู่ (gedit) ยังคงเป็นเป้าหมายของ Python 2
Bart

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

24
มีสภาพการแย่งชิงที่เป็นไปได้ในการแก้ปัญหานี้: หากไฟล์ไม่มีอยู่และถูกสร้างโดยกระบวนการอื่นก่อนที่ฟังก์ชันนี้จะถึงการopen()โทรเนื้อหาของไฟล์จะถูกตัดทอน แนะนำให้ใช้โหมด'a'แทน
เกร็ก Hewgill

7
ตกลง คำตอบที่เหมาะสมคือ: def touch (fname): open (fname, 'wa'). close ()
stepancheg

@ Greg ในขณะที่แก้ปัญหาสภาพการแข่งรถที่อาจเกิดขึ้นopen(fname, 'a').close()จะไม่เปลี่ยนแปลงเลย
SilentGhost

@SilentGhost: จริง แต่ก็ไม่เป็นไรเพราะถ้าไฟล์มีอยู่มันก็แค่สร้าง แน่นอนว่าคุณต้องฝากสายos.utime()ไว้ที่นั่นสำหรับไฟล์ที่มีอยู่แล้ว
Greg Hewgill

4
ทำไมไม่เปิดเพียงเพื่อให้แน่ใจว่ามีอยู่แล้วโทร utime?
itsadok

31

ทำไมไม่ลองล่ะ:

import os

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

ฉันเชื่อว่าสิ่งนี้จะกำจัดสภาพการแข่งขันที่สำคัญ หากไฟล์ไม่มีอยู่จะมีข้อผิดพลาดเกิดขึ้น

เงื่อนไขการแข่งขันที่เป็นไปได้เฉพาะที่นี่คือถ้าไฟล์ถูกสร้างขึ้นก่อนที่จะเปิด () เรียกว่า แต่หลังจาก os.utime () แต่สิ่งนี้ไม่สำคัญเพราะในกรณีนี้เวลาการแก้ไขจะเป็นไปตามที่คาดไว้เนื่องจากต้องเกิดขึ้นระหว่างการโทรเพื่อสัมผัส ()


8

นี่คือโค้ดบางส่วนที่ใช้ ctypes (ทดสอบบน 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

คำตอบนี้เข้ากันได้กับทุกรุ่นตั้งแต่ Python-2.5 เมื่อคำสำคัญwithได้รับการเผยแพร่

1. สร้างไฟล์หากไม่มีอยู่ + ตั้งเวลาปัจจุบัน
(เหมือนกับคำสั่ง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

รุ่นที่แข็งแกร่งกว่านี้:

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. เพียงสร้างไฟล์หากไม่มีอยู่
(ไม่อัปเดตเวลา)

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

3. เพียงอัปเดตการเข้าถึงไฟล์ / แก้ไขครั้ง
(ไม่สร้างไฟล์หากไม่มีอยู่)

import os

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

การใช้os.path.exists()ไม่ทำให้โค้ดง่ายขึ้น:

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)

โบนัส:อัปเดตเวลาของไฟล์ทั้งหมดในไดเรกทอรี

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

ล้มเหลว : with open(fn,'a'): passหรือทางเลือกอื่นopen(fn, 'a').close()ไม่เปลี่ยนเวลาที่แก้ไขโดยใช้ Python 2.7.5 บน Red Hat 7 (ระบบไฟล์คือ XFS) บนแพลตฟอร์มของฉันโซลูชันเหล่านี้เพียงสร้างไฟล์ว่างเปล่าหากไม่มีอยู่ : - /
olibre

3

แบบง่ายๆ:

def touch(fname):
    open(fname, 'a').close()
    os.utime(fname, None)
  • openมั่นใจมีมีไฟล์
  • utimeเพื่อให้แน่ใจว่าการประทับเวลามีการปรับปรุง

ตามทฤษฎีแล้วเป็นไปได้ว่ามีใครบางคนจะลบไฟล์หลังจากopenนั้นทำให้เกิดข้อยกเว้น แต่เนื้อหาก็ไม่เป็นไรเนื่องจากมีบางสิ่งที่ไม่ดีเกิดขึ้น


1

คอมเพล็กซ์ (อาจเป็นบั๊กกี้):

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)

สิ่งนี้พยายามอนุญาตการตั้งค่าเวลาการเข้าถึงหรือการปรับเปลี่ยนเช่น GNU touch


1

มันอาจดูสมเหตุสมผลในการสร้างสตริงที่มีตัวแปรที่ต้องการแล้วส่งไปยัง os.system:

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

สิ่งนี้ไม่เพียงพอในหลายวิธี (เช่นมันไม่จัดการกับช่องว่าง) ดังนั้นอย่าทำ

วิธีการที่แข็งแกร่งยิ่งขึ้นคือการใช้กระบวนการย่อย:

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

แม้ว่าสิ่งนี้จะดีกว่าการใช้เชลล์ย่อย (ด้วย os.system) แต่ก็ยังคงเหมาะสำหรับสคริปต์ที่รวดเร็วและสกปรกเท่านั้น ใช้คำตอบที่ยอมรับสำหรับโปรแกรมข้ามแพลตฟอร์ม


สิ่งนี้ไม่ปลอดภัยมาก: จะเกิดอะไรขึ้นเมื่อมีที่ว่างในชื่อไฟล์
ayke

5
subprocess.call(['touch', os.path.join(dirname, fileName)])ดีกว่าการใช้ subshell (พร้อมos.system) แต่ยังคงใช้สิ่งนี้เฉพาะสคริปต์ที่รวดเร็วและสกปรกใช้คำตอบที่ยอมรับสำหรับโปรแกรมข้ามแพลตฟอร์ม
ayke

1
touchไม่ใช่คำสั่งข้ามแพลตฟอร์ม (เช่น Windows)
Mike T

1

"open (file_name, 'a'). close ()" ใช้งานไม่ได้กับฉันใน Python 2.7 บน Windows "os.utime (file_name, None)" ใช้ได้ดี

นอกจากนี้ฉันยังต้องการสัมผัสไฟล์ซ้ำทั้งหมดในไดเรกทอรีที่มีวันที่เก่ากว่าบางวัน ฉันสร้าง hte แล้วตามการตอบสนองที่เป็นประโยชน์ของ 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

ทำไมคุณไม่ลอง: newfile.py

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

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

python newfile.py foobar.txt

หรือ

ใช้กระบวนการย่อย:

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

0

ต่อไปนี้เพียงพอ:

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

หากคุณต้องการตั้งเวลาเฉพาะสำหรับการสัมผัสให้ใช้ os.utime ดังนี้:

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

Atime และ mtime ทั้งคู่ควรเป็น int / float และควรเท่ากับเวลายุคในหน่วยวินาทีกับเวลาที่คุณต้องการตั้งค่า


0

หากคุณไม่สนใจลองเลยยกเว้น ...

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

สิ่งหนึ่งที่ควรทราบถ้าไฟล์มีชื่อเดียวกันมันจะไม่ทำงานและจะล้มเหลวอย่างเงียบ ๆ


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.