如何更改目录中文件的修改日期以匹配名称顺序?

如何更改目录中文件的修改日期以匹配名称顺序?

这是一个自我回答的问题,其他人也可能会觉得有用。

在 Dolphin 中,我有时会在按名称排序和按修改日期排序之间切换。然而,这可能很烦人,因为 Dolphin 不会仅更改我所在文件夹的顺序。它会更改所有内容。所以我想知道如何修复修改日期以匹配名称顺序,以便在 Dolphin 中的名称和日期之间切换时特定目录不受影响。

答案1

事实证明这非常简单。

for FILE in *; do touch -- "$FILE"; sleep 0.001; done # sort

如果你想递归地进行:

for FILE in **/*; do touch -- "$FILE"; sleep 0.001; done # sort

# sort只是一条评论。目的是为了方便向后搜索命令。

答案2

你的方法(特别是添加sleep 0.001通话之前的通话)仅适用于时间戳具有亚秒精度的系统和文件系统,即使如此,在某些系统和文件系统上,例如具有 ext4 文件系统的 Linux,即使时间戳具有纳秒分辨率,您也会发现某些批次的文件将具有相同的精度时间戳(参见linux:触摸日期精度为什么 Linux 中的文件系统时间总是比系统时间晚一些毫秒?了解详情)。

在此之后:

for f ({1..20}) touch $f
$ ls --full-time -lrt
total 0
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.183520078 +0100 2
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.183520078 +0100 1
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.187520223 +0100 3
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.191520368 +0100 6
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.191520368 +0100 5
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.191520368 +0100 4
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.195520512 +0100 8
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.195520512 +0100 7
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.199520657 +0100 9
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.199520657 +0100 10
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.203520802 +0100 13
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.203520802 +0100 12
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.203520802 +0100 11
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.207520947 +0100 15
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.207520947 +0100 14
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.211521092 +0100 17
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.211521092 +0100 16
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.215521237 +0100 19
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.215521237 +0100 18
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.219521381 +0100 20

(甚至 a 也sleep 0.001可能无济于事,touch在我的系统上运行已经花费了超过 1 毫秒,正如您从上面的时间戳中可以看出的那样)。

更好touch -d '0.000000001 second ago'(假设 GNU touch):

$ strace -qqe /utime touch -d now a
utimensat(0, NULL, NULL, 0)             = 0
$ strace -qqe /utime touch -d '0.000000001 second ago' a
utimensat(0, NULL, [{tv_sec=1687765504, tv_nsec=213757695} /* 2023-06-26T08:45:04.213757695+0100 */, {tv_sec=1687765504, tv_nsec=213757695} /* 2023-06-26T08:45:04.213757695+0100 */], 0) = 0

其中touch请求一个显式时间戳,它使用向系统发出的全精度时钟时间请求来计算,而不是让系统选择当前缓存的 fs 时间,因此您几乎可以保证两个后续调用将获得不同的时间戳,但这会在时间戳不支持亚秒精度的地方仍然不起作用。

如果使用 zsh (并且与 GNUtouch或兼容),您可以这样做:

zmodload zsh/datetime
now=$EPOCHSECONDS
for file ( *(NOn) ) touch -d @$((now--)) -- $file

或者使用标准touch

zmodload zsh/datetime
now=$EPOCHSECONDS
for file ( *(NOn) ) {
  TZ=UTC0 strftime -s t %Y%m%d%H%M.%S $((now--)) &&
    TZ=UTC0 touch -t $t -- $file
}

哪里$EPOCHSECONDS是当前的 Unix 纪元时间,*(NOn)类似于*N启用了 ULLGLOB 并且文件Oname 反向排序(大写 O 反向),因此您得到与 中相同的顺序ls -r。然后我们将时间戳设置为相隔一秒,回溯到过去。

答案3

正如其他人已经说过的那样ls,并且(echo) *排序不同。

如果你想像ls( 或者ls -[some option], ie排序) 排序ls -r,你可以简单地使用它而不是*.

另请注意,“*”本质上是不安全的,因为它扩展为所有文件名的列表,这些文件名可以(在大型目录中)使生成的命令行超过行长度限制。 shell(取决于 shell、版本……)将响应“命令行太长”之类的错误。

最后,顺便说一句:不将 shell 变量命名为大写是一个安全的习惯。系统变量(即“PATH”、“SHELL”、“OPTARG”等)通常都是大写,如果您以相同的方式命名自己的变量,则可能会面临(取消)设置此类变量的风险。在您自己的脚本中使用混合(或小写)大小写,这样您就安全了。

ls |\
while read fFile ; do
     touch fFile
done

相关内容