GNU bug report logs - #79728
Flag to return 0 if no match

Previous Next

Package: grep;

Reported by: Alejandro Colomar <alx <at> kernel.org>

Date: Thu, 30 Oct 2025 13:44:01 UTC

Severity: normal

To reply to this bug, email your comments to 79728 AT debbugs.gnu.org.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-grep <at> gnu.org:
bug#79728; Package grep. (Thu, 30 Oct 2025 13:44:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Alejandro Colomar <alx <at> kernel.org>:
New bug report received and forwarded. Copy sent to bug-grep <at> gnu.org. (Thu, 30 Oct 2025 13:44:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Alejandro Colomar <alx <at> kernel.org>
To: bug-grep <at> gnu.org
Subject: Flag to return 0 if no match
Date: Thu, 30 Oct 2025 14:42:29 +0100
[Message part 1 (text/plain, inline)]
Hi!

I'm working on a script around grep(1) which calls

	set -Eeuo pipefail;
	trap 'exit 2;' ERR;

	...
	| xargs grep regex;

I'd like to be able to return 2 on any errors, like grep(1) does, which
is why I'm using the trap(1).  However, I need to workaround the fact
that grep(1) returns non-zero for no matches.  What I'm doing at the
moment is

	...
	| {
		xargs grep regex || true;
	};

However, this also hides errors from xargs, which I'd like to keep.
Alternatively, I could use something like

	...
	| while read -r f; do
		grep regex "$f" || true;
	done;

However, the performance would drop to unusable levels (I experienced
a slow down of 100x in a quick test I did), so it's not an option.

Would you mind adding an option to coerce grep(1) to return 0 on zero
matches?


Have a lovely day!
Alex

-- 
<https://www.alejandro-colomar.es>
Use port 80 (that is, <...:80/>).
[signature.asc (application/pgp-signature, inline)]

Information forwarded to bug-grep <at> gnu.org:
bug#79728; Package grep. (Thu, 30 Oct 2025 15:03:02 GMT) Full text and rfc822 format available.

Message #8 received at 79728 <at> debbugs.gnu.org (full text, mbox):

From: "Dale R. Worley" <Dale.Worley <at> comcast.net>
To: Alejandro Colomar <alx <at> kernel.org>
Cc: 79728 <at> debbugs.gnu.org
Subject: Re: bug#79728: Flag to return 0 if no match
Date: Thu, 30 Oct 2025 11:02:33 -0400
Of course,

	xargs grep regex

is tricky to handle because you want grep to produce exit statuses that
xargs will process to produce the exit status you want.

I think this will get the effect you want:

	... | xargs bash -c "grep $regex ; [[ \$? -le 1 ]]'

and it runs grep no more times than "xargs grep" will.

Dale




Information forwarded to bug-grep <at> gnu.org:
bug#79728; Package grep. (Thu, 30 Oct 2025 15:12:02 GMT) Full text and rfc822 format available.

Message #11 received at 79728 <at> debbugs.gnu.org (full text, mbox):

From: Alejandro Colomar <alx <at> kernel.org>
To: "Dale R. Worley" <Dale.Worley <at> comcast.net>
Cc: 79728 <at> debbugs.gnu.org
Subject: Re: bug#79728: Flag to return 0 if no match
Date: Thu, 30 Oct 2025 16:11:08 +0100
[Message part 1 (text/plain, inline)]
Hi Dale,

On Thu, Oct 30, 2025 at 11:02:33AM -0400, Dale R. Worley wrote:
> Of course,
> 
> 	xargs grep regex
> 
> is tricky to handle because you want grep to produce exit statuses that
> xargs will process to produce the exit status you want.
> 
> I think this will get the effect you want:
> 
> 	... | xargs bash -c "grep $regex ; [[ \$? -le 1 ]]'
> 
> and it runs grep no more times than "xargs grep" will.

The problem with something based on

	... | xargs bash -c ...

is that it would make it easy to inject commands in the bash script with
malicioulsy crafted files, right?  It sounds scary.


Have a lovely day!
Alex

> 
> Dale

-- 
<https://www.alejandro-colomar.es>
Use port 80 (that is, <...:80/>).
[signature.asc (application/pgp-signature, inline)]

Information forwarded to bug-grep <at> gnu.org:
bug#79728; Package grep. (Thu, 30 Oct 2025 16:56:02 GMT) Full text and rfc822 format available.

Message #14 received at 79728 <at> debbugs.gnu.org (full text, mbox):

From: Paul Eggert <eggert <at> cs.ucla.edu>
To: Alejandro Colomar <alx <at> kernel.org>
Cc: 79728 <at> debbugs.gnu.org
Subject: Re: bug#79728: Flag to return 0 if no match
Date: Thu, 30 Oct 2025 10:55:28 -0600
On 10/30/25 09:11, Alejandro Colomar via Bug reports for GNU grep wrote:
>> 	... | xargs bash -c "grep $regex ; [[ \$? -le 1 ]]'
>>
>> and it runs grep no more times than "xargs grep" will.
> The problem with something based on
> 
> 	... | xargs bash -c ...
> 
> is that it would make it easy to inject commands in the bash script with
> malicioulsy crafted files, right?

If an attacker controls the regex you're already in trouble, because the 
regex can be arbitrarily slow.

That being said, to avoid the regex being interpreted as shell code, you 
can use something like this:

  xargs sh -c 'grep -e "$0" -- "$@"; [ $? -le 1 ]' "$regex"

Admittedly a bit awkward, but it works now and it's portable to any 
POSIX platform.

If this awkwardness is to be simplified it should be a patch to GNU 
xargs not to grep, as programs like diff and cmp behave like grep and 
it's not reasonable to add options to them all merely to work around an 
xargs awkwardness.




Information forwarded to bug-grep <at> gnu.org:
bug#79728; Package grep. (Thu, 30 Oct 2025 17:24:02 GMT) Full text and rfc822 format available.

Message #17 received at 79728 <at> debbugs.gnu.org (full text, mbox):

From: Collin Funk <collin.funk1 <at> gmail.com>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: 79728 <at> debbugs.gnu.org, Alejandro Colomar <alx <at> kernel.org>
Subject: Re: bug#79728: Flag to return 0 if no match
Date: Thu, 30 Oct 2025 10:23:32 -0700
Paul Eggert <eggert <at> cs.ucla.edu> writes:

> On 10/30/25 09:11, Alejandro Colomar via Bug reports for GNU grep wrote:
>>> 	... | xargs bash -c "grep $regex ; [[ \$? -le 1 ]]'
>>>
>>> and it runs grep no more times than "xargs grep" will.
>> The problem with something based on
>> 	... | xargs bash -c ...
>> is that it would make it easy to inject commands in the bash script
>> with
>> malicioulsy crafted files, right?
>
> If an attacker controls the regex you're already in trouble, because
> the regex can be arbitrarily slow.

Or exhaust your systems memory in the case of:

    $ grep -E 'a+++++++++++++++++++++++++++++++++++++++' COPYING

Among many others. :)

