我有这个简单的脚本,它的作用无非是:
- 检查电子邮件是否符合特定模式
- 在这种情况下,将标签添加到标签列表中
- 退出之前,打印该标签列表
set -e
lista_tag=()
in_file="/tmp/grepmail-classify.txt"
# save stdin to file, to use it multiple times
cp /dev/stdin $in_file
# CLASSIFY
res=$(grepmail -B "some regex pattern" < $in_file)
if [ ! -z "$res" ]
then
lista_tag+=("PUSH")
fi
res=$(grepmail -B "some other regex pattern" < $in_file)
if [ ! -z "$res" ]
then
lista_tag+=("MERGIFY")
fi
# ⁝ Many many more similar patterns
# output them comma separated
echo ${lista_tag[*]}
正如您所看到的,存在重构和抽象的情况。res
并且if .. fi
部分重复。但我不确定如何安全地传递命令。我想我想做的是调用这样的函数(或类似的函数):
classify '"grepmail -B "somepattern"' 'MYTAG'
但这很棘手!我已阅读常问问题但我不确定它是否适用于我的情况。
所以问题是:传递命令的正确方法是什么(如果有的话)?res=
这样的函数的一部分会是什么样子?
答案1
classify '"grepmail -B "somepattern"' 'MYTAG'
这很难开始工作,正是由于中提到的原因Bash常见问题解答 050。
但是如果我们把“tag”参数放在前面,我们就可以让它工作,因为这允许我们使用命令的其余部分:
#!/bin/bash
lista_tag=()
classify() {
local tag="$1"
shift
res=$( "$@" < "$in_file")
if [ -n "$res" ]; then
lista_tag+=("$tag")
fi
}
classify PUSH grepmail -B "some regex pattern"
classify MERGIFY grepmail -B "some other regex pattern"
这里的关键是我们不将命令的参数粘贴到一个字符串中,但将它们分开。"$@"
很神奇:它分别扩展到所有位置参数。移出标签后,剩下的就是你的命令了。
但是,您不能以相同的方式将重定向粘贴在那里,因为这需要运行命令eval
并适当地引用它。对于任何用户提供的输入,您还需要仔细执行此操作,否则您很可能会在那里留下命令执行漏洞。
无论如何,由于该grepmail -B
部分看起来是恒定的,因此只需传递标签和模式即可:
#!/bin/bash
lista_tag=()
in_file=foo.txt
classify() {
local pattern="$1"
local tag="$2"
if [[ -n "$(grepmail -B "$pattern" < "$in_file")" ]]; then
lista_tag+=("$tag")
fi
}
classify "some regex pattern" PUSH
classify "some other regex pattern" MERGIFY
答案2
这是使用关联数组的好机会:
#!/bin/bash
lista_tag=()
declare -A patterns=(
[PUSH]='some regex pattern'
[MERGIFY]='some other pattern'
# ... other [tag]=pattern pairs ...
)
# capture stdin
in_file=$(mktemp)
cat > $in_file
# CLASSIFY
# iterate over the array indices
for tag in "${!patterns[@]}"; do
if [[ -n "$( grepmail -B "${patterns[$tag]}" < "$in_file" )" ]]; then
lista_tag+=("$tag")
fi
done
# print comma-separated list
( IFS=","; echo "${lista_tag[*]}" )
grepmail
如果没有匹配项(如 ),则可以具有非零退出状态,那么grep -q
它更简单:
for tag in "${!patterns[@]}"; do
grepmail -q -B "${patterns[$tag]}" < "$in_file" && lista_tag+=("$tag")
done