Why redirect stdin inside a while read loop in bash?

前端 未结 1 757
醉话见心
醉话见心 2020-12-03 19:38

Consider the following sample script:

#!/bin/sh

do_something() {
    echo $@
    return 1
}

cat < sample.text
This is a sam         


        
相关标签:
1条回答
  • 2020-12-03 19:54

    The clear intent here is to prevent do_something from reading from the sample.text stream, by ensuring that its stdin is coming from elsewhere. If you're not seeing differences in behavior with or without the redirection, that's because do_something isn't actually reading from stdin in your tests.

    If you had both read and do_something reading from the same stream, then any content consumed by do_something wouldn't be available to a subsequent instance of read -- and, of course, you'd have illegitimate contents fed on input to do_something, resulting in consequences such as a bad encryption key being attempted (if the real-world use case were something like cryptmount), &c.

    cat sample.text | while read arg1 arg2 arg3 arg4 arg5; do
        ret=0
        do_something "$arg1" "$sarg2" "$arg3" "$arg4" "$arg5" <&3 || ret=$?
    done 3<&1
    

    Now, it's buggy -- 3<&1 is bad practice compared to 3<&0, inasmuch as it assumes without foundation that stdout is something that can also be used as input -- but it does succeed in that goal.


    By the way, I would write this more as follows:

    exec 3</dev/tty || exec 3<&0     ## make FD 3 point to the TTY or stdin (as fallback)
    
    while read -a args; do           ## |- loop over lines read from FD 0
      do_something "${args[@]}" <&3  ## |- run do_something with its stdin copied from FD 3
    done <sample.text                ## \-> ...while the loop is run with sample.txt on FD 0
    
    exec 3<&-                        ## close FD 3 when done.
    

    It's a little more verbose, needing to explicitly close FD 3, but it means that our code is no longer broken if we're run with stdout attached to the write-only side of a FIFO (or any other write-only interface) rather than directly to a TTY.


    As for the bug that this practice prevents, it's a very common one. See for example the following StackOverflow questions regarding it:

    • Shell script while read line loop stops after the first line
    • ssh breaks out of while-loop in bash
    • Bash while loop stops for no reason?

    etc.

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