【问题标题】:Match string at end of line in file匹配文件行尾的字符串
【发布时间】:2021-09-29 20:16:39
【问题描述】:

我有两个文件:

  1. $hashfile:哈希和 ./relative/path/to/file/names,都在一行,用 2 个空格隔开

  2. $badfiles: ./relative/path/to/file/names 我需要在 $hashfile 中找到以获取相应的哈希

这是一个 $hashfile 的摘录:

c2c99b59f3303cafac85c2c6df6653cc  ./vm-mount.sh
058a8fb0b9366f248be32b7390e94595  ./Jerusalem_Canon EOS R5_20210601_031.jpg~
23eba1c54846de5244312047e2709f9a  ./rsync-back.sh
ff3f08f7bf45f8e9ef8b33192db3ce9a  ./vm-backup.sh
11e0d980f3b2219f65da97a0318e7dce  ./Jerusalem_Canon EOS R5_20210601_031.jpg
49fb1fb660dce09acd87861a228c899d  ./vm-test.sh

这是一个包含搜索模式的 $badfiles 示例:

./Jerusalem_Canon EOS R5_20210601_031.jpg
./file.txt

我需要在 $hashfile 中搜索 $badfiles 中的模式,并将包含哈希的匹配行写入第三个文件 $new。

到目前为止,我已经使用了以下内容:

grep -Ff "$badfiles" "$hashfile" > "$new"

但是,这将匹配两者:

058a8fb0b9366f248be32b7390e94595  ./Jerusalem_Canon EOS R5_20210601_031.jpg~
11e0d980f3b2219f65da97a0318e7dce  ./Jerusalem_Canon EOS R5_20210601_031.jpg

然后我在 $badfiles 的每一行末尾添加了一个 $ 并将 grep 命令更改为:

grep -f "$badfiles" "$hashfile" > "$new"

这适用于一个小型测试文件夹,但我担心不会被解释为固定字符串的模式搜索可能会对大型文件系统造成严重破坏。我有大约 300,000 多个文件名和哈希,其中一些使用特殊字符,例如 "':,;()[]? - 简而言之,Linux ext4 和/或 Windows NTFS 文件系统可以接受的任何字符。

有什么想法吗?

编辑:解决方案

显然 grep 没有提供将换行符包含到固定字符串搜索中的简单解决方案。 @anubhava 提供了使用 awk 的最佳解决方案:

awk 'NR == FNR {a[$0]; next}
{b=$0; sub(/^\S+\s+/, "", b)}
b in a' "$badfiles" "$hashfile" > "$new"

注意:$badfiles、$hashfiles 和 $new 是保存文件名的变量。

上面的语法最好描述here under "Two-file processing"NR 保存到目前为止从所有文件读取的行号,而FNR 保存从当前文件读取的行号。因此,当 awk 完成读取 $badfiles 并读取 $hashfile 的第一行时,NR 保存到目前为止读取的所有行的总和,FNR 等于 1,因为这是新文件的第一行。 {a[$0]; next} 将 $badfiles 文件读入一个数组,; next 阻止程序执行后续的条件和动作,直到整个 $badfiles 被读取,即直到 NR == FNR 为 false。

读取 $hashfile 时,$0(已读取的行)被分配给 bb=$0)。 sub(/^\S+\s+/, "", b) 在行首 (^) 替换一个或多个非空格字符 (\S+),然后在变量中由 "" (空字符串) 替换一个或多个空格字符 (\s+) b。然后只留下变量b中的./path/to/file。

最后一行 b in a' "$badfiles" "$hashfile" > "$new" 查看变量 b 是否在 a 中找到,如果是,则将 $hashfile 中的行复制到文件 $new。如果 $badfiles 中的所有行在 $hashfile 中都有匹配的条目,则将带有哈希值的相应 $hashfile 行复制到 $new。

由于文件名前的hash值是固定长度的,awk语句可以简化为:

