Why is “echo foo | read a ; echo $a” not working as expected?

前端 未结 8 1746
慢半拍i
慢半拍i 2021-02-04 15:37

I could replicate the problem with various shells under FreeBSD, GNU/Linux, and Solaris. It had me head-scratching for more than an hour, so I decided to post the question here

相关标签:
8条回答
  • 2021-02-04 16:11

    According to Kernighan and Pike's The Unix Programming Environment (p. 159) "none of the shell built-in commands (as opposed to the control flow primitives, like for) can be redirected with > and <", and "this might be described as a bug in the shell". This seems to explain a) why code like

    ls *.c |
    while read file
    do
       echo $a
    done
    

    invariably works without a problem, and b) the inconsistency that redirecting from a file works.

    0 讨论(0)
  • 2021-02-04 16:12

    Just FYI; in ksh it is working as expected; See also http://kornshell.com/doc/faq.html, Section III (shell programming questions), Q13:

    Q13.    What is $bar after, echo foo | read bar?
    A13.    The is foo.  ksh runs the last component of a pipeline
            in the current process.  Some shells run it as a subshell
            as if you had invoked it as  echo foo | (read bar).
    
    0 讨论(0)
  • 2021-02-04 16:12

    read expects input on a new line

    while read a; do echo $a; done
    foo
    bar
    ^D
    

    is more or less what you wanted

    0 讨论(0)
  • 2021-02-04 16:20

    Due to the piping the read is executed in its own subshell.

    echo foo | while read a; do echo $a; done
    

    will do what you expect it to.

    0 讨论(0)
  • 2021-02-04 16:25

    The | is an inter-process communications operator. So, implicitly, there is a subprocess that must be created to parse and evaluate the expression on one side of it or the other. Bash, and older Bourne shells create a subprocess to the right of the operator (reading from the pipe) which means that any variables set there are only in scope until that process exits (which is at the semicolon in this code example.

    zsh and newer versions of Korn shell (at least from '93 on, but possibly even as far back as ksh '88) will create the subprocess on the other side of the pipe (writing into it). Thus they will work in the way that the author of this question intends. ("As expected" is, of course, highly subjective. Understanding the nature of the pipe should lead one to expect this to behave in an implementation specific manner).

    I don't know if there is any specific provision in Posix or Spec 1170 or SuS or any other published standard that requires one or the other semantics. However, in practice it's clear that you must not depend on the behavior; though you can easily test for it within your scripts.

    0 讨论(0)
  • 2021-02-04 16:28

    I don't think this one has already been given:

    a=`echo foo`
    echo $a
    
    0 讨论(0)
提交回复
热议问题