Fixing stdin inside a redirected loop...

  • Follow


In this group, it is an FGA that one should so:

    commandThatGeneratesTheFilenames | while read x ...

instead of the more common:

    for i in ...

because of the problems of filenames that spaces and other weird characters.

However, one downside to the "while" method is that inside the loop,
stdin is now coming from the pipe, and commands that we execute inside
the loop, that expect a normal stdin, will misbehave.

Workarounds:
    1) append "< /dev/tty" to each command
    2) enclose the commands in parens and redirect that.  I.e.:

	commandThatGeneratesTheFilenames | while read x;do
	    (cmd1;cmd2;cmd3;...) < /dev/tty
	    done

Method 2 is nice and seems to work fine, but I am wondering if there is
any hidden cost to it and if there is any more elegant/effcient way to
do this.

-- 
Just for a change of pace, this sig is *not* an obscure reference to
comp.lang.c...

0
Reply gazelle 7/20/2010 12:13:50 PM

Kenny McCormack wrote:

> In this group, it is an FGA that one should so:
> 
>     commandThatGeneratesTheFilenames | while read x ...
> 
> instead of the more common:
> 
>     for i in ...
> 
> because of the problems of filenames that spaces and other weird
> characters.
> 
> However, one downside to the "while" method is that inside the loop,
> stdin is now coming from the pipe, and commands that we execute inside
> the loop, that expect a normal stdin, will misbehave.
> 
> Workarounds:
>     1) append "< /dev/tty" to each command
>     2) enclose the commands in parens and redirect that.  I.e.:
> 
> commandThatGeneratesTheFilenames | while read x;do
> (cmd1;cmd2;cmd3;...) < /dev/tty
> done

I think (at least with bash) it's possible to use a file descriptor other 
than the standard ones, and use read -u to read from that descriptor. This 
way, standard descriptors (0, 1 and 2) inside the loop are not affected.

Something like

exec 3< <(command)

while read -u 3 line; do
  # unaffected commands here...
done

exec 3>&-


I understand it's a highly nonstandard solution, but, depending on where it 
has to run, it might be applicable.
0
Reply pk 7/20/2010 12:55:19 PM


In article <i246br$vjs$1@speranza.aioe.org>, pk  <pk@pk.invalid> wrote:
....
>I think (at least with bash) it's possible to use a file descriptor other 
>than the standard ones, and use read -u to read from that descriptor. This 
>way, standard descriptors (0, 1 and 2) inside the loop are not affected.
>
>Something like
>
>exec 3< <(command)
>
>while read -u 3 line; do
>  # unaffected commands here...
>done
>
>exec 3>&-
>
>I understand it's a highly nonstandard solution, but, depending on where it 
>has to run, it might be applicable.

Very interesting.  As readers of my posts know, I'm not a "standards
jockey", so don't take what I say next as the typical SJ jockeying...

That said,
    1) The above works if I invoke "bash" as "bash".
    2) (However) It does not work if I invoke "bash" as "sh"
    3) It doesn't work under "dash" (which is what /bin/sh on some Linux
	boxes actually is).

Anyway, as I said, it is quite intriguing.

-- 
Just for a change of pace, this sig is *not* an obscure reference to
comp.lang.c...

0
Reply gazelle 7/20/2010 2:12:57 PM

In article <i24at9$rc5$1@news.xmission.com>,
Kenny McCormack <gazelle@shell.xmission.com> wrote:
....
>That said,
>    1) The above works if I invoke "bash" as "bash".
>    2) (However) It does not work if I invoke "bash" as "sh"
>    3) It doesn't work under "dash" (which is what /bin/sh on some Linux
>	boxes actually is).
>
>Anyway, as I said, it is quite intriguing.

I forgot to add, the important thing, which is that your solution,
unfortunately, does not work on the targetted system (which is a Linux
system without bash - yes, they exist).  So, I am still curious about
the questions raised in the OP - is there any hidden cost (inefficiency)
to using the ()</dev/tty method, and are there any (other) alternatives?

