2

How can I pipe commands, and inside the script decide if it should break the pipe or not.

It's simmilar to this question. But instead of awk I'd like to use my own script:

check_ip | update_bind9

check_ip should find the external ip (curl -s ifconfig.co) and compare it with the IP stored in a file (~/.ip).

Here's the main question:

If the IP has changed, check_ip should pass the IP through the pipe. If not, it should break the pipe.

Is check_ip that breaks the pipe, or update_bind9 that ignores the call if not called with the IP?

I did some tests, calling: check_ip | echo "ok".

I tried to exit 0, exit 1, return true, return false. Tried to set -e. No success.

2 Answers 2

3

All components of a pipeline start simultaneously – the second command always will be started no matter what happens in the first command.

So it's the second command which needs to handle empty stdin.


But I would say pipes aren't a good choice for this in the first place.

(Maybe you're confusing | (pipe) with || (boolean OR)? The latter is frequently used as a shorter alternative to if/then, e.g. x || y will only call y when x fails – and similarly, x && y will only call y when x succeeds.)

In general you should use an if/then block. For example, if check_ip itself returns 0 (success) when addresses are different but 1 (failure) when they're the same, it would look like this:

if addr=$(check_ip); then
    echo "$addr" | update_bind9
fi

Alternatively, if update_bind9 can determine the address on its own (without reading stdin), it would be like this:

if check_ip_needs_updating; then
    update_bind9
fi
1
3

This other answer is good. However, if you really need to pipe conditionally then you may find ifne useful:

ifne runs the following command if and only if the standard input is not empty.

In your case it will be like check_ip | ifne update_bind9.

Most likely ifne is not installed in your Linux by default. In my Debian 10 it's in the moreutils package. One may think an extra tool is totally excessive, since we can store the output of check_ip in a variable and conditionally reuse it, like in the mentioned answer:

if addr=$(check_ip); then
    echo "$addr" | update_bind9
fi

The condition may be [ -n "$addr" ], whatever; the important thing is we use a variable. Code like the above may solve your problem but in general there are at least three issues with it. Assume command1 | command2 instead of check_ip | update_bind9. The solution is generalized to:

if foo=$(command1); then
    echo "$foo" | command2
fi

And then:

  1. foo=$(command1) will strip all trailing newlines from the output of command1. Then echo "$foo" (or printf … "$foo") will add exactly one newline character (or some fixed number of them, respectively). For this reason command2 may not get the exact output from command1.

  2. command1 may produce output containing NUL character(s). Bash cannot store NUL in a variable (most shells cannot; zsh can). If NUL appears then command2 will not get the exact output from command1.

  3. command1 may produce a huge or even endless stream, so storing it in a variable may not be possible.

Using a file instead of the variable solves all but the last issue. command1 | ifne command2 is immune to all three issues.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .