bug-findutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: The xargs -r, --no-run-if-empty Option Is Ignored When a Delimiter I


From: Bernhard Voelker
Subject: Re: The xargs -r, --no-run-if-empty Option Is Ignored When a Delimiter Is Passed
Date: Fri, 14 May 2021 20:53:38 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Thunderbird/78.10.0

On 5/11/21 3:58 AM, Kurt von Laven wrote:
> 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 <<< ""

First of all, let's check what the shell syntax '<<<' produces for an
empty string as argument:

  $ od -tx1z <<< ""
  0000000 0a                                               >.<
  0000001

Aha, the shell apparently turns the <<<"" into nothing, but finally outputs a 
newline
character.  My interpretation of this is that the shell does this for POSIX 
compatibility:
the receiving tool is supposed to work with text files ... which by definition 
have
a terminating newline character.
Also wc(1) tells us that there was 1 line but without a word:

  $ wc  <<< ""
      1       0       1

Now let's see what is happening in xargs(1) - using the 'strace' tool:

  $ strace -vfe read,write xargs --no-run-if-empty --null -I str echo str <<< 
"" >/dev/null
  read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`|\2\0\0\0\0\0"..., 
832) = 832
  read(0, "\n", 4096)                     = 1
  read(0, "", 4096)                       = 0
  strace: Process 31530 attached
  [pid 31529] read(3, "", 4)              = 0
  [pid 31530] read(3, 
"\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`|\2\0\0\0\0\0"..., 832) = 832
  [pid 31530] write(1, "\n", 1)           = 1
  [pid 31530] write(1, "\n", 1)           = 1
  [pid 31530] +++ exited with 0 +++
  --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=1335, si_uid=1000, 
si_status=0, si_utime=0, si_stime=0} ---
  +++ exited with 0 +++

And here again with my comments in between:

  $ strace -vfe read,write xargs --no-run-if-empty --null -I str echo str <<< ""
  read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`|\2\0\0\0\0\0"..., 
832) = 832

Ignore, that comes from reading a library.

  read(0, "\n", 4096)                     = 1

xargs reads a partial item - because it hasn't seen neither the delimiter '\0' 
nor EOF until then.
Therefore it goes for another round ...

  read(0, "", 4096)                       = 0

Now, xargs has hit EOF.
This means the first and only item is "\n".
Okay, we have more than Zero items, so it performs the -exec action with the -I 
str replacement:

  strace: Process 31530 attached
  [pid 31529] read(3, "", 4)              = 0

This read() is in the parent xargs process, and irrelevant for this case.
It is for error handling - for those who care:
  
https://git.sv.gnu.org/cgit/findutils.git/tree/xargs/xargs.c?id=11576f4e6a#n1376

  [pid 31530] read(3, 
"\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`|\2\0\0\0\0\0"..., 832) = 832

ignore again, the child process is reading one of the libraries.

  [pid 31530] write(1, "\n", 1)           = 1

Here, 'echo' outputs the item it got from xargs(1): the newline character.

  [pid 31530] write(1, "\n", 1)           = 1

Here, 'echo' appends the usual newline.

  [pid 31530] +++ exited with 0 +++
  --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=31530, si_uid=1000, 
si_status=0, si_utime=0, si_stime=0} ---
  +++ exited with 0 +++

The child process and xargs terminate without error.

With the -t, --verbose option, one can exactly see how echo is invoked (in 
shell-quotation style):

  $ xargs --no-run-if-empty --null -t -I str echo str <<< "" >/dev/null
  echo ''$'\n'

Therefore, I don't see anything wrong with xargs, and I think it's more a matter
of what a user expects from the shell's <<< operator: it is made for text input.
At least in the manual page of bash(1) on my system, this is documented 
properly:

  Here Strings
     A variant of here documents, the format is:

        [n]<<<word

    The word undergoes [...], and quote removal.
    Pathname expansion and word splitting are not performed.
    The result is supplied as a single string, with a newline appended, to the 
command
____^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    on its standard input (or file descriptor n if n is specified).

Have a nice day,
Berny



reply via email to

[Prev in Thread] Current Thread [Next in Thread]