From: Chris F.A. Johnson on 15 Jan 2010 23:05 On 2010-01-16, mop2 wrote: > On Fri, 15 Jan 2010 18:48:08 -0200, Robert Latest <boblatest(a)yahoo.com> wrote: > >> Hello people, >> >> the snippet from an interactive bash session below should illustrate my >> "problem". Stuff on the right-hand side are my comments for the >> respective line. I attempted assigning hours and minutes from date's >> output to a couple of variables using 'read': >> >> >> $ date '+%H +%M' | read h m ; echo $h $m | First try >> | ... prints empty line >> $ read h m ; echo $h $m | Huh? Can't 'read' read from stdin?? >> 10 20 | This was typed in by hand >> 10 20 | Sure enough: correct output >> $ cat | read h m ; echo $h $m | Let's pipe stdin through cat >> 11 22 | This was typed in >> 10 20 | h and m still have the old values >> $ eval `date '+h=%H; m=%M'` ; echo $h $m | This is what I ended up doing >> 21 38 | ... with the correct result >> >> In short: Why doesn't 'read' assign any values to variables h and m >> when stdin comes in from a pipe? > > > As already said by others, your read is running in a subshell. > Pass the read command to the current shell: > > read h m <<<`date '+%H %M'` If you want to test across different shells, use the portable syntax: read h m <<EOF `date '+%H %M'` EOF > As reference, a quick test with various shell: > > $ for s in /bin/*sh;do printf "$s \t:";$s -c 'printf "\t$h $m\t:\t";read h m <<<`date "+%H %M"`;echo $h $m';done > /bin/ash :Syntax error: redirection unexpected > /bin/bash : : 22 27 > /bin/csh :Missing name for redirect. > /bin/ksh : : 22 27 > /bin/rksh : : 22 27 > /bin/sh : : 22 27 > /bin/tcsh :Missing name for redirect. > /bin/zsh : : 22 27 > $ csh and tcsh are a different type of shell and do not belong in a syntax comparison with Bourne-type shells. -- 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) ===== My code in this post, if any, assumes the POSIX locale ===== ===== and is released under the GNU General Public Licence =====
From: Jerry Peters on 16 Jan 2010 16:36 Jerry Peters <jerry(a)example.invalid> wrote: > Robert Latest <boblatest(a)yahoo.com> wrote: >> Hello people, >> >> the snippet from an interactive bash session below should illustrate my >> "problem". Stuff on the right-hand side are my comments for the >> respective line. I attempted assigning hours and minutes from date's >> output to a couple of variables using 'read': >> >> >> $ date '+%H +%M' | read h m ; echo $h $m | First try >> | ... prints empty line >> $ read h m ; echo $h $m | Huh? Can't 'read' read from stdin?? >> 10 20 | This was typed in by hand >> 10 20 | Sure enough: correct output >> $ cat | read h m ; echo $h $m | Let's pipe stdin through cat >> 11 22 | This was typed in >> 10 20 | h and m still have the old values >> $ eval `date '+h=%H; m=%M'` ; echo $h $m | This is what I ended up doing >> 21 38 | ... with the correct result >> >> In short: Why doesn't 'read' assign any values to variables h and m >> when stdin comes in from a pipe? >> >> Thanks, >> robert > > Read does assign the values, the problem is that read is running in a > child shell which cannot affect the values of h and m in the parent > shell. Try a here document: > > read h m <<-EOF > date '+%H %M' > EOF > > Process substitution should also work. > Oops, that should be: read h m <<-EOF $(date '+%H %M') EOF Jerry
From: Robert Latest on 18 Jan 2010 12:19 mop2 wrote: > As already said by others, your read is running in a subshell. > Pass the read command to the current shell: > > read h m <<<`date '+%H %M'` I've also found that this works: date +'%H %M' | ( read h m ; echo $h $m ) Well... it works in the sense that I get echoed what I wanted echoed for the test, but of course this construct still doesn't get the values into the environment where they are needed. I don't understand, though, why "read" and "echo" are in different shells when the parentheses are left out. robert
From: Icarus Sparry on 18 Jan 2010 13:31 On Mon, 18 Jan 2010 17:19:44 +0000, Robert Latest wrote: > mop2 wrote: >> As already said by others, your read is running in a subshell. Pass the >> read command to the current shell: >> >> read h m <<<`date '+%H %M'` > > I've also found that this works: > > date +'%H %M' | ( read h m ; echo $h $m ) > > Well... it works in the sense that I get echoed what I wanted echoed for > the test, but of course this construct still doesn't get the values into > the environment where they are needed. > > I don't understand, though, why "read" and "echo" are in different > shells when the parentheses are left out. > > robert Let me see if I can explain. Start with the process that is running the script. Let us assume that it is process number 100. It sees the line "date +'%H %D' | something". For shell implementations like bash, it notices that this is not a shell builtin, so it has to run another program, so it calls "fork" to create a new process, let us say 101. 101 is given the task of running the command. 101 notices that there is a pipe, so it calls "pipe" to create a pipe, and then calls "fork" to create a new process to run the left hand side of the pipe. Let us say this creates process 102. Processes 101 and 102 adjust their file descriptors so that stdout of 102 goes to the pipe, and stdin of 101 comes from the pipe. Then 102 calls "exec" to replace itself with the "date" command, and 101 goes on to execute the "something". **** The "date | read ; echo" case If the "something" is "read h m" then 101 does the read (once 102 - now date- has produced the values), and then exits, discarding the values of h & m. At this point 100, which has been waiting for 101 to finish, now reads the next command which is "echo $h $m". It then processes that. **** The "date | { read ; echo ; }" case The "something" is "read h m ; echo $h $m" (the { and } were just used to group the commands, and are no longer needed). Process 101 says "read", this is a builtin, so I will run it. It sets the variables h and m. It then moves onto the next command, which is "echo $h $m", and it runs that, outputting the values you want. At this point 101 has reached the end of its commands, so it exits. 100, which has been waiting for 101, picks up the script at the next line. In your case you are using ( and ), rather than { and }. This may cause another shell to be invoked, so instead of 101 doing the read and echo, a new shell (say 104) might do it. The important point is that with the grouping the read and echo are done in the same shell, whilst without the grouping then in shell implementations like bash they are done in different shells.
From: mop2 on 18 Jan 2010 14:41 On Mon, 18 Jan 2010 15:19:44 -0200, Robert Latest <boblatest(a)yahoo.com> wrote: > mop2 wrote: >> As already said by others, your read is running in a subshell. >> Pass the read command to the current shell: >> >> read h m <<<`date '+%H %M'` > > I've also found that this works: > > date +'%H %M' | ( read h m ; echo $h $m ) > > Well... it works in the sense that I get echoed what I wanted echoed for > the test, but of course this construct still doesn't get the values into > the environment where they are needed. > > I don't understand, though, why "read" and "echo" are in different > shells when the parentheses are left out. > > robert In your example above both sides of the pipe "are" in the same shell (a binary program on memory), but the right side of the pipe is in a subshell of its own. An isolated area which receive data from the parent shell, but doesn't send nothing to it, except the return status (sorry, AFAIK). If I am wrong, someone let me know... Well, the shell can explain this better than me. A subshell is not always an other kernel process: $ eval echo \$0 \$$ -bash 1504 $ { eval echo \$0 \$$;} -bash 1504 $ (eval echo \$0 \$$) -bash 1504 $ cat< <(eval echo \$0 \$$) -bash 1504 $ echo `eval echo \$0 \$$` -bash 1504 $ echo $(eval echo \$0 \$$) -bash 1504 $ bash -c 'eval echo \$0 \$$' bash 16500 $ bash <<<'eval echo \$0 \$$' bash 17253 You can see, a similar behavior about your question, using a function in bash. An "isolated process" (process of the shell) for the variable "r": $ echo =$r= == $ f(){ local r; read r;} $ f <<<isolated $ echo =$r= == Now the shell uses the value of the "r" (default behavior): $ f(){ read r;} $ echo =$r= == $ f <<<r_not_local $ echo =$r= =r_not_local= $ bash's man page perhaps can help a bit more, seek for "COMMAND EXECUTION ENVIRONMENT"
First
|
Prev
|
Next
|
Last
Pages: 1 2 3 4 Prev: Mutt: unable to attach file Next: Why NonZero Exit Status |