如何遍历目录中的所有文件,按创建日期排序,有些文件名的名称中有空格

How to iterate through all files in a directory, ordered by date created, with some filenames have spaces in their names

首先我有

for file in `ls -t dir` ; do
  #blah
done

但是带空格的文件被分成两个迭代。

我发现了很多解决空格问题的变体,但随后在 $file 变量中留下了一些日期信息。

编辑:展示一个这样的变体:

for file in `find . -printf "%T@ %Tc %p\n" | sort -n` ; do
  #blah
done

这个问题是所有时间信息仍然在循环中的 $file 变量中。 (此外,这不起作用,因为我碰巧在 OSX,其 find 实用程序缺少 -printf 选项...)

结合使用findxargs传递文件名以NUL字节分隔,并使用while读取循环以提高效率和space保存:

find /path/to/dir -type f -print0 | xargs -0 ls -t | while read file
do
    ls "$file" # or whatever you want with $file, which may have spaces
               # so always enclose it in double quotes
done

find 生成文件列表,ls 在这种情况下按时间排列它们。要反转排序顺序,请将 -t 替换为 -tr。如果您想按大小排序,请将 -t 替换为 -s

示例:

$ touch -d '2015-06-17' 'foo foo'
$ touch -d '2016-02-12' 'bar bar'
$ touch -d '2016-05-01' 'baz baz'
$ ls -1
bar bar
baz baz
foo foo
$ find . -type f -print0 | xargs -0 ls -t | while read file
> do
> ls -l "$file"
> done
-rw-rw-r-- 1 bishop bishop 0 May  1 00:00 ./baz baz
-rw-rw-r-- 1 bishop bishop 0 Feb 12 00:00 ./bar bar
-rw-rw-r-- 1 bishop bishop 0 Jun 17  2015 ./foo foo

为了完整起见,我将突出问题评论中的一点:-t 是按修改时间排序,严格来说不是创建时间。这些文件所在的文件系统决定了创建时间是否可用。由于您最初的尝试使用 -t,我认为修改时间是您所关心的,即使它不是迂腐的真实。

如果你想要创建时间,你必须从一些来源中提取它,比如 stat 或者文件名,如果它在那里编码的话。这基本上意味着用管道传送到 sort 的合适命令替换 xargs -0 ls -t,例如:xargs -0 stat -c '%W' | sort -n

您可以临时设置 IFS 变量以避免空格问题(感谢 http://www.linuxjournal.com/article/10954?page=0,1

IFS_backup=$IFS
IFS=$(echo -en "\n\b")
for file in `ls -t dir` ; do
  #blah
done
IFS=$IFS_backup

编辑:这适用于 Ubuntu,但不适用于 RHEL6。 bishop 建议的替代方案似乎更便携,例如:

ls -t dir|while read file; do ...; done

使用 GNU find 和 GNU sort,可以执行以下操作:

while IFS='' read -r -d ' ' mtime && IFS='' read -r -d '' filename; do
  printf 'Processing file %q with timestamp of %s\n' "$filename" "$mtime"
done < <(find "$dir" -type f -printf '%T@ %p[=10=]' | sort -znr)

其工作原理如下:

  • find<seconds-since-epoch> <filename><NUL>.
  • 格式打印其输出
  • sort 对其进行数字排序——因此,按修改时间,以自纪元以​​来的秒数表示。
  • IFS='' read -r -d ' ' mtime 将 space 之前的所有内容读入变量 mtime.
  • IFS='' read -r -d '' filename 将直到 NUL 的所有剩余内容读入变量 filename

因为 NUL 不能存在于文件名中(与换行符相比,可以),这不能被内容令人惊讶的名称所忽略。有关详细讨论,请参阅 BashFAQ #3

此外,因为它不依赖于将名称作为命令行参数传递给 ls -t(与所有其他外部命令一样,它在每次调用时只能接受有限数量的命令行参数),这种方法不受它可以可靠排序的文件数量的限制。 (当正在处理的文件名数量大于可以传递给单个 ls 调用的数量时,使用 find ... -exec ls -t {} +... | xargs ls -t 将导致不正确的结果。