以下内容假设您使用的是默认的 macOS 实用程序(touch、date...),因为它们已经完全过时,如果您使用更新的版本(例如 macports 或 brew),则需要进行一些调整)。它还假设您使用的是bash。
如果您有子文件夹,ls 不是正确的工具。无论如何,ls 的输出不是针对计算机的,而是针对人类的。因此,首先要做的是找到所有电子邮件文件。你猜怎么了?执行此操作的实用程序名为 find:
$ find . -type f -name '*.emlx'
foo/bar.emlx
baz.emlx
...
从当前目录 (.) 开始搜索名称为 anything.emlx (-name '*.emlx') 的真实文件 (-type f)。适应你的情况。如果所有文件都是电子邮件文件,您可以跳过 -name ... 部分。
接下来我们需要遍历所有这些文件并处理它们中的每一个。这比for f in ... 稍微复杂一些,原因有几个(大量文件、带空格的文件名...),一个可靠的方法是将find 命令的输出重定向到while 循环:
while IFS= read -r -d '' f; do
<process file "$f">
done < <(find . -type f -name '*.emlx' -print0)
find 的-print0 选项用于用空字符而不是默认的换行符分隔文件名。 < <(find...) 部分是一种将find 的输出重定向到while 循环输入的方法。 while IFS= read -r -d '' f; do 读取由find 生成的每个文件名,将其存储在shell 变量f 中,保留前导和尾随空格(如果有)(IFS=)、反斜杠(-r)并使用空字符作为分隔符 (-d '')。
现在我们必须对每个文件的处理进行编码。让我们先检索交货时间,假设它总是以X-Delivery-Time:开头的最后一行的第二个单词:
awk '/^X-Delivery-Time:/ {t = $2} END {print t}' "$f"
这样做。如果您还不了解 awk,那么是时候学习一点了。它是非常有用的瑞士文本处理工具之一(sed 是另一种)。但是让我们稍微改进一下,让它返回第一个遇到的交付时间而不是最后一个,一旦遇到就停止,并检查时间戳是否是真实的时间戳(数字):
awk '/^X-Delivery-Time:[[:space:]]+[[:digit:]]+$/ {print $2; exit}' "$f"
正则表达式的 [[:space:]]+ 部分匹配 1 个或多个空格、制表符等,[[:digit:]]+ 匹配 1 个或多个数字。 ^ 和 $ 分别匹配行的开头和结尾。结果可以赋值给一个shell变量:
t="$(awk '/^X-Delivery-Time:[[:space:]]+[[:digit:]]+$/ {print $2; exit}' "$f")"
请注意,如果没有匹配,t 变量将存储空字符串。稍后我们将使用它来跳过此类文件。
一旦我们有了这个交付时间,在您的示例中它看起来像一个 UNIX 时间戳(自 1970/01/01 以来的秒数),我们必须使用它来更改电子邮件文件的最后修改时间。执行此操作的命令是 touch:
$ man touch
...
touch [-A [-][[hh]mm]SS] [-acfhm] [-r file] [-t [[CC]YY]MMDDhhmm[.SS]] file ...
...
不幸的是,touch 想要CCYYMMDDhhmm.SS 格式的时间。不用担心,date 实用程序可用于将 UNIX 时间戳转换为我们喜欢的任何格式。例如,使用您的示例时间戳 (1535436541):
$ date -r 1535436541 +%Y%m%d%H%M.%S
201808280809.01
我们差不多完成了:
while IFS= read -r -d '' f; do
# uncomment for debugging
# echo "processing $f"
t="$(awk '/^X-Delivery-Time:[[:space:]]+[[:digit:]]+$/ {print $2; exit}' "$f")"
if [ -z "$t" ]; then
echo "no delivery time found in $f"
continue
fi
# uncomment for debugging
# echo touch -t "$(date -r "$t" +%Y%m%d%H%M.%S)" "$f"
touch -t "$(date -r "$t" +%Y%m%d%H%M.%S)" "$f"
done < <(find . -type f -name '*.emlx' -print0)
请注意我们如何测试 t 是否为空字符串 (if [ -z "$t" ])。如果是,我们打印一条消息并跳转到下一个文件(continue)。只需将所有这些放在带有 shebang 行的文件中并运行...
如果您必须使用具有更复杂和可变格式的Date 字段而不是X-Delivery-Time 字段(例如Date: Mon, 11 Jun 2018 10:36:14 +0200),那么最好安装一个最新版本的touch Mac Ports 或 Homebrew 的 coreutils 包。那么:
while IFS= read -r -d '' f; do
t="$(awk '/^Date:/ {print gensub(/^Date:[[:space:]+](.*)$/,"\\1","1"); exit}' "$f")"
if [ -z "$t" ]; then
echo "no delivery time found in $f"
continue
fi
touch -d "$t" "$f"
done < <(find . -type f -name '*.emlx' -print0)
awk 命令稍微复杂一些。它打印没有Date: 前缀的匹配行。以下 sed 命令将以更紧凑的形式执行相同的操作,但实际上并不会更具可读性:
t="$(sed -rn 's/^Date:\s*(.*)/\1/p;Ta;q;:a' "$f")"