如何使用“查找”在两个日期之间查找文件?

Zuu*_*uul 38 command-line find

我有一个已通过 60GB 电子邮件的电子邮件帐户,目前我在使用电子邮件客户端归档去年(2011 年)的电子邮件时遇到了很多麻烦。

通过终端,我试图使用find来定位 2011-01-01 和 2011-12-31 之间的文件,但无济于事。

如何在两个日期之间查找文件?

如果相关,最终目标将是将找到的每个文件(与日期间隔匹配)移动到文件夹的批处理。

Eri*_*ski 62

Bash 在两个日期之间查找文件:

find . -type f -newermt 2010-10-07 ! -newermt 2014-10-08
Run Code Online (Sandbox Code Playgroud)

返回时间戳在 2010-10-07 之后和 2014-10-08 之前的文件列表

Bash 查找 15 分钟前到现在的文件:

find . -type f -mmin -15
Run Code Online (Sandbox Code Playgroud)

返回时间戳在 15 分钟之前但现在之前的文件列表。

Bash 在两个时间戳之间查找文件:

find . -type f -newermt "2014-10-08 10:17:00" ! -newermt "2014-10-08 10:53:00"
Run Code Online (Sandbox Code Playgroud)

返回时间戳介于2014-10-08 10:17:00和之间的文件2014-10-08 10:53:00


Oct*_*ves 22

你可以使用这个脚本:

#!/bin/bash
for i in $(find Your_Mail_Dir/ -newermt "2011-01-01" ! -newermt "2011-12-31"); do
  mv $i /moved_emails_dir/
