一行排除有例外的模式

one-liner to exclude patterns with exceptions

我的目标是制作一个优雅的单行本

输入:

源文件,包含字符串列表:

foo123
bar12356
foo999
var8292
bar922
link991
bar8
var000

具有黑名单模式的文件:

bar
link

白名单文件(可包含多个条目):

bar922

任务: 我们需要根据黑名单中的模式删除字符串,保留白名单中的精确匹配。一个问题是我们需要保持输出与原始文件中的顺序相同,并且我们不应该添加白名单字符串(如果它们不存在)在初始文件中.

输出:

foo123
foo999
var8292
bar922
var000

您可以为此使用 awk 这个小命令。这个想法是在黑名单文件 (bl) 上使用 paste 命令使其内容由 | 分隔,例如bar|link 以及白名单文件 (wl).

文件生成后,我们对文件内容进行正则表达式匹配,条件是这些条目可以在白名单中,或者条目不应该在黑名单中。

awk -v bl=$(paste -sd'|' bl) -v wl=$(paste -sd'|' wl) '[=10=] ~ wl || [=10=] !~ bl' file

评论中有一点要处理 wl 文件中的空行。如果您怀疑它们,请使用 sed -i '/^$/d' wl.

修复它们

您可以创建一个简单的 one-liner 使用 grep -f 从每个白名单和黑名单文件中读取模式并包括 -v 以反转与黑名单的匹配,然后使用 stdin 上的组合结果来创建有序文件,例如

输入、白名单、黑名单文件

$ cat file
foo123
bar12356
foo999
var8292
bar922
link991
bar8
var000

白名单

$ cat white
bar922

黑名单

$ cat black
bar
link

编辑 在与@kvantour 进行交流工作后,您可以使用 进程替换 stdin 上的最终排序提供最终排序列表,例如

$ grep -wof - file < <(grep -v -f black file; grep -f white file)
foo123
foo999
var8292
bar922
var000

的解决方案真的很优雅,没有乱来。如果你想保持顺序,你可以将它扩展为:

$ { grep -wf wl file; grep -vf bl file ;} | grep -wf - file

请注意,我们为白名单引入了 -w 标志以确保单词匹配。

备注:这是一个优雅的解决方案,它是迄今为止最有效的解决方案!

也可以使用 Perl,解决这个问题。

> cat stan.in
foo123
bar12356
foo999
var8292
bar922
link991
bar8
var000
> cat white
bar922
> cat black
bar
link
> perl -lne 'BEGIN{ @w=qx(cat white);@b=qx(cat black);}chomp for(@w);chomp for(@b); $x=$_;print if grep {$x=~/$_/} @w; print if scalar(grep { $x=~/$_/g} @b)==0' stan.in 
foo123
foo999
var8292
bar922
var000
> 

$ perl -lne 'BEGIN{ @w=qx(cat white);@b=qx(cat black);}chomp for(@w,@b);$x=$_;print if grep {$x=~/$_/} @w; print if scalar(grep { $x=~/$_/g} @b)==0' stan.in
foo123
foo999
var8292
bar922
var000

只是为了好玩,这里有一个使用 bash 数组的选项1:

# Gather our lists...
mapfile -t a < input.txt; mapfile -t wl < wl; mapfile -t bl < bl

# And store the whitelist as indices for easier handling...
declare -A wl_a=(); for x in "${wl[@]}"; do wl_a["$x"]=1; done

# Then step through the data array,
for x in "${!a[@]}"; do
  # detecting and skipping whitelist matches,
  [[ "${wl_a[${a[$x]}]}" = 1 ]] &&
    printf 'wl: %s\n' "$x" &&
    continue
  # and deleting blacklist matches.
  for y in "${bl[@]}"; do
    [[ "${a[$x]}" = "$y"* ]] &&
      printf 'bl: %s\n' "${a[$x]}" &&
      unset a["$x"]
  done
done

结果是数组 ${a[@]} 包含您的数据,按原始顺序排列,并删除了相应的黑名单项目。您可以使用 declare -p aprintf '%s\n' "${a[@]}".

查看结果

请注意,这是实现此目的的一种疯狂方法(而且它显然不是单行代码),您可以在 awk 中更有效地完成它。但至少这是一个纯粹的 bash 解决方案,并且不使用任何外部工具,是的。

1.这取决于 bash 4+,因为其中一个数组是关联的。