关于循环:使用bash,如何遍历目录中的所有文件,按创建日期排序,但某些文件名的名称中包含空格

Using bash, how can I iterate through all files in a directory, ordered by date created, but some filenames have spaces in their names

我第一次

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

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

我发现了大量的变化可以解决空间问题,但是在$file变量中留下了一些日期信息。

编辑:显示这样的变化:

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

问题在于,循环中的$file变量中的所有时间信息仍然存在。(而且,这不起作用,因为我正好在OSX上,它的find实用程序缺少-printf选项…)


使用findxargs组合传递具有nul字节分隔的文件名,并使用while读取循环以提高效率和节省空间:

1
2
3
4
5
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按时间排列。要颠倒排序顺序,用-tr替换-t。如果要按大小排序,请将-t替换为-s

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ 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或者文件名(如果在那里编码的话)。这基本上意味着用一个合适的命令来替换xargs -0 ls -t,该命令通过管道连接到sort,类似于:xargs -0 stat -c '%W' | sort -n


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

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

其工作原理如下:

  • find 格式打印输出。
  • sort对其进行了数值排序,因此,通过修改时间,用从新纪元以来的秒数表示。
  • IFS='' read -r -d ' ' mtime把空间中的所有内容都读入变量mtime中。
  • IFS='' read -r -d '' filename将nul之前的所有剩余内容读入变量filename中。

因为nul不能存在于文件名中(与换行相比,换行可以),所以不能被具有令人惊讶内容的名称丢弃。有关详细讨论,请参阅bashfaq 3。

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


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

1
2
3
4
5
6
7
IFS_backup=$IFS
IFS=$(echo -en"
\b"
)
for file in `ls -t dir` ; do
  #blah
done
IFS=$IFS_backup

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

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