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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37793886/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 14:46:14  来源:igfitidea点击:

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

bashloopsunixfilenames

提问by Phildo

First I had

首先我有

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

but files with spaces are split into two iterations.

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

I've found tons of variations on this that fix the spaces issue, but then leaves some date info in the $filevariable.

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

Edit: to show one such variation:

编辑:显示一个这样的变化:

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

The problem with this is that all the time info is still in-place within the $filevariable in the loop. (also, this doesn't work because I happen to be on OSX, whose findutility lacks the -printfoption...)

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

采纳答案by bishop

Use findin combination with xargsto pass file names with NUL-byte separation, and use a whileread loop for efficiency and space preservation:

使用find与组合xargs通过与NUL字节分离文件名,并使用while效率和空间保存读取循环:

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

findgenerates the list of files, lsarranges them, by time in this case. To reverse the sort order, replace -twith -tr. If you wanted to sort by size, replace -twith -s.

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

Example:

例子:

$ 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

For completeness, I'll highlight a point from comments to the question: -tis sorting by modification time, which not strictly creation time. The file system on which these files reside dictates whether or not creation time is available. Since your initial attempts used -t, I figured modification time was what you were concerned about, even if it's not pedantically true.

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

If you want creation time, you'll have to pull it from some source, like stator the file name if its encoded there. This basically means replacing the xargs -0 ls -twith a suitable command piped to sort, something like: xargs -0 stat -c '%W' | sort -n

如果您想要创建时间,则必须从某个来源中提取它,例如stat或文件名(如果在那里编码)。这基本上意味着用xargs -0 ls -t一个合适的命令来替换sort,例如:xargs -0 stat -c '%W' | sort -n

回答by Charles Duffy

Using GNU findand GNU sort, one can do the following:

使用 GNUfind和 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
IFS_backup=$IFS
IFS=$(echo -en "\n\b")
for file in `ls -t dir` ; do
  #blah
done
IFS=$IFS_backup
' | sort -znr)

This works as follows:

其工作原理如下:

  • findprints its output in the format <seconds-since-epoch> <filename><NUL>.
  • sortsorts that numerically -- thus, by modification time, expressed in seconds since epoch.
  • IFS='' read -r -d ' ' mtimereads everything up to the space into the variable mtime.
  • IFS='' read -r -d '' filenamereads all remaining content up to the NUL into the variable filename
  • find以格式打印其输出<seconds-since-epoch> <filename><NUL>
  • sort以数字方式对其进行排序 - 因此,通过修改时间,以自纪元以​​来的秒数表示。
  • IFS='' read -r -d ' ' mtime将直到空间的所有内容读入变量mtime
  • IFS='' read -r -d '' filename将直到 NUL 的所有剩余内容读取到变量中 filename

Because NUL cannot exist in filenames (as compared to newlines, which can), this can't be thrown off by names with surprising contents. See BashFAQ #3for a detailed discussion.

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

Moreover, because it doesn't depend on passing names as command-line arguments to ls -t(which, like all other external commands, can only accept a limited number of command-line arguments on each invocation), this approach is not limited in the number of files it can reliably sort. (Using find ... -exec ls -t {} +or ... | xargs ls -twill result in silently incorrect results when the number of filenames being processed grows larger than the number that can be passed to a single lsinvocation).

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

回答by teadotjay

You can temporarily set your IFS variable to avoid the problem with spaces (thanks to http://www.linuxjournal.com/article/10954?page=0,1)

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

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

Edit: this worked on Ubuntu, but not RHEL6. The alternative suggested by bishop appears to be more portable, for example:

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

##代码##