如何传递命令?

如何传递命令?

我有这个简单的脚本,它的作用无非是:

  • 检查电子邮件是否符合特定模式
  • 在这种情况下,将标签添加到标签列表中
  • 退出之前,打印该标签列表
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

相关内容