7

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
  • 719
  • 9
  • 20

5 Answers5

5

Maybe:

yourcommand | grep -qv .

! ( yourcommand |  grep -q ^ )

Or inverse meaning: return false if nothing returned:

yourcommand |  grep -q ^ || echo no output.
F. Hauri
  • 58,205
  • 15
  • 105
  • 122
  • I know about that but I'd like to avoid the negation. It doesn't matter if its grep or something else. – enumag Mar 19 '17 at 07:58
  • Without negation, you have to inverse the meaning of condition. – F. Hauri Mar 19 '17 at 08:00
  • 1
    If the empty line counts as output (such from the simple `echo`) should test the `^` instead of the `.`. e.g. `echo | grep -q ^ || echo no` vs `echo | grep -q . || echo no` – jm666 Mar 19 '17 at 08:09
  • I'll go with `! (mycommand | grep ^)` for now (no -q because I want to see the output). Something without the ! would be prefered though. – enumag Mar 19 '17 at 08:16
  • Then `grep -v ^` though again, that will not display anything. Your obsession with avoiding the `!` seems irrational. – tripleee Mar 19 '17 at 08:19
  • @tripleee Already tried that. Doesn't seem to work with my command for some reason (exit code is 1 on empty result). Not sure why. The ! works fine. – enumag Mar 19 '17 at 08:22
  • It's not obsession. As I said I'll use it unless there is something better. – enumag Mar 19 '17 at 08:24
  • Damn. Turns out I can't use the !. It doesn't work in .travis.yml. – enumag Mar 19 '17 at 08:31
  • Try `bash -c '! yourcommand | grep ^'` maybe? – tripleee Mar 19 '17 at 09:54
  • This doesn't work for an empty line: `printf '\n' | grep -q .` returns 1. – l0b0 Mar 19 '17 at 11:23
  • @l0b0 As remarked above by jm666, the fix is to use `grep ^` – tripleee Mar 19 '17 at 11:44
  • Solved it. ! works in the end, I just didn't realize it was a special character in YAML. When I put the entire line in quotes it works. Thank you all! – enumag Mar 19 '17 at 15:42
  • @tripleee `grep -q ''` same work too, But I can't explain why. – F. Hauri Mar 20 '17 at 06:50
3

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
  • 52,149
  • 24
  • 132
  • 195
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
jm666
  • 58,683
  • 17
  • 101
  • 180
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
  • 158,107
  • 27
  • 234
  • 292
0

Seems that the cleanest way is

mycommand | ( ! grep ^ )
olegrog
  • 111
  • 1
  • 5