awk、sed 或 grep 命令为 file1 中的每个单词输出布尔文件,以检查它是否存在于 file2 中

awk、sed 或 grep 命令为 file1 中的每个单词输出布尔文件,以检查它是否存在于 file2 中

我有两个文件,如果文件1中的单词在文件2中不存在,我想在新文件(例如文件3)的相应行中生成单词false。否则,我想在相应行中输出true。

文件1:

a
b
c
d

文件2:

a
d
c
e
t
y

文件3:

true
false
true
true

有没有办法使用 awk/sed/grep 命令来做到这一点?

答案1

假设 file2 不为空,并且也没有太大到无法放入内存而不引起问题:

awk 'NR==FNR{a[$0]; next} {print ($0 in a ? "true" : "false")}' file2 file1

答案2

阅读man grep bash,并执行类似 UNTESTED 的操作:

for pat in $(cat "file 1") ; do
  ans="False"
  grep --quiet  "^$pat\$" "file 2" || \
    ans="True"
  echo -e "$pat\t$ans" >>"file 3"

答案3

$ perl -le '
  # construct a partial regex from the first filename argument
  my $re = join("|", split /\n+/, do { local(@ARGV,$/) = shift; <> });

  # complete and pre-compile the regex
  $re = qr/\b(?:$re)\b/;

  # read and process stdin and/or remaining filename args
  while(<>) {
    print /$re/ ? "true" : "false"
  }' file2 file1 
true
false
true
true

此 perl 脚本读取第一个参数 ( ) 指示的整个文件file2,并构造一个与文件每一行上的各个单词相匹配的正则表达式。正则表达式中的每个单词都由交替字符分隔|。它假定文件每行包含一个单词,并且各行由一个或多个换行符分隔(这具有忽略空行的有用副作用)。

注意: 的每个单词都file2将被解释为正则表达式。如果您希望将它们解释为固定字符串,请将行更改my $re ...为:

my $re = join("|", map { quotemeta $_ } split /\n+/, do { local(@ARGV,$/) = shift; <> });

quotemeta 函数“引用”字符串中的所有正则表达式元字符,以便它们失去其特殊含义并被视为文字字符。看perldoc -f quotemeta。该map函数使{ quotemeta $_ }块应用于返回的列表的每个元素split。参见perldoc -f mapperldoc -f split

顺便说一句,do { local(@ARGV,$/) = shift; <> })这是一个相当常见的 Perl 习惯用法,用于“slurping”文件,即一次读入整个文件。还有许多其他方法可以做同样的事情,包括像文件::吸食者,但这很简单且可移植,并且不需要使用或安装任何库模块。

然后,该脚本使用qr引用运算符来预编译正则表达式以提高性能(在每次循环中重新编译相同的正则表达式将极大地浪费 CPU 时间)。单词边界标记 ,\b用于防止部分匹配,并?:用于防止捕获匹配(这只会浪费时间,因为我们只需要检测匹配发生,不需要将匹配用于任何事情)。参见perldoc -f qrman perlre

正则表达式区分大小写,但可以使用正则i表达式修饰符使其不区分大小写:

$re = qr/\b(?:$re)\b/i;

然后,该脚本读取任何剩余的输入,并且对于每一行输入,如果该行与正则表达式匹配,则打印“true”,否则打印“false”。因为它使用while(<>)它将从标准输入和/或任何文件名参数读取数据。在此示例中,它从 读取输入file1

内存使用量与第一个文件的大小成正比 - 中的单词越多file2,它使用的 RAM 就越多。当然,运行时间与第一个文件和所有其他输入的大小成正比。

答案4

tmp=$(mktemp)
comm -2 <(sort file1) <(sort file2) \
| sed -e 's/^\t.*/true/;t
  c false' > "$tmp"

paste <(cat -n < "$tmp") \
  <(cat -n file1 | sort -bk2) \
| sort -bk3,3n | cut -f2

输出:-

true
false
true
true

笔记:

  • 第一步,我们对这两个文件进行排序,并通过 comm 运行它们,并抑制第二个文件。
  • 接下来 sed 将识别 true/false 元素
  • 最后,为了恢复原始顺序,我们对输出和编号排序的输入进行粘贴。

相关内容