6

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?

2
  • You could build a key value array , but honestly I don’t see why you would want to build dynamic inputs Commented Sep 27, 2018 at 14:31
  • FYI, using == in [ is not guaranteed to work in all POSIX-compliant shells. It's more reliable to use = for string comparison, as it's required to work by the POSIX standard for test, and correspondingly works even with minimal /bin/sh implementations, not just bash. Commented Jul 21, 2020 at 23:23

4 Answers 4

5

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.

Sign up to request clarification or add additional context in comments.

Comments

4

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

1 Comment

FYI, I took the liberty of adopting your functions in an answer to Can I make a shell function in a pipeline conditionally disappear without using cat?
1

I was looking for solution without fixed order. Ended up with:

# 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[@]}"

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.