如何从Python更改符号链接的atime和mtime?

时间:2018-01-02 22:30:07

标签: python python-2.7 filesystems symlink

我有一个Python 2.7程序,它必须创建一个过去修改日期的符号链接。我可以使用os.symlink()创建链接,os.utime()声明设置文件的访问时间和修改时间,但是当我在新创建的符号链接上使用os.utime()时,它会更改atime和符号链接所指向的文件的mtime,而不是符号链接的atime和mtime。

从Python代码设置符号链接的访问时间和修改时间的最佳方法是什么?

这是一个测试程序,它演示了我在做什么:

#!/usr/bin/env python2.7
import os, datetime, time

if __name__ == '__main__':
    path1, path2 = 'source', 'link'
    if os.path.exists(path1):
        os.rmdir(path1)
    os.mkdir(path1)
    if os.path.lexists(path2):
        os.remove(path2)
    os.symlink(path1, 'link')

    lstat1, lstat2 = os.lstat(path1), os.lstat(path2)
    print("Before: {path1} atime {sa}, mtime {sm}, {path2} atime {la}, mtime {lm}".format(
        path1=path1, path2=path2, sa=lstat1.st_atime, sm=lstat1.st_mtime, 
        la=lstat2.st_atime, lm=lstat2.st_mtime))

    long_ago = datetime.datetime(datetime.date.today().year - 1,1,1,00,00,00)
    long_ago_posix = time.mktime(long_ago.timetuple())
    print("Desired: {path1} unchanged, {path2} atime {m}, mtime {m}".format(
        path1=path1, path2=path2, m=long_ago_posix))

    os.utime(path2, (long_ago_posix, long_ago_posix))

    lstat1, lstat2 = os.lstat(path1), os.lstat(path2)
    print("After: {path1} atime {sa}, mtime {sm}, {path2} atime {la}, mtime {lm}".format(
        path1=path1, path2=path2, sa=lstat1.st_atime, sm=lstat1.st_mtime, 
        la=lstat2.st_atime, lm=lstat2.st_mtime))

这是我看到的不当行为。 "之后:"时间变化为"来源"而不是"链接",但反过来应该发生:

% ../src/utime_symlink_test.py
Before: source atime 1514931280.0, mtime 1514931280.0, link atime 1514931280.0, mtime 1514931280.0
Desired: source unchanged, link atime 1483257600.0, mtime 1483257600.0
After: source atime 1483257600.0, mtime 1483257600.0, link atime 1514931280.0, mtime 1514931280.0
% ls -ldT source link
lrwxr-xr-x  1 myuser  staff   6  2 Jan 14:14:40 2018 link -> source
drwxr-xr-x  2 myuser  staff  68  1 Jan 00:00:00 2017 source

相比之下,touch -h会根据需要更改符号链接的atime和mtime。

% touch -h -t 201701010000 link
% ls -ldT source link          
lrwxr-xr-x  1 myuser  staff   6  1 Jan 00:00:00 2017 link -> source
drwxr-xr-x  2 myuser  staff  68  1 Jan 00:00:00 2017 source

也许从Python执行touch -h是我的最佳选择,但我希望能有更好的东西。

2 个答案:

答案 0 :(得分:2)

升级到Python 3.6并使用import pandas as pd from io import StringIO for_pd = StringIO() with open('jasper.txt') as jasper: print (jasper.readline(), file=for_pd) line = jasper.readline() complete_record = '' for line in jasper: line = ''.join(line.rstrip().replace(', ', ',').replace("'", '')) if line.startswith(','): complete_record += line.replace(',,', ',').replace(',', ' ') else: if complete_record: print (complete_record, file=for_pd) complete_record = line if complete_record: print (complete_record, file=for_pd) for_pd.seek(0) df = pd.read_csv(for_pd) print (df) 选项。

follow_symlinks

答案 1 :(得分:0)

正如@Barmar指出的那样,Python 3's os.utime()有一个参数follow_symlinks = False,它提供了提问者想要的行为。遗憾的是,Python 2's os.utime()不允许此参数。

