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

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

通过终端,我试图使用find在2011-01-01和2011-12-31之间找到文件,但无济于事。

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

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

您可以使用此脚本:

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

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

 find . -type f -newermt 2010-10-07 ! -newermt 2014-10-08 

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

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

 find . -type f -mmin -15 

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

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

 find . -type f -newermt "2014-10-08 10:17:00" ! -newermt "2014-10-08 10:53:00" 

返回时间戳在2014-10-08 10:17:002014-10-08 10:53:00之间的文件

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

正如Subv3rsion和Eric Leschinski的答案所示, -newermt谓词选择的文件比指定为其操作数的日期(和可选时间)更新。 要查找文件

  • srcdir任何位置(即,包括其子目录,子目录等)
  • 最后修改于(例如)2014年9月
  • 并将它们移动destdir

……你可以跑:

 find srcdir -type f -newermt 2014-08-31 ! -newermt 2014-09-30 -exec mv -i {} destdir / \; 

-exec表达式中,find传递代替{}的文件名。 ; 表示-exec已经提供了要运行的命令及其参数(如果后续表达式被传递给在特定的-exec谓词的参数之后查找 – 请参阅下面的示例)。 ; 必须被转义为\; 所以它不是由shell专门解释的。 (没有\ ;将结束整个find命令,与新行一样工作。即使此find命令在此-exec表达式之后没有任何内容,但未通过;参数仍然是语法错误。)

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

mv -i会随时提示在目的地覆盖文件,例如:

  • 从以前的备份中存在同名文件,
  • 在同一个find操作期间,已经移动了同名但来自srcdir的不同子目录的文件, 或者
  • (最不可能)在同一个find操作期间,在srcdir中的某个地方创建了一个同名文件,在原始文件被移动之后,但很快就会find遍历不同子目录的文件。

调用rm其他方法:

您还可以使用其他选项来处理具有重复名称的文件。

  • 如果没有-i (即mv {} destdir / ), mv通常不会提示批准,但如果目标文件是只读的,则会这样做。 ( mv甚至可以成功覆盖只读文件,例如运行它的用户拥有该文件。)
  • 如果您不想要那种程度的交互性,并希望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 / \; 

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

如果您使用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 \; 

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

我将该命令分成两行以便于阅读。 它可以以这种方式运行,或者你可以删除\和换行符(即换行符)。

如何find命令?

find谓词可以是测试 (如-type-newermt ),用于返回值,或者是操作 (如-print-exec ),它们通常用于副作用。

当表达式之间没有提供运算符(例如-a for -o for )时,暗示了-afind 和的使用短路评估 。 p q (即p -a q )仅在pq表达式都为真时才为真,因此如果p为假,则不需要求q 。 虽然我们通常不会在这些术语中考虑它,但这就是为什么测试必须适用于后续行动或要评估的测试。 例如,假设find出现在目录中。 它将-type f为false,因此它可以在之后跳过所有内容。

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

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

这会尝试移动文件,如果mv报告失败,则停止。 如果其他问题是为什么它没有被移动,我们不想警告正确跳过的文件。

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

如果[返回false,那么文件就消失了,所以它被移动了,所以没有必要做任何事情。 但如果[返回false,文件仍然存在。 然后find计算下一个-exec表达式,它会打印警告消息。

进一步阅读

  • man find和GNU Findutils参考手册
  • man mv和GNU Coreutils参考手册 (特别是11.4 mv :移动(重命名)文件 )
  • man \[和16.3测试:检查文件类型并比较 Coreutils文档中的值
    这个(即/usr/bin/[ ),而不是shell的[ builtin ,是你在使用-exec [时运行的。