一行排除有例外的模式
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 a
或 printf '%s\n' "${a[@]}"
.
查看结果
请注意,这是实现此目的的一种疯狂方法(而且它显然不是单行代码),您可以在 awk 中更有效地完成它。但至少这是一个纯粹的 bash 解决方案,并且不使用任何外部工具,是的。
1.这取决于 bash 4+,因为其中一个数组是关联的。
我的目标是制作一个优雅的单行本。
输入:
源文件,包含字符串列表:
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 a
或 printf '%s\n' "${a[@]}"
.
请注意,这是实现此目的的一种疯狂方法(而且它显然不是单行代码),您可以在 awk 中更有效地完成它。但至少这是一个纯粹的 bash 解决方案,并且不使用任何外部工具,是的。
1.这取决于 bash 4+,因为其中一个数组是关联的。