在Bash中动态构建命令管道

5

我正在编写一个Bash程序,用于处理选项。

例如:./my_program -l 3 -a -s

  • -l 3将限制输出为三行
  • -a将选择所有我的文件
  • -s将排序输出

目前为止,我只能同时使用两个选项:

if [ $all == 1 ]
then
    if [ $sort == 1 ]
    then
        printf '%s\n' "${haikus[@]}" | sed -e 's/^[ \t]*//' | sort
    else
        printf '%s\n' "${haikus[@]}" | sed -e 's/^[ \t]*//'
    fi
fi

如果使用-a选项,我会打印整个文件;或者,如果同时使用-a-s选项,我将使用相同的命令但是会添加sort指令。
使用这种解决方案,如果我想实现-l选项,就需要创建很多"if"语句。
首先,我考虑创建一个包含我的命令的变量。
例如:
sort='sort'
limit='grep -m3'

然后我这样编写我的命令:
printf '%s\n' "${haikus[@]}" | sed -e 's/^[ \t]*//' | $sort | $limit

但这根本行不通。

事实上,我想编写一个基本命令,并能根据选项添加更多内容。

如何在没有大量“if”语句的情况下正确完成此操作?


你可以构建一个键值数组,但说实话,我不明白为什么你想要构建动态输入。 - Mike Q
FYI,在[中使用==不能保证在所有符合POSIX标准的shell中都能正常工作。对于字符串比较,使用=更可靠,因为它是由测试的POSIX标准所要求的,并且即使是最小的/bin/sh实现也可以正常工作,而不仅仅是bash。 - Charles Duffy
4个回答

4
你可以直接将管道输入到一个if语句中。但是,这需要像cat这样的无操作过滤器,在某些情况下会稍微不那么高效。
printf '%s\n' "${haikus[@]}" | sed -e 's/^[ \t]*//' |
 if [ "$sort" = 1 ]; then sort; else cat; fi |
 if [ "$l" -gt 0 ]; then grep -m"$l"; else cat; fi

if语句本身就是一个命令,因此它有自己的一组文件描述符,封闭的命令继承了这些描述符。if语句本身不会对这些描述符进行任何操作,因此sortcat看到的数据与未包装时相同。


3

这是一个非常好的问题,但解决方案并不那么明显。

你可以将一些函数调用连接在一起。这些函数可以检查相关的标志,然后执行“某些操作”,例如调用sort,或者“什么也不做”,只是调用cat。在管道中使用普通的cat调用本质上是一个无操作:它将stdin复制到stdout而没有任何更改。

maybe_sort() {
    if [[ $sort == 1 ]]; then
        sort
    else
        cat
    fi
}

maybe_limit() {
    if [[ -n $limit ]]; then
        head -n "$limit"
    else
        cat
    fi
}

要使用这些,你需要写成这样:
printf '%s\n' "${haikus[@]}" | sed -e 's/^[ \t]*//' | maybe_sort | maybe_limit

1
顺便说一下,我在回答如何在管道中有条件地使shell函数消失而不使用cat的问题时采用了你的函数。 - Charles Duffy

0
大家好,我回复有点晚。我不知道我可以使用cat作为无操作符,这就是我寻找的解决方案。 所以我只需用“cat”操作符初始化所有选项。如果选项被正确触发,我只需使用正确的操作符更新字符串。 然后,我只需要编写一行带有所有选项的操作符即可。
非常感谢你们。

0

我在寻找没有固定顺序的解决方案。最终得到了:

# assemble pipeline from sequence of arguments
# each argument interpreted as command to evaluate (e.g. spaces interpreted)
pipe_all() {
    if [[ $# -gt 0 ]]; then
        local current=$1
        shift
        eval "$current" | pipe_all "$@"
    else
        cat
    fi
}

filter_weapon() {
    grep '/Weapon/'
}

filters=()
filters+=(sort)
filters+=(uniq)
filters+=(filter_weapon)
filters+=("head -n $1")  # should be encoded as a single argument and be a valid command

pipe_all "${filters[@]}"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接