372

How can I test if a command outputs an empty string?

tshepang
  • 12,111
  • 21
  • 91
  • 136
barp
  • 6,489
  • 9
  • 30
  • 37

13 Answers13

438

Previously, the question asked how to check whether there are files in a directory. The following code achieves that, but see rsp's answer for a better solution.


Empty output

Commands don’t return values – they output them. You can capture this output by using command substitution; e.g. $(ls -A). You can test for a non-empty string in Bash like this:

if [[ $(ls -A) ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Note that I've used -A rather than -a, since it omits the symbolic current (.) and parent (..) directory entries.

Note: As pointed out in the comments, command substitution doesn't capture trailing newlines. Therefore, if the command outputs only newlines, the substitution will capture nothing and the test will return false. While very unlikely, this is possible in the above example, since a single newline is a valid filename! More information in this answer.


Exit code

If you want to check that the command completed successfully, you can inspect $?, which contains the exit code of the last command (zero for success, non-zero for failure). For example:

files=$(ls -A)
if [[ $? != 0 ]]; then
    echo "Command failed."
elif [[ $files ]]; then
    echo "Files found."
else
    echo "No files found."
fi

More info here.

Community
  • 1
  • 1
Will Vousden
  • 32,488
  • 9
  • 84
  • 95
129

TL;DR

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then ...; fi

Thanks to netj for a suggestion to improve my original:
if [[ $(ls -A | wc -c) -ne 0 ]]; then ...; fi


This is an old question but I see at least two things that need some improvement or at least some clarification.

First problem

First problem I see is that most of the examples provided here simply don't work. They use the ls -al and ls -Al commands - both of which output non-empty strings in empty directories. Those examples always report that there are files even when there are none.

For that reason you should use just ls -A - Why would anyone want to use the -l switch which means "use a long listing format" when all you want is test if there is any output or not, anyway?

So most of the answers here are simply incorrect.

Second problem

The second problem is that while some answers work fine (those that don't use ls -al or ls -Al but ls -A instead) they all do something like this:

  1. run a command
  2. buffer its entire output in RAM
  3. convert the output into a huge single-line string
  4. compare that string to an empty string

What I would suggest doing instead would be:

  1. run a command
  2. count the characters in its output without storing them
    • or even better - count the number of maximally 1 character using head -c1
      (thanks to netj for posting this idea in the comments below)
  3. compare that number with zero

So for example, instead of:

if [[ $(ls -A) ]]

I would use:

if [[ $(ls -A | wc -c) -ne 0 ]]
# or:
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]

Instead of:

if [ -z "$(ls -lA)" ]

I would use:

if [ $(ls -lA | wc -c) -eq 0 ]
# or:
if [ $(ls -lA | head -c1 | wc -c) -eq 0 ]

and so on.

For small outputs it may not be a problem but for larger outputs the difference may be significant:

$ time [ -z "$(seq 1 10000000)" ]

real    0m2.703s
user    0m2.485s
sys 0m0.347s

Compare it with:

$ time [ $(seq 1 10000000 | wc -c) -eq 0 ]

real    0m0.128s
user    0m0.081s
sys 0m0.105s

And even better:

$ time [ $(seq 1 10000000 | head -c1 | wc -c) -eq 0 ]

real    0m0.004s
user    0m0.000s
sys 0m0.007s

Full example

Updated example from the answer by Will Vousden:

if [[ $(ls -A | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Updated again after suggestions by netj:

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Additional update by jakeonfire:

grep will exit with a failure if there is no match. We can take advantage of this to simplify the syntax slightly:

if ls -A | head -c1 | grep -E '.'; then
    echo "there are files"
fi

if ! ls -A | head -c1 | grep -E '.'; then
    echo "no files found"
fi

Discarding whitespace

If the command that you're testing could output some whitespace that you want to treat as an empty string, then instead of:

| wc -c

you could use:

| tr -d ' \n\r\t ' | wc -c

or with head -c1:

| tr -d ' \n\r\t ' | head -c1 | wc -c

or something like that.

Summary

  1. First, use a command that works.

  2. Second, avoid unnecessary storing in RAM and processing of potentially huge data.

The answer didn't specify that the output is always small so a possibility of large output needs to be considered as well.

jakeonfire
  • 75
  • 4
rsp
  • 107,747
  • 29
  • 201
  • 177
  • 5
    `[[ $(... | head -c | wc -c) -gt 0 ]]` or `[[ -n $(... | head -c1) ]]` are better because `wc -c` would have to consume the entire output of the command. – netj Feb 18 '17 at 06:37
  • @netj This is a very good idea. See my updated answer - I added your suggestion. Thanks a lot! – rsp May 10 '17 at 09:01
  • Any ideas on how to get a hold of the output? – fahrradflucht May 28 '18 at 10:29
  • I think the original (without head) is better in the general case. It's not always a good idea to kill the command as soon as it starts writing output! – stk Feb 03 '19 at 14:44
  • @stk Most programs will exit safely after receiving a kill signal. – cambunctious Mar 05 '20 at 23:12
53
if [ -z "$(ls -lA)" ]; then
  echo "no files found"
else
  echo "There are files"
fi

This will run the command and check whether the returned output (string) has a zero length. You might want to check the 'test' manual pages for other flags.

Use the "" around the argument that is being checked, otherwise empty results will result in a syntax error as there is no second argument (to check) given!

Note: that ls -la always returns . and .. so using that will not work, see ls manual pages. Furthermore, while this might seem convenient and easy, I suppose it will break easily. Writing a small script/application that returns 0 or 1 depending on the result is much more reliable!

Veger
  • 37,240
  • 11
  • 105
  • 116
36

For those who want an elegant, bash version-independent solution (in fact should work in other modern shells) and those who love to use one-liners for quick tasks. Here we go!

ls | grep . && echo 'files found' || echo 'files not found'

(note as one of the comments mentioned, ls -al and in fact, just -l and -a will all return something, so in my answer I use simple ls

Alex
  • 627
  • 8
  • 15
  • 8
    If you use `grep -q ^` instead, newline characters will also be matched, and grep won't print anything to the standard output. Furthermore, it will exit as soon as it receives any input, rather than waiting for its input stream to end. – mortehu Jun 12 '15 at 18:32
  • Should start with `ls -A` if you want to include dot-files (or directories) – bill.lee Feb 18 '20 at 17:07
23

Bash Reference Manual

6.4 Bash Conditional Expressions

-z string
     True if the length of string is zero.

-n string
string
     True if the length of string is non-zero.

You can use shorthand version:

if [[ $(ls -A) ]]; then
  echo "there are files"
else
  echo "no files found"
fi
user
  • 23,260
  • 9
  • 113
  • 101
  • 1
    There is a -z or -n missing in parenthessis [[ ... ]]. – 71GA Jan 06 '15 at 09:17
  • 6
    @71GA: Perhaps that is easy to overlook, but if you'll look carefully, you'll see that just `string` is a synonym for `-n string`. – user Jan 09 '15 at 19:13
17

As Jon Lin commented, ls -al will always output (for . and ..). You want ls -Al to avoid these two directories.

You could for example put the output of the command into a shell variable:

v=$(ls -Al)

An older, non-nestable, notation is

v=`ls -Al`

but I prefer the nestable notation $( ... )

The you can test if that variable is non empty

if [ -n "$v" ]; then
    echo there are files
else
    echo no files
fi

And you could combine both as if [ -n "$(ls -Al)" ]; then

Sometimes, ls may be some shell alias. You might prefer to use $(/bin/ls -Al). See ls(1) and hier(7) and environ(7) and your ~/.bashrc (if your shell is GNU bash; my interactive shell is zsh, defined in /etc/passwd - see passwd(5) and chsh(1)).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
7

I'm guessing you want the output of the ls -al command, so in bash, you'd have something like:

LS=`ls -la`

if [ -n "$LS" ]; then
  echo "there are files"
else
  echo "no files found"
fi
Jon Lin
  • 142,182
  • 29
  • 220
  • 220
  • This doesn't work: the command `ls` is never being executed. You only check whether the _expansion of the variable_ `LS` is non-empty. – gniourf_gniourf May 10 '17 at 09:31
  • 2
    @gniourf_gniourf - what makes you think that? The backtick incantation is admittedly not as pretty or flexible as $( ), but it works ... – tink Sep 21 '18 at 01:49
  • The `-a` switch will never return no content (i.e., it will return content whether there are files or not) since there is always the implicit `.` and `..` directories present. – bill.lee Feb 18 '20 at 17:09
4

sometimes "something" may come not to stdout but to the stderr of the testing application, so here is the fix working more universal way:

if [[ $(partprobe ${1} 2>&1 | wc -c) -ne 0 ]]; then
    echo "require fixing GPT parititioning"
else
    echo "no GPT fix necessary"
fi
Oleg Kokorin
  • 2,288
  • 2
  • 16
  • 28
3

Here's a solution for more extreme cases:

if [ `command | head -c1 | wc -c` -gt 0 ]; then ...; fi

This will work

  • for all Bourne shells;
  • if the command output is all zeroes;
  • efficiently regardless of output size;

however,

  • the command or its subprocesses will be killed once anything is output.
Curt
  • 470
  • 3
  • 7
1

All the answers given so far deal with commands that terminate and output a non-empty string.

Most are broken in the following senses:

  • They don't deal properly with commands outputting only newlines;
  • starting from Bash≥4.4 most will spam standard error if the command output null bytes (as they use command substitution);
  • most will slurp the full output stream, so will wait until the command terminates before answering. Some commands never terminate (try, e.g., yes).

So to fix all these issues, and to answer the following question efficiently,

How can I test if a command outputs an empty string?

you can use:

if read -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

You may also add some timeout so as to test whether a command outputs a non-empty string within a given time, using read's -t option. E.g., for a 2.5 seconds timeout:

if read -t2.5 -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

Remark. If you think you need to determine whether a command outputs a non-empty string, you very likely have an XY problem.

gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
  • I think this answer is rather under-appreciated. I was performance-testing some of the solutions here using 100 iterations for each of 3 input scenarios (`seq 1 10000000`, `seq 1 20`, and `printf""`). The only matchup this read approach didn't win was with the `-z "$(command)"` approach for an empty input. Even then, it only loses out by around 10-15%. They seem to break even around `seq 1 10`. Regardless, the `-z "$(command)"` approach becomes *so* slow as input size grows that I'd avoid using it for any variable-size input. – abathur Nov 02 '19 at 19:38
1

Here's an alternative approach that writes the std-out and std-err of some command a temporary file, and then checks to see if that file is empty. A benefit of this approach is that it captures both outputs, and does not use sub-shells or pipes. These latter aspects are important because they can interfere with trapping bash exit handling (e.g. here)

tmpfile=$(mktemp)
some-command  &> "$tmpfile"
if [[ $? != 0 ]]; then
    echo "Command failed"
elif [[ -s "$tmpfile" ]]; then
    echo "Command generated output"
else
    echo "Command has no output"
fi
rm -f "$tmpfile"
Darren Smith
  • 2,261
  • 16
  • 16
0

Sometimes you want to save the output, if it's non-empty, to pass it to another command. If so, you could use something like

list=`grep -l "MY_DESIRED_STRING" *.log `
if [ $? -eq 0 ]
then
    /bin/rm $list
fi

This way, the rm command won't hang if the list is empty.

Scott C Wilson
  • 19,102
  • 10
  • 61
  • 83
0

As mentioned by tripleee in the question comments , use moreutils ifne (if input not empty).

In this case we want ifne -n which negates the test:

ls -A /tmp/empty | ifne -n command-to-run-if-empty-input

The advantage of this over many of the another answers when the output of the initial command is non-empty. ifne will start writing it to STDOUT straight away, rather than buffering the entire output then writing it later, which is important if the initial output is slowly generated or extremely long and would overflow the maximum length of a shell variable.

There are a few utils in moreutils that arguably should be in coreutils -- they're worth checking out if you spend a lot of time living in a shell.

In particular interest to the OP may be dirempty/exists tool which at the time of writing is still under consideration, and has been for some time (it could probably use a bump).

Tom Hale
  • 40,825
  • 36
  • 187
  • 242