如何从 Python 更改符号 link 的 atime 和 mtime?
How can I change atime and mtime of a symbolic link from Python?
我有一个 Python 2.7 程序,它必须创建一个具有过去修改日期的符号 link。我可以用 os.symlink()
创建 link,并且 os.utime()
声称可以设置文件的访问时间和修改时间,但是当我在新创建的 symlink,它改变了 symlink 指向的文件的 atime 和 mtime,而不是符号 link.
的 atime 和 mtime
从 Python 代码设置符号 link 的访问时间和修改时间的最佳方法是什么?
这是一个演示我在做什么的测试程序:
#!/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))
这是我看到的不当行为。 "After:" 时间在 "source" 而不是 "link",但相反的情况应该发生:
% ../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
随心所欲地改变 symlink 的 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
是我最好的选择,但我希望有更好的选择。
升级到 Python 3.6 并使用 follow_symlinks
选项。
os.utime(path2, (long_ago_posix, long_ago_posix), follow_symlinks = False)
因为@Barmar points out, Python 3's os.utime() has a parameter, follow_symlinks = False
, which gives the behaviour the questioner wants. Unfortunately, Python 2's os.utime()不允许这个参数。
Python 2 的另一种选择是呼叫 touch
command, using subprocess.call()
。这实际上也适用于 Python 3。但是,我只在 Mac 上测试过它。它可能适用于 Linux,它预装了类似的 touch
实用程序和类似的进程调用约定。它未在 Windows 上进行测试,除非您特意安装 touch
实用程序,否则可能无法在那里工作。
这里是问题的测试程序,重写以显示这三个选项。使用单个参数调用它,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
我有一个 Python 2.7 程序,它必须创建一个具有过去修改日期的符号 link。我可以用 os.symlink()
创建 link,并且 os.utime()
声称可以设置文件的访问时间和修改时间,但是当我在新创建的 symlink,它改变了 symlink 指向的文件的 atime 和 mtime,而不是符号 link.
从 Python 代码设置符号 link 的访问时间和修改时间的最佳方法是什么?
这是一个演示我在做什么的测试程序:
#!/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))
这是我看到的不当行为。 "After:" 时间在 "source" 而不是 "link",但相反的情况应该发生:
% ../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
随心所欲地改变 symlink 的 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
是我最好的选择,但我希望有更好的选择。
升级到 Python 3.6 并使用 follow_symlinks
选项。
os.utime(path2, (long_ago_posix, long_ago_posix), follow_symlinks = False)
因为@Barmar points out, Python 3's os.utime() has a parameter, follow_symlinks = False
, which gives the behaviour the questioner wants. Unfortunately, Python 2's os.utime()不允许这个参数。
Python 2 的另一种选择是呼叫 touch
command, using subprocess.call()
。这实际上也适用于 Python 3。但是,我只在 Mac 上测试过它。它可能适用于 Linux,它预装了类似的 touch
实用程序和类似的进程调用约定。它未在 Windows 上进行测试,除非您特意安装 touch
实用程序,否则可能无法在那里工作。
这里是问题的测试程序,重写以显示这三个选项。使用单个参数调用它,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