awk 'NR == FNR {a[$0]; next}
{b=substr($0,35)}
b in a' "$badfiles" "$hashfile" > "$new"

上面的substr() 语句采用输入行$0 并去掉前34 个字符,从1 开始计数。子字符串b 然后从位置35 开始。这很像bash 中的子字符串提取,例如${mystring:34}。请注意,bash 子字符串提取从 0 开始计数。

我现在使用该 awk 命令的变体来创建一个新的哈希文件,其中包含除$deletedfiles 中列出的文件之外的所有文件哈希:

awk 'NR == FNR {a[$0]; next}
{b=substr($0,35)}
!(b in a)' "$deletedfiles" "$hashfile" > "$new"

使用上述命令,$deletedfiles 中未找到的每个字符串 b(来自 $hashfile)将相应的行从 $hashfile 复制到 $new。必须特别注意一个空的 $deletedfiles 文件:如果 $deletedfiles 是一个空文件,那么 $new 文件也将是空的!预期结果是 $new 文件与 $hashfile 相同。

即使在一个哈希文件中包含 200,000-300,000 个文件名,此解决方案也非常有效(而且速度很快)。

【问题讨论】:

  • 也许sed 你的模式文件将所有奇怪的字符转换为点?不是一个很好的解决方案,但可能会使问题更简单。

标签: bash awk grep anchor newline


【解决方案1】:

这个awk 解决方案应该适合你:

awk 'FNR == NR {srch[$0]; next} 
{s = $0; sub(/^[^[:blank:]]+[[:blank:]]+/, "", s)}
s in srch' badfiles hashfile

11e0d980f3b2219f65da97a0318e7dce  ./Jerusalem_Canon EOS R5_20210601_031.jpg

此解决方案首先将来自badfiles 的所有行存储在数组srch 中。然后从hashfile 删除文本直到第一个空格,然后如果在srch 数组中找到剩余部分,则打印同一文件中的每一行。

【讨论】:

  • awk: cmd. line:1: FNR == NR {srch[./xxhash_replace_tst.sh]; next} awk: cmd. line:1: ^ syntax error awk: cmd. line:1: FNR == NR {srch[./xxhash_replace_tst.sh]; next} awk: cmd. line:1: ^ unterminated regexp awk: cmd. line:2: error: Unmatched ( or \(: /xxhash_replace_tst.sh; sub(/ 谢谢@anubhava。我试过你的建议,但它产生了错误。它将搜索字符串评估为正则表达式。这不是我想要的。 badfiles 中的字符串应该被视为固定字符串,就像 grep -F 选项一样,但带有换行符。
  • 为什么你有srch[./xxhash_replace_tst.sh]; 而不是我建议的srch[$0];
  • 抱歉,正在尝试找出 cmets 中的格式。我确实使用了 srch[$0],您在上面看到的是我在处理 badfiles 时遇到的错误。它实际上试图解释点 (.)。 badfiles 中的每个路径都以./ 开头。 ./xxhash_replace_tst.sh 是 badfiles 中的一行。
  • 我的 awk 命令中没有点解释。我发布的命令是在 gnu-awk 和 BSD-awk 中经过测试和工作的命令。
  • 我在一个小型测试文件夹中使用您的代码,我在其中模拟文件的位腐烂。它工作得很好。见这里:5f2aacccec64dba5a79016fed368fc9e ./file\s.txt 11e0d980f3b2219f65da97a0318e7dce ./"Jerusalem: shot with 'Canon EOS R5' [20210601] 031".jpg。我想知道这将如何扩展到具有数十万行(文件和哈希)的大型哈希文件? grep 在这些文件上工作得非常快。感谢分享解决方案。可惜 grep 似乎没有解决办法。
猜你喜欢
  • 1970-01-01
  • 2011-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-03
  • 1970-01-01
  • 2021-09-17
相关资源
最近更新 更多