Python 2的另一种选择是使用touch调用subprocess.call()命令。这实际上也适用于Python 3。但是,我只在Mac上测试过它。它可能适用于Linux,它预先安装了类似的touch实用程序和类似的进程调用约定。它没有在Windows上进行测试,除非您不介意安装touch实用程序,否则可能无法在Windows上运行。

这是问题的测试程序,重写以显示这三个选项。使用单个参数调用它,2.utime(失败),3.utime(成功,仅限Python 3)或2.touch(成功,仅限Mac或Linux)之一。默认值为2.utime

import os, datetime, time, sys, subprocess

if __name__ == '__main__':
    method = 'missing' if len(sys.argv) < 2 else sys.argv[1]
    path1, path2 = 'source', 'link'
    if os.path.exists(path1):
        os.rmdir(path1)
    os.mkdir(path1)
    if os.path.lexists(path2):
        os.remove(path2)
    os.symlink(path1, 'link')

    lstat1, lstat2 = os.lstat(path1), os.lstat(path2)
    print("Before: {path1} atime {sa}, mtime {sm}, {path2} atime {la}, mtime {lm}".format(
        path1=path1, path2=path2, sa=lstat1.st_atime, sm=lstat1.st_mtime, 
        la=lstat2.st_atime, lm=lstat2.st_mtime))

    long_ago = datetime.datetime(datetime.date.today().year - 1,1,1,00,00,00)
    long_ago_posix = time.mktime(long_ago.timetuple())
    print("Desired: {path1} unchanged, {path2} atime {m}, mtime {m}".format(
        path1=path1, path2=path2, m=long_ago_posix))

    if method in ['missing', '2.utime']: 
        # runs on Python 2 or 3, always follows symbolic links
        os.utime(path2, (long_ago_posix, long_ago_posix))
    elif method in ['2.touch']:
        # runs on Python 2 or 3, tested on Mac only, maybe works on Linux, probably not Windows
        invocation = ['touch', '-h', '-t', long_ago.strftime('%Y%m%d%H%M.%S'), path2]
        subprocess.call(invocation)
    elif method in ['3.utime']:
        # runs on Python 3 only, changes links instead of following them
        os.utime(path2, (long_ago_posix, long_ago_posix), follow_symlinks=False)
    else:
        print("Don't recognise option {0}. Try 2.utime, 2.touch, or 3.utime .".format(method))

    lstat1, lstat2 = os.lstat(path1), os.lstat(path2)
    print("After: {path1} atime {sa}, mtime {sm}, {path2} atime {la}, mtime {lm}".format(
        path1=path1, path2=path2, sa=lstat1.st_atime, sm=lstat1.st_mtime, 
        la=lstat2.st_atime, lm=lstat2.st_mtime))

以下是Python 3 os.utime()成功:

% python3 ../src/utime_symlink_test.py 3.utime
Before: source atime 1514961960.0, mtime 1514961960.0, link atime 1514961960.0, mtime 1514961960.0
Desired: source unchanged, link atime 1483257600.0, mtime 1483257600.0
After: source atime 1514961960.0, mtime 1514961960.0, link atime 1483257600.0, mtime 1483257600.0
% ls -ldT source link                                     
lrwxr-xr-x  1 myuser  staff   6  1 Jan 00:00:00 2017 link -> source
drwxr-xr-x  2 myuser  staff  68  2 Jan 22:46:00 2018 source

以下是在Python 2上成功进行的touch调用(仅在Mac上测试):

% python ../src/utime_symlink_test.py 2.touch 
Before: source atime 1514961838.0, mtime 1514961838.0, link atime 1514961838.0, mtime 1514961838.0
Desired: source unchanged, link atime 1483257600.0, mtime 1483257600.0
After: source atime 1514961838.0, mtime 1514961838.0, link atime 1483257600.0, mtime 1483257600.0
% ls -ldT source link
lrwxr-xr-x  1 myuser  staff   6  1 Jan 00:00:00 2017 link -> source
drwxr-xr-x  2 myuser  staff  68  2 Jan 22:43:58 2018 source