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选项…)
- 任何事情都不要使用ls输出。ls是一个交互查看目录元数据的工具。用代码解析ls输出的任何尝试都被破坏。地球仪更加简单和正确:for file in *.txt。读取解析LS
- @菲尔多,"我发现了大量的变化"-从这些变化中选择一个,并把它作为一个起点添加到问题中。删除某些内容通常比添加更简单。
- 大多数文件系统根本不存储文件的创建时间。在那些情况下,您需要非标准的工具来检索它。例如,在gnu find中尝试-newerBB。
- @Ranyalbegwein大多数文件系统?真的?ls -t不是标准工具吗?它是如何工作的?实际上,我不需要以某种标准方式检索时间数据——我只需要合理的时间排序文件名(因此,即使文件系统不同,只要比较一致,我也应该是好的?)
- 对。ls -t将按修改时间排序,最新的将首先出现。总之,解析ls是错误的。
- 为了解释@rany的观点,许多UNIX文件系统不存储创建时间,只存储最后一次修改时间。请参见,例如,Unix/Linix SE上的这个问题和答案。如果要创建文件,可以将创建时间放在文件名中,或将其保存到其他位置。
- 100%安全和可移植的方式(通过可移植,我不是指跨外壳,我是指跨安装了bash的系统)是使用排序算法,例如这个答案中给出的算法。
- @Phildo,这是一个标准工具,但它不是一个为编程使用而设计的标准工具。有一个建议,在posix标准ls中添加由nul定界的输出支持;当这种情况发生时,而不是以前,它将像描述的那样可靠地使用。
- 请参阅ie.stackoverflow.com/a/40663567/14122,以获得真正可靠的解决方案(在破坏排序顺序之前不限制文件名的最大数量;不限制哪些文件名可以出现)。
使用find与xargs组合传递具有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。
- 好吧,等等,我认为这是正确的方向-让我澄清一些事情,我会给你一个公认的答案-对于ls来说,这似乎只是-t:对于-lt,我只剩下$file中的所有文件元数据。另外,如果您将每个文件单独传递给ls,那么这实际上是对它们进行排序吗?或者只是"对一个列表进行排序",这样就可以按查找结果排序了?
- 是的,-l是用来诊断的。我删除了它,换了一个例子——它也可以回答你的第二个问题。
- 你为什么要在这里使用find?只过滤常规文件?顺便说一下,对于包含换行符(不能固定)、反斜杠(可以用-r选项固定到read或尾随空格(可以用IFS= 来固定到read命令)的文件名,您的命令是中断的;如果文件太多,它也是中断的(中断是由EDOCX1的用法引入的)。〔6〕/xargs。事实上,你的find/xargs组合(不是标准的)可以换成标准更高的find /path/to/dir -type f -exec ls -t {} +(而且效率也会更高)。
- @你能提供这个测试例子吗?我下面给出的答案也适用吗?
- 如果您不想过滤常规文件并重复使用(这似乎不在OP的要求中),那么ls -t /path/to/dir | while IFS= read -r file; do stuff_with"$file"; done。
- @这是我正在寻找的优雅的答案。结果我的答案是错误的,我删除了它。
- @brycedrew:对于包含换行符的文件名,只需尝试使用touch $'a
file
with
newlines'。对于包含尾随空格的文件名,touch 'file with trailing spaces '。对于包含反斜杠的文件名:touch 'filename_with_backslash
'。对于太多的文件名,只需创建一组文件(一组文件将取决于您的系统)。参见,例如,本页
- @我这样回答是因为它在精神上最接近OP的尝试。它既不是最有效、最便携的方法,也不是最正确的方法。但是,由于它与OP的原始尝试非常接近,所以它的形式是可以识别的。
- 由于引入find来改变行为(递归和过滤常规文件),所以没有那么接近。至少,修复你的read命令:IFS= read -r file。
- 这不是一个绝对的顺序——它只在ls -t的个别调用中进行排序。如果有足够的文件名使其拆分为单独的调用,则会破坏全局顺序。
- @查尔斯达菲是真的。
使用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 |
- 我似乎无法在我的发行版上复制它。
- 但是现在,如果有文件名带有换行符或glob字符,这就中断了。
- @T.J.任何事情都不要使用ls输出。ls是一个交互查看目录元数据的工具。用代码解析ls输出的任何尝试都被破坏。地球仪更加简单和正确:for file in *.txt。读取解析LS
- @Ranyalbegwein:你是对的,但这里的问题是排序WRT修改时间。这不能用地球仪来完成。如果不将某些时间戳序列化并使用一些非标准排序工具(例如,GNU sort和-z选项)或使用排序算法(例如,此答案中的算法),就无法安全地完成此操作。
- 对,这似乎只适用于Ubuntu,而不是Rhel6。(不要射杀信使)。这个备选方案(每个Bishop)适用于rhel6:ls-t_,而读x;do…完成