我有两个文件:
$hashfile:哈希值和。/relative/path/to/file/name,都在一行,用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
我需要在$badfiles中搜索$hashfile中的模式,并将包含哈希值的匹配行写入第三个文件$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文件系统可以接受的任何字符。
任何想法?
<编辑:解决方案/strong>
显然,grep并没有提供在固定字符串搜索中包含换行符的简单解决方案。@anubhava提供了使用awk的最佳解决方案:
awk 'NR == FNR {a[$0]; next}
{b=$0; sub(/^S+s+/, "", b)}
b in a' "$badfiles" "$hashfile" > "$new"
注意:$badfiles, $hashfiles和$new是保存文件名的变量。
上面的语法最好在"双文件处理"下描述。NR
保存到目前为止从所有文件读取的行号,而FNR
保存到目前为止从当前文件读取的行号。因此,当awk完成读取$badfiles并读取$hashfile的第一行时,NR
保存到目前为止读取的所有行的总和,并且FNR
等于1,因为这是新文件的第一行。{a[$0]; next}
将$badfiles文件读入一个数组,; next
阻止程序执行后续的条件和操作,直到整个$badfiles被读完,也就是说,直到NR == FNR
为假。
当读取$hashfile时,$0
(已读取的行)被分配给b
(b=$0
)。sub(/^S+s+/, "", b)
在行首(^
)替换一个或多个非空格字符(S+
),然后在变量b
中用""
(空字符串)替换一个或多个空格字符(s+
)。这样,在变量b
中只留下。/path/to/文件。
最后一行b in a' "$badfiles" "$hashfile" > "$new"
查看变量b
是否在a
中找到,如果是,则将$hashfile中的行复制到$new文件中。如果$badfiles中的所有行在$hashfile中都有一个匹配的条目,则将包含哈希值的对应$hashfile行复制到$new中。
由于文件名前的哈希值是固定长度的,因此awk语句可以简化为:
awk 'NR == FNR {a[$0]; next}
{b=substr($0,35)}
b in a' "$badfiles" "$hashfile" > "$new"
上面的substr()
语句取输入行$0
,从1开始,去掉前34个字符。然后子字符串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 file与$hashfile相同。
这个解决方案工作得非常好(而且很快),即使在一个哈希文件中有200,000-300,000个文件名。
这个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
数组中找到剩余部分。