[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