help-bash
[Top][All Lists]
Advanced

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

Re: printf '%s\n' "$@" versus <<< redirection


From: alex xmb ratchev
Subject: Re: printf '%s\n' "$@" versus <<< redirection
Date: Sat, 18 Feb 2023 13:21:48 +0100

On Sat, Feb 18, 2023, 11:09 AM Kerin Millar <kfm@plushkava.net> wrote:

> 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
>

may i ask for what the IFS= does in a single var read ment ?
i know its been manywhere , but , i couldnt keep it

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
>
>


reply via email to

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