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: goncholden
Subject: Re: printf '%s\n' "$@" versus <<< redirection
Date: Sat, 18 Feb 2023 10:46:08 +0000

------- Original Message -------
On Saturday, February 18th, 2023 at 10:09 PM, 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
> 
> 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

I am not convinced that tho loop is equivalent to the first example.
My intention is to use prinf line by line on arguments containing newlines.  
With a newline also introduced between arguments $1 $2 $3 etc.  

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