8

Let's say I have a bash command mycommand. Sometimes it writes something to output but always return exit code 0. I need use pipe to fix the exit code to be 0 for empty output and something else for non-empty output.

mycommand | ???

I'm not interested in ifs nor custom commands. I want just a simple command like grep with some parameters to check if its input is empty and return the exit code.

Preferably it should print the input if it is not empty but it's not mandatory.

enumag
  • 830
  • 11
  • 21

5 Answers5

7

Maybe:

yourcommand | grep -qv .

! ( yourcommand |  grep -q ^ )

Or inverse meaning: return false if nothing returned:

yourcommand |  grep -q ^ || echo no output.
F. Hauri - Give Up GitHub
  • 64,122
  • 17
  • 116
  • 137
6

wc supports newlines and \0:

$ printf '\n' | wc -c
1
$ printf '\0' | wc -c
1

So this would be a simple POSIX compliant way to do it:

mycommand | [ $(wc -c) -eq 0 ]

Examples:

$ printf '' | [ $(wc -c) -eq 0 ]; echo $?
0
$ printf 'yay' | [ $(wc -c) -eq 0 ]; echo $?
1
$ printf '\n' | [ $(wc -c) -eq 0 ]; echo $?
1
$ printf '\0' | [ $(wc -c) -eq 0 ]; echo $?
1

Alternatives which don't work:

  • grep . doesn't match a solitary newline or \0 as a character:

    $ printf '\n' | grep .; echo $?
    1
    $ printf '\0' | grep .; echo $?
    1
    
  • You can't save \0 in a variable:

    $ a=$'foo\0bar\0baz'
    $ printf '%s' "$a" | wc -c
    3
    
  • Command substitutions remove trailing newlines:

    $ test -z "$(printf '\n')"; echo $?
    0
    
l0b0
  • 55,365
  • 30
  • 138
  • 223
1

Because many different answers - so grep using the ^.

pp() { printf "================\nfor: %s\n" "$@"; }

pp "no input"
printf '' | grep -q ^ ; echo $?
printf '' | grep -q ^ && echo got input || echo no input

pp 'null character'
printf '\0' | grep -q ^ ; echo $?
printf '\0' | grep -q ^ && echo got input || echo no input

pp '\n'
printf '\n' | grep -q ^ ; echo $?
printf '\n' | grep -q ^ && echo got input || echo no input

pp 'some'
printf 'some' | grep -q ^ ; echo $?
printf 'some' | grep -q ^ && echo got input || echo no input

pp 'some\n'
printf 'some\n' | grep -q ^ ; echo $?
printf 'some' | grep -q ^ && echo got input || echo no input

output

================
for: no input
1
no input
================
for: null character
0
got input
================
for: \n
0
got input
================
for: some
0
got input
================
for: some\n
0
got input
clt60
  • 62,119
  • 17
  • 107
  • 194
1

Seems that the cleanest way is

mycommand | ( ! grep ^ )
olegrog
  • 153
  • 2
  • 6
0

A common, though slightly inelegant, workaround is to use a temporary file.

t=$(mktemp -t myscript.XXXXXXXX) || exit
# Use a trap to clean up even if interrupted
trap 'rm -f "$t"' EXIT
trap 'exit' HUP TERM
cat >"$t"
if [ -s "$t" ]; then
    : do stuff with "$t"
fi

This is obviously too complex to use casually on the command line, but sometimes you just have to write a script.

This generalizes nicely to any situation where you need to perform multiple commands on all of the input.

I will note in passing also xargs -r which of course only makes sense if you're piping to xargs already.

tripleee
  • 175,061
  • 34
  • 275
  • 318