bugfindutils - Bugs: bug #62043, The xargs -r, --no-run-if-empty...

 
 

bug #62043: The xargs -r, --no-run-if-empty Option Is Ignored When a Delimiter Is Passed

Submitter:  Paul Eggert <eggert>
Submitted:  Sun 13 Feb 2022 05:57:51 AM UTC
   
 
Category:  xargs Severity:  3 - Normal
Item Group:  None Status:  Working as Intended
Privacy:  Public Assigned to:  berny
Originator Name:  Kurt von Laven Open/Closed:  Closed
Release:  None Fixed Release:  None
* Mandatory Fields

Add a New Comment Rich Markup
   

Jump to the original submission

Sun 24 Apr 2022 12:13:10 PM UTC, comment #8: 

Thanks for confirming.  I'm hereby closing this issue.

Bernhard Voelker <berny>
Group administrator
Thu 24 Feb 2022 06:51:32 AM UTC, comment #7: 

comment #5: by Jay

> comment #4: Andreas writes:
>> OTOH I am trying to fix the documentation of xargs'
>> inconsistent behavior, imho these two commands should produce
>> identical output both for consistency reasons and according to
>> the documentation but do not:
>> printf '\n' | xargs -r echo foo
>> printf '\0' | xargs -0 echo foo
>
> These are not supposed to behave similarly.  In its default
> mode (without -d or -0), xargs does whitespace and quote
> processing.

[...]

> You're right though; if the documentation does not make this
> clear, we should fix the documentation.


I think I get it now. The major relevant difference (apart from quoting) between -0/-d and default is:

  1. default: args are separated by whitespace character*s* (plus leading/trailing whitespace is ignored)
  2. -0/-d: args are separated by each single instance of NUL or -d's arg.

From which follows that it is impossible to pass empty arguments in default mode (without quoting). - Multiple adjacent whitespace characters (including \n) are collapsed so one ends up with the equivalent of totally empty input.

cu Andreas

Andreas Metzler <ametzler>
Wed 23 Feb 2022 06:03:28 PM UTC, comment #6: 

comment #4:
[...]

> OTOH I am trying to fix the documentation of xargs' inconsistent behavior, imho these two commands should produce identical output both for consistency reasons and according to the documentation but do not:
> printf '\n' | xargs -r echo foo
> printf '\0' | xargs -r -0 echo foo

[...]