-- 
"We should always be disposed to believe that which appears to us to be
white is really black, if the hierarchy  of the church so decides." 

    - Saint Ignatius Loyola (1491-1556) Founder of the Jesuit Order -

0
Reply gazelle 7/20/2010 2:24:37 PM

On Tue, 20 Jul 2010 14:12:57 +0000 (UTC) gazelle@shell.xmission.com (Kenny
McCormack) wrote:

> In article <i246br$vjs$1@speranza.aioe.org>, pk  <pk@pk.invalid> wrote:
> ...
> >I think (at least with bash) it's possible to use a file descriptor
> >other than the standard ones, and use read -u to read from that
> >descriptor. This way, standard descriptors (0, 1 and 2) inside the loop
> >are not affected.
> >
> >Something like
> >
> >exec 3< <(command)
> >
> >while read -u 3 line; do
> >  # unaffected commands here...
> >done
> >
> >exec 3>&-
> >
> >I understand it's a highly nonstandard solution, but, depending on where
> >it has to run, it might be applicable.
> 
> Very interesting.  As readers of my posts know, I'm not a "standards
> jockey", so don't take what I say next as the typical SJ jockeying...
> 
> That said,
>     1) The above works if I invoke "bash" as "bash".
>     2) (However) It does not work if I invoke "bash" as "sh"
>     3) It doesn't work under "dash" (which is what /bin/sh on some Linux
> 	boxes actually is).

Standards jockey or not, if that is where it has to run, you definitely need
a standard solution.

0
Reply pk 7/20/2010 5:06:56 PM

Kenny McCormack wrote:

> In this group, it is an FGA that one should so:
> 
>     commandThatGeneratesTheFilenames | while read x ...
> 
> instead of the more common:
> 
>     for i in ...
> 
> because of the problems of filenames that spaces and other weird
> characters.
> 
> However, one downside to the "while" method is that inside the loop,
> stdin is now coming from the pipe, and commands that we execute inside
> the loop, that expect a normal stdin, will misbehave.

