fd: Thoughts on non-zero exit code for no matches?

grep returns a non-zero exit code when no matches are found, making it easy to include in shell scripts and if conditions directly without having to shell out to test to analyze the results.

What are your thoughts on having fd do the same so that fd not-exists and fd exists have differing exit codes that could be directly checked?

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 5
  • Comments: 26 (15 by maintainers)

Most upvoted comments

Thanks, everyone!

I think I’m now leaning towards using -q/--quiet with an additional hidden alias (either --has-results or --has-match, no real preference here).

Any final objections? Preferences for either “has-results” or “has-match”?

Ok, let’s try to implement this. I like --test. We can probably reuse the --max-results functionality and run a --max-results=1 search. Instead of printing anything to the console, we would simply return 0 or 1, depending on whether we have at least one result or not.

if fd --test pattern; then
  ...
fi

What do you think @ChrisPenner @mqudsi @vn971?

In particular, the decision not to print anything? Otherwise, we would have to use > /dev/null in scripts. And we already have fd -1 pattern to print the first search result and quit.

Thank you for the feedback!

Interesting thought. However, I’m not sure that it’s a good idea to implement it this way. find always returns 0 if it finished the search without any errors, even if there are no results.

The idea is that you can use the exit code to tell whether or not you can rely on the search results. If the exit code is non-zero, there might be folders/files which were not searched (due to permission issues, for example). I believe we should probably do the same with fd (we currently silently ignore these).

Coming back to your use case, I agree that it would be nice to use fd this way in shell scripts, but I guess there are some rather simple workarounds that would achieve the same, for example

fd ... |  grep .

Just chiming in that I’d also appreciate a flag for setting exit status 😃

Sure, the given alternatives work; but I’d need to add a comment to have any faith that anyone would understand why. If I saw something like fd something | head -c 1 | grep . 1>/dev/null I’d have no clue what the intent was; whereas if I saw:

if fd --set-exit-status 'pattern'; then
  ...
fi

Then the intent is clear and my coworkers won’t get confused 😄

Alternative names for the option could be --test or --match

Sure thing!

Really? I would expect a --quiet option to do exactly the same, but without the output. The option that is being proposed here would change the behavior (only search for the first result, then quit).

My argument for -q and why I think it works despite what the responses to that were before is that even if it changes the behavior of fd (i.e. it exits after the first match), the observable (non-error) behavior of fd does not change. If fd -q returns after one result or after one thousand, the user-observable state (output + status code) does not change - only how quickly that result is rendered changes. The user can’t know how long the result should take (caches, ignored directories, etc) and in all cases, even if they can expect “slow” or “fast” depending on the number of results, it’s a non-deterministic factor. That’s all to say if you agree with -q except you feel that it shouldn’t change the behavior of fd, then my counter is that it doesn’t change the user observable behavior, only the internal logic, which is within its right.

It’s unfortunate that the status code depends on whether or not -q is specified (instead of always terminating with a non-zero status code if no results were found, a la grep and co and just documenting that as a breaking change), but I think that documenting the exit code change with -q is still preferable to --has-foo or whatever because it doesn’t break with unix convention and is more discoverable. I know you mentioned find doesn’t change its exit code - to me that is bizarre because I would have intuitively expected find to behave like grep (I mean, it’s literally an imperative command “sudo, find these files!” so if it doesn’t have any results to return, you’d expect it to let you know). In all cases, fd isn’t by any means a rewrite of find (it doesn’t support the same parameters) so I think it’s not the end of the world to break with in this regard as well, but as I mentioned before, I’m not going to pick this hill to die on.

fwiw, this is the rationale we use in https://github.com/fish-shell/fish-shell for using -q to abort on first match for all our first-party builtins and functions and it’s the behavior that pretty much all the other (maintained) shells and greybeard utilities are using.

(this is all aside from fd -q just being plain shorter and easier to work with and there generally being no question of whether or not -q should be a shortcode for --quiet)

I believe that -q/--quiet is “the norm” for this sort of behavior (no output + early termination if possible),

Really? I would expect a --quiet option to do exactly the same, but without the output. The option that is being proposed here would change the behavior (only search for the first result, then quit).

--test doesn’t really imply anything that one could intuit as being suppression of output.

that’s true, but it would be used to test if a search has a match. Another possibility would be --check or --has-match ….

I still feel that in addition to/apart from any “fast eval” mode, the standard exit code should also reflect the match/no match status to match standard unix tools like grep and find.

I argued above that we don’t want to do this because it would not reflect what find does. find does return 0 if there are no matches (and no filesystem errors have occurred). We don’t necessarily have to be consistent with find, but in this case, I think we should aim to be.

In the meantime (since writing the comments above) there have been some new developments:

We now have a --max-results=<count> option and a -1 alias for --max-results=1 that can be used to exit early after finding the first result(s). This is even faster than the early exit via fd … | head -n1 because the latter is actually running until the second result.

we can list the flag in --help and the man page, which can lead to people getting their job done faster. After all, it is a common requirement to test for emptiness.

Ok, agreed. But I would assume that this use case (testing for existence of search results) is something that usually comes up in a script, not in the interactive use of a terminal. Therefore, I don’t think it’s too bad if there is no super-short way to achieve that result is found.

there’s a tiny overhead in calling 2 extra binaries piped together, even though that’s more aesthetics than practical concern (for most people?)

As you said, I think that’s negligible compared to the IO overhead (accessing the disk / cache) to get the search results.

  • it’s more readable: fd --quiet something VS fd something | head -c 1 | grep . 1>/dev/null

Fair point. I think there are slightly more readable alternatives though:

if [[ $(fd -1 …) ]]; then
  echo "found something!"
else
  echo "did not find anything"
fi
  • not everybody is a pro at bash (who is, anyway?), it’s nice when you can get the job done faster and simpler

Ok. Let’s reopen this ticket and see what others think.

Fair enough.

It does occur to me that a better option here (if one were to want to improve this) might be to add a --has-results or similar flag that would use the 0/1 return code, which has the added benefit of allowing fd to terminate early when a match is found.