Collin




Information forwarded to bug-grep <at> gnu.org:
bug#79728; Package grep. (Thu, 30 Oct 2025 17:54:02 GMT) Full text and rfc822 format available.

Message #20 received at 79728 <at> debbugs.gnu.org (full text, mbox):

From: Alejandro Colomar <alx <at> kernel.org>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: 79728 <at> debbugs.gnu.org
Subject: Re: bug#79728: Flag to return 0 if no match
Date: Thu, 30 Oct 2025 18:53:19 +0100
[Message part 1 (text/plain, inline)]
Hi Paul,

On Thu, Oct 30, 2025 at 10:55:28AM -0600, Paul Eggert wrote:
> On 10/30/25 09:11, Alejandro Colomar via Bug reports for GNU grep wrote:
> > > 	... | xargs bash -c "grep $regex ; [[ \$? -le 1 ]]'
> > > 
> > > and it runs grep no more times than "xargs grep" will.
> > The problem with something based on
> > 
> > 	... | xargs bash -c ...
> > 
> > is that it would make it easy to inject commands in the bash script with
> > malicioulsy crafted files, right?
> 
> If an attacker controls the regex you're already in trouble, because the
> regex can be arbitrarily slow.

I'm not too worried about DoS.  I was worried about remote code
execution.  As in, I'll try to search for something in an arbitrary
directory, possibly controlled by a malicious actor, and file names or
contents could result in giving them control of my computer.

> That being said, to avoid the regex being interpreted as shell code, you can
> use something like this:
> 
>   xargs sh -c 'grep -e "$0" -- "$@"; [ $? -le 1 ]' "$regex"

Hmmm, it seems like what I want.  Testing seems to work:

	alx <at> devuan:~/tmp$ cat script 
	#!/bin/bash

	set -Eeuo pipefail

	echo foo bar baz | xargs sh -c 'echo "$0" -- "$@"; test $? -le 1;' "regex";
	alx <at> devuan:~/tmp$ ./script 
	regex -- foo bar baz

> 
> Admittedly a bit awkward, but it works now and it's portable to any POSIX
> platform.

This sounds quite robust; thanks!

> If this awkwardness is to be simplified it should be a patch to GNU xargs
> not to grep, as programs like diff and cmp behave like grep and it's not
> reasonable to add options to them all merely to work around an xargs
> awkwardness.

