Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically building a command pipe in Bash

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?

like image 800
Kaikina Avatar asked Mar 06 '23 08:03

Kaikina


2 Answers

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
like image 50
John Kugelman Avatar answered Apr 05 '23 22:04

John Kugelman


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.

like image 20
chepner Avatar answered Apr 06 '23 00:04

chepner