[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: |
Kurt von Laven |
Subject: |
Re: The xargs -r, --no-run-if-empty Option Is Ignored When a Delimiter Is Passed |
Date: |
Fri, 14 May 2021 19:24:20 -0700 |
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 (<mail@bernhard-voelker.de>)
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
>