Makes sense; thanks!  But I think I like your approach above; I don't
need simplifying it.


Have a lovely night!
Alex

-- 
<https://www.alejandro-colomar.es>
Use port 80 (that is, <...:80/>).
[signature.asc (application/pgp-signature, inline)]

Information forwarded to bug-grep <at> gnu.org:
bug#79728; Package grep. (Fri, 31 Oct 2025 00:49:02 GMT) Full text and rfc822 format available.

Message #23 received at 79728 <at> debbugs.gnu.org (full text, mbox):

From: Alejandro Colomar <alx <at> kernel.org>
To: Paul Eggert <eggert <at> cs.ucla.edu>
Cc: 79728 <at> debbugs.gnu.org, chet.ramey <at> case.edu
Subject: Re: bug#79728: Flag to return 0 if no match
Date: Fri, 31 Oct 2025 01:48:10 +0100
[Message part 1 (text/plain, inline)]
[CC += Chet]

Hi Paul, Chet,

On Thu, Oct 30, 2025 at 06:53:22PM +0100, Alejandro Colomar wrote:
> > That being said, to avoid the regex being interpreted as shell code, you can
> > use something like this:
> > 
> >   xargs sh -c 'grep -e "$0" -- "$@"; [ $? -le 1 ]' "$regex"

I've applied the following commit:

	commit 243666cfbab9d0bdb26424f1ba9cae8e5571cf68
	Author: Alejandro Colomar <alx <at> kernel.org>
	Date:   Fri Oct 31 00:58:42 2025 +0100

	    src/bin/grepc: Handle xargs(1) errors properly
	    
	    'xargs grep' is hard, because grep(1) exits with a status of 1 for no
	    match, which is not an error, but xargs(1) takes that as an error.
	    
	    This trick with sh(1) was suggested by Paul, and nicely solves this
	    problem.
	    
	    Link: <https://lists.gnu.org/archive/html/bug-grep/2025-10/msg00029.html>
	    Suggested-by: Paul Eggert <eggert <at> cs.ucla.edu>
	    Signed-off-by: Alejandro Colomar <alx <at> kernel.org>

	diff --git a/src/bin/grepc b/src/bin/grepc
	index 9f6de55d7..300a3b6c9 100755
	--- a/src/bin/grepc
	+++ b/src/bin/grepc
	@@ -238,18 +238,16 @@ opts=($A $B $C $c $h $i $l -M $m $n);
	 
	 
	 if test -z "$*"; then
	-       pcre2grep "${opts[@]}" -f "$patterns" || true;
	+       pcre2grep "${opts[@]}" -f "$patterns" || test $? -eq 1;
	 else
		find "$@" -type f -print0 \
		| if test -z "$c"; then
			# shellcheck disable=SC2248  # $i may be -i or nothing.
	-               xargs -0 grep -lZPI $i -- "$identifier" || true;
	+               xargs -0 sh -c 'grep -lZPI $i -e "$0" -- "$@" || test $? -eq 1;' "$identifier";
		else
			cat;
		fi \
	-       | {
	-               xargs -0 pcre2grep "${opts[@]}" -f "$patterns" || true;
	-       };
	+       | xargs -0 sh -c 'pcre2grep '"${opts[*]}"' -f "$0" -- "$@" || test $? -eq 1;' "$patterns";
	 fi \
	 | if test "$r" = 'yes'; then
		perl -p -e 's/('"$identifier"')/\033[32m\1\033[0m/';

I've also had an idea for trying to keep the return value of 1 when
there are no matches.  I could send a USR1 signal from grep(1).

I've written this proof-of-concept:

	alx <at> devuan:~/tmp/g$ cat script2 
	#!/bin/bash

	set -Eeuo pipefail;
	shopt -s lastpipe;

	trap 'exit 2;' ERR;
	trap 'exit 1;' USR1;

	find "$@" -type f \
	| xargs bash -c 'grep -e "$0" -- "$@" || { test $? -eq 1 && kill -USR1 '"$$"'; };' 'foo';

However, if find(1) fails, grep(1) still sends this signal, and that
seems to have preference over the ERR trap.

	$ ./script2 nonexistent; echo $?
	find: ‘nonexistent’: No such file or directory
	1

Do you know of any way to achieve that?  I'd like to avoid sending other
signals from find, as the actual script is much more complex, and
sending signals from every command in the pipeline would be tedious (and
likely error prone).


Have a lovely night!
Alex

-- 
<https://www.alejandro-colomar.es>
Use port 80 (that is, <...:80/>).
[signature.asc (application/pgp-signature, inline)]

This bug report was last modified 5 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.