done
Run Code Online (Sandbox Code Playgroud)

  • `find` 的输出不应该像这样在 shell 的 `for` 循环中处理,除非保证文件名中没有任何空格。`-exec`、`-execdir` 或 `-print0 | 通常应该使用 xargs`;另一个可能的解决方案,通常不太理想,但允许使用 `for` 循环,是临时设置 `IFS`,这样空格就不会被识别为字段分隔符。 (6认同)
  • @SherylHohman 不,不要使用`exec` 命令。如[我发布的答案](https://askubuntu.com/a/533891) 中所述,使用带有“-exec”动作的“find”命令来运行“mv”或任何您需要运行的程序。当`find`...`-exec` 使用它找到的路径名运行你的命令时,它不使用shell,所以空格不会触发[分词](https://www.gnu.org/software /bash/manual/bash.html#Word-Splitting) 或 [globbing](https://www.gnu.org/software/bash/manual/bash.html#Filename-Expansion)。(您可能想发布有关您的具体案例的新问题,或者确切地询问您想知道的内容。) (4认同)

Eli*_*gan 15

移动文件,并在有重复名称时提示用户:

正如Subv3rsionEric Leschinski 的回答所示,-newermt谓词选择比指定为其操作数的日期(和可选时间)最近修改的文件。查找文件

  • 任何地方srcdir(即,包括其子目录、它们的子目录等)
  • 上次修改时间(例如)2014 年 9 月
  • 并将它们移动destdir

...你可以运行:

find srcdir -type f -newermt 2014-08-31 ! -newermt 2014-09-30 -exec mv -i {} destdir/ \;
Run Code Online (Sandbox Code Playgroud)

-exec表达式中, find 传递找到的文件名代替{}. ;表示-exec要运行的命令及其参数都已提供(以防在特定-exec谓词的参数之后将后续表达式传递给 find - 有关此示例,请参见下文)。;必须被转义,\;所以它不会被外壳特别解释。(没有\,;将结束整个find命令,与换行符一样工作。即使此find命令在此-exec表达式之后没有任何内容,但未能传递;参数仍然是语法错误。)

如果您只想列出文件(如果您不确定旧电子邮件的存储方式或可能存在哪些其他文件,建议您这样做)- 省略-exec其右侧的所有内容。(对于电子邮件,通常来自不同日期的电子邮件存储在同一个文件中;对于这里问题中描述的情况的人,我建议在移动任何文件之前调查它们的存储方式。)如果您想打印他们的姓名并移动他们,-print-exec.

mv -i 文件将在目标处被覆盖的任何时候都会提示,例如在以下情况下会发生:

  • 先前备份中存在同名文件,
  • srcdir在同一find操作期间已移动同名但来自不同子目录的文件,
  • (最不可能)srcdir在同一find操作期间的某处创建了一个同名文件,在原始文件被移动后,但很快就会在find遍历不同的子目录时被找到。

其他调用方式mv

对于如何处理具有重复名称的文件,您还有其他选择。

  • 如果没有-i(即),通常不会提示批准,但如果目标文件是只读的,则会提示批准。(有时甚至可以成功覆盖只读文件,例如如果运行它的用户拥有该文件。)mv {} destdir/mvmv
  • 如果您甚至不想要那种程度的交互性,并且希望mv始终(尝试)覆盖同名文件,请使用mv -f.
  • 相反,如果您想在已有同名目标文件时跳过源文件,请使用mv -n.
  • mv接受-b--backup标志以自动重命名目标中已存在的同名文件。默认情况下~添加以生成备份名称,如果目的地已存在具有该名称的文件和具有备份名称的文件,则该备份文件将被覆盖。这个默认值可以被调用时传递的选项mv和环境变量覆盖。有关man mv详细信息,请参见下面的示例。

移动文件并在名称重复的情况下创建备份:

要移动所有文件,使用~后缀备份具有重复名称的文件,并在文件已经存在时使用编号后缀(以避免覆盖任何内容),请运行:.~n~.~

find srcdir -type f -newermt 2014-08-31 ! -newermt 2014-09-30 -exec mv --backup=existing {} destdir/ \;
Run Code Online (Sandbox Code Playgroud)

如果您跳过具有重复名称的文件并想知道哪些文件:

如果您使用mv -n并想知道哪些文件没有被移动,因为有另一个同名文件,最好的方法可能是find再次运行原始命令,不-exec包括它右侧的所有内容。这将打印他们的名字。
它还将打印自您运行原始find .... -exec ...命令以来创建的任何匹配文件的名称,但对于此应用程序,通常不会有任何名称,因为您正在寻找具有旧修改时间的文件。可以使用touch和其他机制为文件提供比其实际年龄更旧的修改时间戳,但在您不知情的情况下,这种情况似乎不太可能发生。

立即知道文件因重名而被跳过:

mv -n当它避免移动文件时,不报告,也不返回任何特殊的退出代码。因此,如果您想在find运行时立即获知跳过的文件,则必须为此单独执行一个步骤。一种方法是:

find srcdir -type f -newermt 2014-08-31 ! -newermt 2014-09-30 -exec mv -n {} destdir/ \; \
    -exec [ -f {} ] \; -exec printf "\`%s' skipped (exists in \`%s')\\n" {} destdir \; 
Run Code Online (Sandbox Code Playgroud)

一些可能的次要技术考虑:如果mv由于与目标位置不同的原因无法复制文件并退出报告成功,则会错误地发出警告。这似乎不太可能,但我不确定这是不可能的。它也可能会遇到竞争条件:如果在移动旧文件后和检查之前的很短的时间内在同一位置创建了同名的新文件,它会在根本没有真正的错误时发出警告看看它是否被删除了。(考虑到应用程序,我怀疑这两个问题是否真的会发生。)它可以被重写以检查目的地之前移动文件而不是之后:那么竞争条件将与新创建的目标文件而不是源文件相关。虽然findor mv(或[,尽管不应该有任何)报告的错误和警告将写入标准错误,但我们的...skipped (exists in...警告将写入标准输出。通常两者都出现在您的终端上,但如果您正在编写脚本,这可能很重要。

我已将该命令拆分为两行以便于阅读。它可以这样运行,也可以删除\和 换行符(即换行符)。

find命令如何工作?

find谓词可以是测试(如-typeand -newermt),用于返回值,或动作(如-printand -exec),通常用于其副作用。

当表达式之间没有提供运算符(如-afor-ofor)时,-a是隐含的。对andorfind使用短路评估。(即, ) 仅当pq表达式都为真时才为真,因此如果p为假,则不需要计算q。尽管我们通常不会以这些术语来考虑它,但这就是为什么要评估后续操作或测试的测试必须为真。例如,假设遇到一个目录。它的计算结果为 false,因此它之后可以跳过所有内容。p qp -a qfind-type f

与测试一样,动作也会评估为真或假。这样就-exec报告执行的命令是否退出报告成功(true)或失败(false)。我们有这个-exec与隐式连接的表达式链:

-exec mv -n {} destdir/ \; -exec [ -f {} ] \; -exec printf "\`%s' skipped (exists in \`%s')\\n" {} destdir \;
Run Code Online (Sandbox Code Playgroud)

这会尝试移动文件,如果mv报告失败,则停止。如果其他问题是文件未被移动的原因,我们不想就正确跳过的文件发出警告。

但是,如果它成功了,它然后运行[命令。像find,[支持它自己的作为参数传递的表达式。[ -f {} ]检查之后的操作数-f(由find代替传递给它{})是否存在(并且是常规文件),并返回真/成功或假/失败。
(许多命令的退出状态最好解释为表示成功或失败,但[的存在状态通常最好解释为 true 或 false。)

如果[返回 false,则该文件已消失,因此它已被移动,因此无需执行任何操作。但如果[返回 false,文件仍然存在。然后find计算下一个-exec表达式,打印警告消息。

进一步阅读