How to break pipe if stdin is empty?
Asked Answered
K

2

5

I want to break the whole pipe if the stdin is empty. I try to combined xargs -r and tee, which means not print and write if stdin is empty, but it failed

...| upstream commands | xargs -r tee output.txt | downstream commands | ...

Any feedback appreciated.

Knowing answered 27/5, 2021 at 6:10 Comment(1)
Related: Exit when one process in pipe failsDado
D
10

There is no way you can actually terminate a bash pipe conditionally. All commands in a pipeline are started simultaneously. There is however a tool available that would assist you with creating a conditional pipeline. In moreutils you can find the tool ifne which executes a command if and only if the input /dev/stdin is not empty. So you could write something like:

$ command1 | ifne command2 | ifne command3 | ifne command4

Here all commands ifne and command1 are started simultaniously. Only if ifne receives input via /dev/stdin, it will start its respective commandx

Dado answered 27/5, 2021 at 6:52 Comment(0)
E
3

Pipe'll break if command failed. You can add grep in between to achieve this. An example:

$ echo ok | awk '{print $0,"1"}' | awk '{print $0,"2"}' | awk '{print $0,"3"}'
ok 1 2 3

Now add grep:

$ echo ok | grep -Ei '^.+$' | awk '{print $0,"1"}' | awk '{print $0,"2"}' | awk '{print $0,"3"}'
ok 1 2 3

And test empty echo:

$ echo | awk '{print $0,"1"}' | awk '{print $0,"2"}' | awk '{print $0,"3"}'
 1 2 3

$ echo | grep -Ei '^.+$' | awk '{print $0,"1"}' | awk '{print $0,"2"}' | awk '{print $0,"3"}'

Looks like this works but it doesn't, interesting indeed, well then obvy pipes don't fit here, try this approach:

#!/bin/bash
set -x

fun(){
    data=$(echo "$1"); [[ $data ]] && data=$(awk '{print $0,1}' <<< "$data") || return 1; [[ $data ]] && data=$(awk '{print $0,2}' <<< "$data") || return 1; [[ $data ]] && data=$(awk '{print $0,3}' <<< "$data") || return 1; echo "$data"
}

fun ok
fun

Testing:

$ ./test 
+ fun ok
++ echo ok
+ data=ok
+ [[ -n ok ]]
++ awk '{print $0,1}'
+ data='ok 1'
+ [[ -n ok 1 ]]
++ awk '{print $0,2}'
+ data='ok 1 2'
+ [[ -n ok 1 2 ]]
++ awk '{print $0,3}'
+ data='ok 1 2 3'
+ echo 'ok 1 2 3'
ok 1 2 3
+ fun
++ echo ''
+ data=
+ [[ -n '' ]]
+ return 1

More readable variant:

#!/bin/bash
set -x

fun(){
        data=$(echo "$1")
    [[ $data ]] && data=$(awk '{print $0,1}' <<< "$data") || return 1
    [[ $data ]] && data=$(awk '{print $0,2}' <<< "$data") || return 1
    [[ $data ]] && data=$(awk '{print $0,3}' <<< "$data") || return 1
        echo     "$data"
}

fun ok
fun
Emilio answered 27/5, 2021 at 6:33 Comment(2)
This does not break the pipe, it still executes each command. The idea is not to execute the commandsDado
As @Dado said, actually I want to break the following commands if the last stdout is empty, so as to avoid to generated a lot of empty file by tee in a loop.Knowing

© 2022 - 2024 — McMap. All rights reserved.