我的bash脚本通过管道(stdin
)获取完整路径,并通过命令行参数获取排除模式。目前这处理正则表达式模式,但我想重写仅处理glob模式。
如何使用glob模式过滤stdin
(例如fnmatch
)?因为据我所知,我无法使用grep
进行globbing ,我不想手动将glob模式转换为regexp。 - 所以我想找到一个快速而不是hacky的解决方案。
echo -e 'apple tree\nbanana tree\norange tree' | ./filter_script.sh '*ban' '*ge*'
# banana tree
# orange tree
完整示例代码位于GitHub Gists
在现实生活中,这个脚本将获得数千条路径,所以我认为,原生bash实现效果不佳。
任何人都知道“grep like”glob过滤器?
答案 0 :(得分:1)
假设您希望仅以原生bash
方式支持glob
模式,您可以使用Extended Globs来实现默认情况下不设置,但可以通过
shopt -s extglob
您有以下列方式定义的字符串
myStr=$'apple tree\nbanana tree\norange tree'
您可以按如下方式应用glob
模式。 Fist将字符串读入数组,使用readarray
由换行符分割(您需要bash 4.0
或更高)
readarray -t y <<<"$myStr"
现在是循环,
for i in "${y[@]}"; do
[[ $i == @(*n*) ]] && echo "$i" ;
done
根据需要生成结果。
其中,@(list)
代表Matches one of the given patterns
要匹配glob
中*le*
(或)apple
中的ge
的另一个示例orange
将使用
for i in "${y[@]}"; do [[ $i == @(*le*|*ge*) ]] && echo "$i" ; done
apple tree
orange tree
有关您的原始输入,
for i in "${y[@]}"; do [[ $i == @(*ban*|*ge*) ]] && echo "$i" ; done
banana tree
orange tree
对于OP要求将输入参数转换为glob pattetrns,由|
分隔,需要额外的一行来解析位置参数,具体取决于计数,
#!/bin/bash
shopt -s extglob
myStr=$'apple tree\nbanana tree\norange tree'
readarray -t y <<<"$myStr"
# if the argument count is more than 1, since the input arguments are
# separated by ' ', replace them with `|` as required in the glob
# pattern
(($# > 1)) && args=$(printf "%s" "$*" | tr ' ' '|') || args="$*"
for i in "${y[@]}"; do
[[ $i == @($args) ]] && echo "$i" ;
done
现在您可以将脚本作为
运行bash script.sh '*ge*' '*le*'
apple tree
orange tree
(或)只是一个参数,
bash script.sh '*ba*'
banana tree
答案 1 :(得分:0)
我认为以下内容可以满足您的需求:
#!/bin/bash
while IFS= read -r line
do
for pattern in "$@"
do
if
[[ $line = $pattern ]]
then
echo "$line"
break
fi
done
done
这逐行读取标准输入(因此可以在&#34;流式&#34;应用程序或非常大的文件中使用)。在Bash条件([[ ]]
)中,相等比较(以及!=
)执行glob-type匹配,除非引用右侧字符串。
答案 2 :(得分:0)
从grep
的速度中受益的一种方法是将模式转换为正则表达式。
试试这个:
#!/bin/bash
glob_to_regex()
{
local regex=$1
regex=${regex//\./\\.}
regex=${regex/\\/\\\\}
regex=${regex//\*/.*}
regex=${regex//\?/.}
printf %s "^$regex$"
}
regex_from_args()
{
local arg
local not_first=
for arg in "$@"
do
[[ $not_first ]] && printf %s "|"
not_first=1
glob_to_regex "$arg"
done
}
egrep "$(regex_from_args "$@")"