Name one, and I am pretty sure that I can tell you the switch that will 
prevent it from considering stdin.  For example, ssh(1) has `-n'.

-- 
PointedEars
0
Reply Thomas 7/21/2010 12:14:38 AM

Kenny McCormack wrote:

> In this group, it is an FGA that one should so:
> 
>     commandThatGeneratesTheFilenames | while read x ...
> 
> instead of the more common:
> 
>     for i in ...
> 
> because of the problems of filenames that spaces and other weird
> characters.

No, the foregone assumption (FGA is not a common Usenet acronym -- shouldn't 
that be "foregone conclusion" anyway?) is that find(1) with `-exec', or 
`find' with xargs(1), are safer than both approaches.

Usenet rule of thumb: Never ask what is better; bottom line is that better 
is what works for you.  Ask instead what is more efficient aso. given a 
specific context.

-- 
PointedEars
0
Reply Thomas 7/21/2010 12:24:50 AM

In article <i243tu$l8g$1@news.xmission.com>,
Kenny McCormack <gazelle@shell.xmission.com> wrote:
>In this group, it is an FGA that one should so:
>
>    commandThatGeneratesTheFilenames | while read x ...
>
>instead of the more common:
>
>    for i in ...
>
>because of the problems of filenames that spaces and other weird characters.
>
>However, one downside to the "while" method is that inside the loop,
>stdin is now coming from the pipe, and commands that we execute inside
>the loop, that expect a normal stdin, will misbehave.
>
>Workarounds:
>    1) append "< /dev/tty" to each command
>    2) enclose the commands in parens and redirect that.  I.e.:
>
>	commandThatGeneratesTheFilenames | while read x;do
>	    (cmd1;cmd2;cmd3;...) < /dev/tty
>	    done
>
>Method 2 is nice and seems to work fine, but I am wondering if there is
>any hidden cost to it and if there is any more elegant/effcient way to
>do this.

    Unless you specifically want to read from the controlling tty rather than
    the standard input, it's always better to save the standard input to
    another fd and then read from it.

    {
	commandThatGeneratesTheFilenames |
	    while read x <&4; do
		cmd1
		cmd2
		cmd3
	    done 4<&0 0<&3
    } 3<&0

    In modern shells (ksh93/bash/zsh) you can do:

    while read -u3 x; do
	cmd1
	cmd2
	cmd3
    done 3< <(commandThatGeneratesTheFilenames)

	John
-- 
John DuBois  spcecdt@armory.com  KC6QKZ/AE  http://www.armory.com/~spcecdt/
0
Reply spcecdt 7/21/2010 4:56:01 PM

In article <B_qdnbdAifw8u9rRnZ2dnUVZ_s6dnZ2d@speakeasy.net>,
John DuBois <spcecdt@armory.com> wrote:
....
>    Unless you specifically want to read from the controlling tty rather than
>    the standard input, it's always better to save the standard input to
>    another fd and then read from it.
>
>    {
>	commandThatGeneratesTheFilenames |
>	    while read x <&4; do
>		cmd1
>		cmd2
>		cmd3
>	    done 4<&0 0<&3
>    } 3<&0

What's the difference between enclosing the cmds in {} vs. ()?

-- 
Faced with the choice between changing one's mind and proving that there is
no need to do so, almost everyone gets busy on the proof. 

    - John Kenneth Galbraith -

0
Reply gazelle 7/21/2010 5:45:28 PM

On 2010-07-21, Kenny McCormack wrote:
> In article <B_qdnbdAifw8u9rRnZ2dnUVZ_s6dnZ2d@speakeasy.net>,
> John DuBois <spcecdt@armory.com> wrote:
> ...
>>    Unless you specifically want to read from the controlling tty rather than
>>    the standard input, it's always better to save the standard input to
>>    another fd and then read from it.
>>
>>    {
>>	commandThatGeneratesTheFilenames |
>>	    while read x <&4; do
>>		cmd1
>>		cmd2
>>		cmd3
>>	    done 4<&0 0<&3
>>    } 3<&0
>
> What's the difference between enclosing the cmds in {} vs. ()?

   Commands in ( ) are executed in a subshell.


-- 
   Chris F.A. Johnson, author           <http://shell.cfajohnson.com/>
   ===================================================================
   Shell Scripting Recipes: A Problem-Solution Approach (2005, Apress)
   Pro Bash Programming: Scripting the GNU/Linux Shell (2009, Apress)

0
Reply Chris 7/21/2010 7:05:00 PM

In article <8aoumsFvcrU2@mid.individual.net>,
Chris F.A. Johnson <cfajohnson@gmail.com> wrote:
>On 2010-07-21, Kenny McCormack wrote:
>> In article <B_qdnbdAifw8u9rRnZ2dnUVZ_s6dnZ2d@speakeasy.net>,
>> John DuBois <spcecdt@armory.com> wrote:
>> ...
>>>    Unless you specifically want to read from the controlling tty rather than
>>>    the standard input, it's always better to save the standard input to
>>>    another fd and then read from it.
>>>
>>>    {
>>>	commandThatGeneratesTheFilenames |
>>>	    while read x <&4; do
>>>		cmd1
>>>		cmd2
>>>		cmd3
>>>	    done 4<&0 0<&3
>>>    } 3<&0
>>
>> What's the difference between enclosing the cmds in {} vs. ()?
>
>   Commands in ( ) are executed in a subshell.

That's what I thought.  So, the answer to the original question, despite
all the other flurry, is simply: Yes, you should use {} rather than ().

-- 
"We should always be disposed to believe that which appears to us to be
white is really black, if the hierarchy  of the church so decides." 

    - Saint Ignatius Loyola (1491-1556) Founder of the Jesuit Order -

0
Reply gazelle 7/21/2010 7:10:05 PM

Kenny McCormack wrote:

> Chris F.A. Johnson <cfajohnson@gmail.com> wrote:
>> On 2010-07-21, Kenny McCormack wrote:
>>> What's the difference between enclosing the cmds in {} vs. ()?
>>   Commands in ( ) are executed in a subshell.
> 
> That's what I thought.  So, the answer to the original question, despite
> all the other flurry, is simply: Yes, you should use {} rather than ().

No, the answer simply is, again, "it depends."  That is the part of software 
development that you do not seem to understand.

-- 
PointedEars
0
Reply Thomas 7/21/2010 8:59:14 PM

On 2010-07-21, John DuBois wrote:
> In article <i243tu$l8g$1@news.xmission.com>,
> Kenny McCormack <gazelle@shell.xmission.com> wrote:
>>In this group, it is an FGA that one should so:
>>
>>    commandThatGeneratesTheFilenames | while read x ...
>>
>>instead of the more common:
>>
>>    for i in ...
>>
>>because of the problems of filenames that spaces and other weird characters.
>>
>>However, one downside to the "while" method is that inside the loop,
>>stdin is now coming from the pipe, and commands that we execute inside
>>the loop, that expect a normal stdin, will misbehave.
>>
>>Workarounds:
>>    1) append "< /dev/tty" to each command
>>    2) enclose the commands in parens and redirect that.  I.e.:
>>
>>	commandThatGeneratesTheFilenames | while read x;do
>>	    (cmd1;cmd2;cmd3;...) < /dev/tty
>>	    done
>>
>>Method 2 is nice and seems to work fine, but I am wondering if there is
>>any hidden cost to it and if there is any more elegant/effcient way to
>>do this.
>
>     Unless you specifically want to read from the controlling tty rather than
>     the standard input, it's always better to save the standard input to
>     another fd and then read from it.

   Why?

>     {
> 	commandThatGeneratesTheFilenames |
> 	    while read x <&4; do

   That will fail on filenames beginning or ending with spaces.

while IFS= read -r x <&4

> 		cmd1
> 		cmd2
> 		cmd3
> 	    done 4<&0 0<&3
>     } 3<&0
>
>     In modern shells (ksh93/bash/zsh) you can do:
>
>     while read -u3 x; do
> 	cmd1
> 	cmd2
> 	cmd3
>     done 3< <(commandThatGeneratesTheFilenames)
>
> 	John


-- 
   Chris F.A. Johnson, author           <http://shell.cfajohnson.com/>
   ===================================================================
   Shell Scripting Recipes: A Problem-Solution Approach (2005, Apress)
   Pro Bash Programming: Scripting the GNU/Linux Shell (2009, Apress)

0
Reply Chris 7/21/2010 9:30:36 PM

In article <4540035.MirdbgypaU@PointedEars.de>,
Thomas 'PointedEars' Lahn  <usenet@PointedEars.de> wrote:
>Kenny McCormack wrote:
>
>> Chris F.A. Johnson <cfajohnson@gmail.com> wrote:
>>> On 2010-07-21, Kenny McCormack wrote:
>>>> What's the difference between enclosing the cmds in {} vs. ()?
>>>   Commands in ( ) are executed in a subshell.
>> 
>> That's what I thought.  So, the answer to the original question, despite
>> all the other flurry, is simply: Yes, you should use {} rather than ().
>
>No, the answer simply is, again, "it depends."  That is the part of software 
>development that you do not seem to understand.

You funny.

(And I've not even seen any pix of you and you funny ears!)

-- 
> No, I haven't, that's why I'm asking questions. If you won't help me,
> why don't you just go find your lost manhood elsewhere.

CLC in a nutshell.

0
Reply gazelle 7/21/2010 9:34:20 PM

On 2010-07-21, Thomas 'PointedEars' Lahn <PointedEars@web.de> wrote:
> No, the foregone assumption (FGA is not a common Usenet acronym

"Frequently Given Answer", used by some guy whose name I've forgotten
to refer to things he maintains in a format chosen to prevent people from
rebutting the idiocy therein.

-s
-- 
Copyright 2010, all wrongs reversed.  Peter Seebach / usenet-nospam@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
0
Reply Seebs 7/21/2010 9:49:57 PM

Kenny McCormack wrote:

> Thomas 'PointedEars' Lahn wrote:
>> Kenny McCormack wrote:
>>> Chris F.A. Johnson <cfajohnson@gmail.com> wrote:
>>>> On 2010-07-21, Kenny McCormack wrote:
>>>>> What's the difference between enclosing the cmds in {} vs. ()?
>>>>   Commands in ( ) are executed in a subshell.
>>> That's what I thought.  So, the answer to the original question, despite
>>> all the other flurry, is simply: Yes, you should use {} rather than ().
>> No, the answer simply is, again, "it depends."  That is the part of
>> software development that you do not seem to understand.
> 
> You funny.

It was not my intention to amuse you, but to make you think twice.  BTW, in 
responding like this you are also making a fool of yourself with regard to 
grammar.

> (And I've not even seen any pix of you and you funny ears!)
 
I have not seen a photo of you either.  So what?
-- 
PointedEars
0
Reply Thomas 7/22/2010 12:31:09 AM

Seebs wrote:

> Thomas 'PointedEars' Lahn wrote:
>> No, the foregone assumption (FGA is not a common Usenet acronym
> 
> "Frequently Given Answer", used by some guy whose name I've forgotten
> to refer to things he maintains in a format chosen to prevent people from
> rebutting the idiocy therein.

Thanks.  In ten years of regular Usenet participation, in and outside of the 
Big 8, I had never come across that acronym.  It is not in the Jargon File 
either.  So I doubt it is more than his original research.

-- 
PointedEars
0
Reply Thomas 7/22/2010 9:44:28 AM

In article <8ap77sFvcrU3@mid.individual.net>,
Chris F.A. Johnson <cfajohnson@gmail.com> wrote:
>On 2010-07-21, John DuBois wrote:
>> In article <i243tu$l8g$1@news.xmission.com>,
>> Kenny McCormack <gazelle@shell.xmission.com> wrote:
>>>Workarounds:
>>>    1) append "< /dev/tty" to each command
>>>    2) enclose the commands in parens and redirect that.  I.e.:
>>>
>>>	commandThatGeneratesTheFilenames | while read x;do
>>>	    (cmd1;cmd2;cmd3;...) < /dev/tty
>>>	    done
>>>
>>>Method 2 is nice and seems to work fine, but I am wondering if there is
>>>any hidden cost to it and if there is any more elegant/effcient way to
>>>do this.
>>
>>     Unless you specifically want to read from the controlling tty rather than
>>     the standard input, it's always better to save the standard input to
>>     another fd and then read from it.
>
>   Why?

Because the standard input may not be the controlling tty (if the program is
piped or redirected into, or started from an environment with no controlling
tty, etc).

	John
-- 
John DuBois  spcecdt@armory.com  KC6QKZ/AE  http://www.armory.com/~spcecdt/
0
Reply spcecdt 7/22/2010 3:03:54 PM

2010-07-20, 12:13(+00), Kenny McCormack:
> In this group, it is an FGA that one should so:
>
>     commandThatGeneratesTheFilenames | while read x ...
>
> instead of the more common:
>
>     for i in ...
>
> because of the problems of filenames that spaces and other weird characters.

After

IFS='
' # NL
set -f
for i in $(commandThatGeneratesTheFilenames); do...

shouldn't be a problem (except that the loop only starts when
commandThatGeneratesTheFilenames finishes).

Other solution (not any better either than the ones that have
already been given):

while IFS= read <&3 -r i; do
   ...
done 3<<EOF
$(commandThatGeneratesTheFilenames)
EOF

{
  commandThatGeneratesTheFilenames |
    while IFS= read <&3 -r i; do
      ...
    done 3<&0 <&4 4<&-
} 4<&0

as already given would be the best one. If you're picky, you may
write it:

{
  commandThatGeneratesTheFilenames 4<&- |
    while IFS= read <&3 -r i; do
    {
      ...
    } 3<&-
    done 3<&0 <&4 4<&-
} 4<&0

or:

{
  commandThatGeneratesTheFilenames 4<&- |
    while IFS= read -r i 4<&-; do
    {
      ...
    } <&4 4<&-
    done
} 4<&0

-- 
Stephane
0
Reply Stephane 7/26/2010 4:57:51 PM

18 Replies
1393 Views

(page loaded in 0.333 seconds)

Similiar Articles:










7/22/2012 1:58:39 AM


Reply: