Dynamically building a command pipe in Bash

早过忘川 提交于 2020-08-08 06:56:09

问题


I am writing a bash program that takes options.

For example : ./my_program -l 3 -a -s

  • -l 3 will limit the output to three lines
  • -a will select all my file
  • -s will sort the output

For now, I could use two options at a time this way:

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

If -a option, I print the whole file, or, if -a option and -s option, I use the same command but i use sort.

With this solution, if I want to implement the -l, it would create a lot of "if" statements.

I first thought of creating a variable containing my command.

Example:

sort='sort'
limit='grep -m3'

and then write my command this way:

printf '%s\n' "${haikus[@]}" | sed -e 's/^[ \t]*//' | $sort | $limit

But that is simply not working.

The fact is, I would like to write a basic command, and being able to add more to this one, depending of the options.

How can I do this properly without tons of "if" statements?


回答1:


Great question with a tricky, not-so-obvious solution.

What you can do is chain together a couple of function calls. Those functions can examine the relevant flags and then either "do something" like call sort or "do nothing" and simply call cat. A plain cat call in a pipeline is essentially a no-op: it copies stdin to stdout unchanged.

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

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

To use these you'd then write:

printf '%s\n' "${haikus[@]}" | sed -e 's/^[ \t]*//' | maybe_sort | maybe_limit



回答2:


You can pipe directly to an if statement. This will require a no-op filter like cat, though, in some places, so it is slightly less efficient in that regard.

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

An if statement is itself a command, so it has its own sets of file descriptors that the enclosed commands inherit. The if statement itself doesn't do anything with those descriptors, so sort and cat both see the data they would if they hadn't been wrapped in the first place.




回答3:


Ok everyone. I'm replying a bit late. I didn't know I could use cat as a no-op, and that was the solution I sought. So I simply initialized all my options with "cat" op. If the option is triggered correctly, I simply update the string with the good op. Then, I just had to write one op line, with all options in it.

Thank you a lot.



来源:https://stackoverflow.com/questions/52538881/dynamically-building-a-command-pipe-in-bash

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!