[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: printf '%s\n' "$@" versus <<< redirection
From: |
Kerin Millar |
Subject: |
Re: printf '%s\n' "$@" versus <<< redirection |
Date: |
Sat, 18 Feb 2023 10:09:09 +0000 |
On Sat, 18 Feb 2023 08:50:02 +0000
goncholden <goncholden@protonmail.com> wrote:
> I am using the following bash function
>
> theone ()
> {
> printf '%s\n' "$@" \
> | while IFS="" read -r vl; do
> ...
> done
> }
>
> I have also been looking at this second implementation
>
> theone ()
> {
> while IFS="" read -r vl; do
> ...
> done <<< "$@"
> }
>
> But it occurs to me that the two are actually different. Using <<< means
> reading from stdin,
> which will not preserve the arguments, so special chars (like newline) may
> cause troubles.
Clearly, they do different things but the second sentence is confused. Firstly,
the reason that incorporating a newline character into the values of any of the
parameters might cause a problem is because read defaults to using the newline
as a record (line) delimiter. That particular problem can be circumvented by
using NUL as a delimiter instead. It serves as a safe delimiter because bash
strings can never contain it.
printf '%s\0' "$@" | while IFS= read -rd '' vl; do ...; done
Secondly, and regarding your herestring example, "$@" normally expands to as
many distinct words as there are positional parameters. However, a herestring
always expects to consume just _one_ word. In this case, bash chooses to handle
the situation by joining the positional parameters with the space character,
producing that word. Should you ever genuinely want to do this, it would make
more sense to write "$*", in which case the parameters are joined by the first
character of IFS (by default, a space). The behaviour of "$*" is always the
same, no matter the context in which it is used. Therefore, it would better
indicate to the reader that the intention to join the parameters is deliberate.
A better analogue of the first shown function would have involved the use of a
process substitution, which is another way of setting up a pipe.
while IFS= read -r vl; do ...; done < <(printf '%s\n' "$@")
Still, I can only assume that the first example is academic in nature, because
a simple for loop ought to be used instead.
for vl in "$@"; do ...; done
>
> The first implementation honours newlines in the arguments, whilst also
> introduces a newline
> between arguments (between $1, $2, $3, etc). Am I missing anything in my
> analysis ?
Apart from that which has been stated above, a herestring always used to result
in the creation of a temporary file. This behaviour changed with the release of
5.1. Now, bash may choose to handle a herestring by internally creating a pipe,
provided that the length of the word is within the limit defined by PIPE_BUF.
Taking Linux as an example, the default pipe buffer size is 16 times the
platform's page size. For an x86_64 system, that would amount to 64 KiB.
$ declare -p BASH_VERSION
declare -- BASH_VERSION="5.1.16(1)-release"
$ readlink /proc/self/fd/0 <<<"$(printf %65535s)" # off-by-one because
herestrings add a newline
pipe:[1276109]
$ readlink /proc/self/fd/0 <<<"$(printf %65536s)" # ditto
/tmp/sh-thd.zpxDpj (deleted)
--
Kerin Millar
Re: printf '%s\n' "$@" versus <<< redirection, alex xmb ratchev, 2023/02/18