Pipe | Redirection < > Precedence

前端 未结 5 2043
清酒与你
清酒与你 2020-12-13 05:07

I want to make clear when does pipe | or redirection < > takes precedence in a command?

This is my thought but need confirmation this is how it works.

Exa

相关标签:
5条回答
  • 2020-12-13 05:31

    From man bash (as are the other quotes):

    SHELL GRAMMAR
       Simple Commands
           A simple command is a sequence of optional variable assignments followed by
           blank-separated words and redirections, and terminated  by  a  control
           operator. The first word specifies the command to be executed, and is
           passed as argument zero.  The remaining words are passed as arguments
           to the invoked command.
    
           The return value of a simple command is its exit status, or 128+n if
           the command is terminated by signal n.
    
       Pipelines
           A pipeline is a sequence of one or more commands separated by one of
           the control operators | or |&.  The format for a pipeline is:
    
                  [time [-p]] [ ! ] command [ [|⎪|&] command2 ... ]
    

    In other words, you can have any number of redirections for a (simple) command; you can also use that as part of a pipeline. Or, put another way, redirection binds more tightly than pipe.

    There are a couple of ways to get work around this (although they're rarely either necessary or aesthetic):

    1. You can make a "compound command" and redirect into it:

     Compound Commands
       A compound command is one of the following:
    
       (list)  list is executed in a subshell environment (see
               COMMAND EXECUTION ENVIRONMENT below).  Variable
               assignments  and  builtin  commands  that  affect  the
               shell's environment do not remain in effect after the
               command completes.  The return status is the exit status of list.
    
       { list; }
              list  is  simply  executed  in the current shell environment.  list
              must be terminated with a newline or semicolon.  This is known as a
              group command. The return status is the exit status of list.  Note
              that unlike the metacharacters ( and ), { and } are reserved words
              and must occur where a reserved word is permitted to be recognized.
              Since they do not cause a word break, they must be separated from
              list by whitespace or another shell metacharacter.
    

    So:

    $ echo foo > input
    $ { cat | sed 's/^/I saw a line: /'; } < input
    I saw a line: foo
    

    2. You can redirect to a pipe using "process substitution":

    Process Substitution
       Process  substitution  is  supported on systems that support named pipes
       (FIFOs) or the /dev/fd method of naming open files.  It takes the form of
       <(list) or >(list).  The process list is run with its input or output
       connected to a FIFO or some file in /dev/fd.  The name of this file is
       passed as  an  argument  to  the  current  command  as the result of the
       expansion.  If the >(list) form is used, writing to the file will provide
       input for list.  If the <(list) form is used, the file passed as an argument
       should be read to obtain the output of list.
    

    So:

     rici@...$ cat > >(sed 's/^/I saw a line: /') < <(echo foo; echo bar)
     I saw a line: foo
     rici@...$ I saw a line: bar
    

    (Why the prompt appears before the output terminates, and what to do about it are left as exercises).

    0 讨论(0)
  • 2020-12-13 05:34

    It's a little unorthodox, but perfectly legal, to place the < anywhere you like, so I prefer this as it better illustrates the left-to-right data flow:

    <input.txt sort | head >output.txt
    

    The only time you cannot do this is with built-in control structure commands (for, if, while).

    # Unfortunately, NOT LEGAL
    <input.txt  while read line; do ...; done
    

    Note that all of these are equivalent commands, but to avoid confusion you should use only the first or the last one:

    <input.txt grep -l foobar
    grep <input.txt -l foobar
    grep -l <input.txt foobar
    grep -l foobar <input.txt
    

    Because the file name must always come directly after the redirection operator, I prefer to leave out the optional space between the < and the file name.

    0 讨论(0)
  • 2020-12-13 05:38

    In terms of syntactic grouping, > and < have higher precedence; that is, these two commands are equivalent:

    sort < names | head
    ( sort < names ) | head
    

    as are these two:

    ls | sort > out.txt
    ls | ( sort > out.txt )
    

    But in terms of sequential ordering, | is performed first; so, this command:

    cat in.txt > out1.txt | cat > out2.txt
    

    will populate out1.txt, not out2.txt, because the > out1.txt is performed after the |, and therefore supersedes it (so no output is piped out to cat > out2.txt).

    Similarly, this command:

    cat < in1.txt | cat < in2.txt
    

    will print in2.txt, not in1.txt, because the < in2.txt is performed after the |, and therefore supersedes it (so no input is piped in from cat < in1.txt).

    0 讨论(0)
  • 2020-12-13 05:44

    Corrections:

    Example 1:

    sort < names | head
    

    In this case, input redirect runs first (names are sorted), then the result of that is piped to head.

    In general you can read from left to right. The standard idiom works as follows:

    • Use of input redirection "<" tells the program reads from a file instead of stdin
    • Use of output redirection ">" tells the program to output to a file instead of stdout
    • Use of pipe "program_a | program_b" takes everything that would normally be output by program_a to stdout, and feeds it all directly to program_b as if it was read from stdin.
    0 讨论(0)
  • 2020-12-13 05:51

    This is pretty much what I understand after doing some reading (including ruakh's answer)

    First of all, if you redirect multiple times, all the redirections are performed, but only the last redirection will take effect (assuming none of the earlier redirections cause error)

    • e.g. cat < in1.txt < in2.txt is equivalent to cat < in2.txt, unless in1.txt does not exist in which case this command will fail (since < in1.txt is performed first)

    • Similarly, with cat in.txt > out1.txt > out2.txt, only out2.txt would contain the contents of out2.txt, but since > out1.txt was performed first, out1.txt would be created if it doesn't exist.

    What pipe does is connect the stdout of previous command to the stdin of the next command, and that connection comes before any other redirections (from Bash manual).

    So you can think of

    cat in1.txt > out1.txt | cat > out2.txt
    

    as

    cat in1.txt > pipe > out1.txt; cat < pipe > out2.txt
    

    And applying the multiple redirection rule mentioned before, we can simplify this to

    cat in1.txt > out1.txt; cat < pipe > out2.txt
    

    Result: The content of in1.txt is copied to out1.txt, since nothing was written to pipe


    Using another of ruakh's example,

    cat < in1.txt | cat > in2.txt
    

    is roughly equivalent to

    cat > pipe < in1.txt; cat < pipe < in2.txt
    

    which is effectively

    cat > pipe < in1.txt; cat < in2.txt
    

    Result: This time something is written to the pipe, but since the second cat reads from in2.txt instead of pipe, only the content of in2.txt is printed out.

    0 讨论(0)
提交回复
热议问题