(I've added -r to the second which I assume is what you intended)

No they should not, in the first case, the input contains no record, because without -d/-r, any sequence of unquoted blanks or newlines act as separator and unquoted leading or trailing blanks or newline are ignored.

Same in


printf '\n\n\t\t \n \n' | xargs -r echo foo


In the second, which is the same as:


printf '\0' | xargs -r -d '\0' echo foo


input is interpreted as NUL delimited records (and there's no quote processing), so the input contains one such record which happens to be empty. So echo is called with "foo" and an empty argument. Which on Linux you can see with:


$ printf '\0' | strace -qqfe execve -e signal='!all' xargs -r -d '\0' echo foo | sed -n l
execve("/usr/bin/xargs", ["xargs", "-r", "-d", "\\0", "echo", "foo"], 0x7ffdac4335c8 /* 40 vars */) = 0
[pid 1181116] execve("/usr/bin/echo", ["echo", "foo", ""], 0x7ffec72add10 /* 40 vars */) = 0
foo $


In:


printf '' | xargs -d '\0' echo foo


echo is invoked with foo but no extra argument.


printf '\n' | xargs -r -d '\n' echo foo
printf '\0' | xargs -r -0 echo foo
printf ' \n\n "" \t\t\n\n\n' | xargs -r echo foo


Should give the same output and do.

My suggested wording should hopefully clarify things, bearing in mind that how the input is broken into the records that make up the arguments to be passed to the command is documented elsewhere.

Stephane Chazelas <stephanechazelas>
Wed 23 Feb 2022 05:59:53 PM UTC, comment #5: 

Andreas writes:

OTOH I am trying to fix the documentation of xargs' inconsistent behavior, imho these two commands should produce identical output both for consistency reasons and according to the documentation but do not:
printf '\n' | xargs -r echo foo
printf '\0' | xargs -0 echo foo

These are not supposed to behave similarly.  In its default mode (without -d or -0), xargs does whitespace and quote processing.  Like so:

horizon:~$ printf 'one "item two is long" three' | xargs   -n1 echo foo
foo one
foo item two is long
foo three
horizon:~$ printf 'one "item two is long" three' | xargs   -n1  -d '\n' echo foo
foo one "item two is long" three

So the input "\n" has contains one (empty) arg with -d "\n" and contains no args in the default mode, and contains one arg (a newline) with -0.

You're right though; if the documentation does not make this clear, we should fix the documentation.



James Youngman <jay>
Group administrator
Wed 23 Feb 2022 05:42:53 PM UTC, comment #4: 

comment #3: by Stephane Chazelas

> The point is that with -d '\n' an input that contains one
> newline character has one empty record, and one with two
> newlines has 2 empty records and so on.


> With -0, an input with one newline character optionally
> followed by a NUL has one record made of one newline
> character, an input with one NUL has one empty record
> and so on.


> With -0/-d, only an empty input has no record.


After reading your comment thrice I have noted the misunderstanding, you are trying to explain the behavior on newline characters in the input when -0 is set.

OTOH I am trying to fix the documentation of xargs' inconsistent behavior, imho these two commands should produce identical output both for consistency reasons and according to the documentation but do not:
printf '\n' | xargs -r echo foo
printf '\0' | xargs -0 echo foo

cu Andreas

Andreas Metzler <ametzler>
Wed 23 Feb 2022 05:03:47 PM UTC, comment #3: 

The point is that with -d '\n' an input that contains one newline character has one empty record, and one with two newlines has 2 empty records and so on.

With -0, an input with one newline character optionally followed by a NUL has one record made of one newline character, an input with one NUL has one empty record and so on.

With -0/-d, only an empty input has no record.

With neither -0/-d, an input like [newline][space][tab][newline]... has no record, an input like '' ""  '' has 3 empty records.

So maybe a better wording could be:

-r: if the input doesn't contain any record/argument, do not run the command once with no argument as is otherwise done by default for standard compliance.

I would not mention standard input, as with -a, the input comes from a file argument.

Note that with NetBSD xargs, the -r behaviour is the default (which makes it non-POSIX-compliant).

Stephane Chazelas <stephanechazelas>
Sun 13 Feb 2022 03:31:00 PM UTC, comment #2: 

How about the following  change:



diff --git a/xargs/xargs.1 b/xargs/xargs.1
index f743e60c..d0d54bfa 100644
--- a/xargs/xargs.1
+++ b/xargs/xargs.1
@@ -240,8 +240,12 @@ once child processes exit.  This can be used in a rudimentary load
 distribution scheme, for example.
 .TP
 .B \-r, \-\-no\-run\-if\-empty
-If the standard input does not contain any nonblanks, do not run the
+If the standard input is empty, do not run the
 command.  Normally, the command is run once even if there is no input.
+If neither of the options
+.B \-0 \-\-null \-\-delimiter \-d
+is used the command will also not be run if the input nonempty but consists of
+blanks.
 This option is a GNU extension.
 .TP
 .BI -s " max-chars\fR, \fI" \-\-max\-chars "=\fImax-chars\fR"


cu Andreas

Andreas Metzler <ametzler>
Sun 13 Feb 2022 12:48:53 PM UTC, comment #1: 

This a duplicate of bug #61356

cu Andreas

Andreas Metzler <ametzler>
Sun 13 Feb 2022 05:57:51 AM UTC, original submission:  

I am filing this bug on behalf of Kurt von Laven, who mistakenly filed it as Coreutils Bug 48329. The rest of this bug report is from Kurt.


Hello,

When the -r, --no-run-if-empty option is combined with the -d, --delimiter
or -0, --null options, the -r, --no-run-if-empty option does not
take effect. The manual describes the intended behavior of the -r,
--no-run-if-empty option: "If the standard input does not contain any
nonblanks, do not run the command.  Normally, the command is run once even
if there is no input.  This option is a GNU extension."

The following command correctly outputs nothing.
xargs --no-run-if-empty -I str echo str <<< ""

The following command incorrectly outputs 1 empty line.
xargs --no-run-if-empty --delimiter="\n" -I str echo str <<< ""

The following command incorrectly outputs 2 empty lines.
xargs --no-run-if-empty --null -I str echo str <<< ""

As a workaround according to this Stack Overflow answer
<https://stackoverflow.com/a/17287097/3866835>, the following commands
output nothing when the input consists only of whitespace.
[[ $input = *[![:space:]]* ]] && xargs --delimiter="\n" -I str echo str <<<
"$input"
[[ $input = *[![:space:]]* ]] || xargs --null -I str echo str <<< "$input"

Be well,
Kurt von Laven


Paul Eggert <eggert>

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach Files:
   
   
Comment:
   

No files currently attached

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -email is unavailable- added by berny (Posted a comment)
  • -email is unavailable- added by jay (Posted a comment)
  • -email is unavailable- added by stephanechazelas (Posted a comment)
  • -email is unavailable- added by eggert (Submitted the item)
  • -email is unavailable- added by eggert
  •  

    There are 0 votes so far. Votes easily highlight which items people would like to see resolved in priority, independently of the priority of the item set by tracker managers.

    Only logged-in users can vote.

     

    Follow 4 latest changes.

    Date Changed by Updated Field Previous Value => Replaced by
    2022-04-24 berny StatusNone Working as Intended
        Assigned toNone berny
        Open/ClosedOpen Closed
    2022-02-13 eggert Carbon-Copy- Added -email is unavailable-

    Back to the top

    Powered by Savane 3.13-caa5.
    Corresponding source code