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

Kurt von Laven <[email protected]>
Newsgroups gmane.comp.gnu.findutils.bugs
Message-ID <CAO-OgztmPfhMxQe+q_ESiEndFtHqfmykLWx6kTQDxWjf5akXpA@mail.gmail.com>
Thank you, Berny. Your explanation was enlightening to a degree I rarely
encounter. Apologies for the erroneous bug report, and thank you for
the quick reply. I confirmed that this issue is specific to <<< by using
pipes instead. I will go forth and use <<< correctly.

Be well,
Kurt

El vie, 14 may 2021 a las 11:53, Bernhard Voelker (<[email protected]>)
escribió:

> 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
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.