Search code examples
bashshellposixzsh

An alternative to "read -u" for POSIX shells


I'm trying to port this function from zsh to generic POSIX shell script:

mcp () {
    # run commands on a variety of searches (defaults to giving a count)
    while read -u9 -n line ; do
        command=`echo $line| sed 's/#.*$//'`
        echo -n $command
        eval "$command | ${*:-wc -l}"
    done 9<<EOF
magrep "Precedence:bulk"         # mass mails
magrep "broadcastSendId:.*"      # mass mails
magrep "Feedback-ID:.*"          # mass mails
EOF
}

(Context here)

The while loop uses file descriptor 9 instead of stdin, so that the commands within the loop can include commands that read from the function's stdin.

POSIX appears to allow the same redirection, but its 'read' does not have a "-u" option to take an alternative input fd, and I'm struggling to find a redirection command that works as a replacement.

Does anyone have an alternative to read -u for a POSIX version of this function?


Solution

  • Just open the read command each time with the file descriptor passed to stdin:

    while read line <&9; do
           echo "$line"
    done 9<<'EOF'
    magrep "Precedence:bulk"         # mass mails
    magrep "broadcastSendId:.*"      # mass mails
    magrep "Feedback-ID:.*"          # mass mails
    EOF   
    

    or more advanced example:

    while 
        IFS= read -r line <&9
        IFS=' ' read -r file2col1 file2col2 <&10
        IFS=' ' read -r file3col1 file3col2 <&11
    do
        echo -----------
        echo "$line"
        echo "$file2col1"
        echo "$file2col2"
        echo "$file3col1"
        echo "$file2col2"
    done 9<<'EOF' 10<<EOF2 11<<'EOF3'
    magrep "Precedence:bulk"         # mass mails
    magrep "broadcastSendId:.*"      # mass mails
    magrep "Feedback-ID:.*"          # mass mails
    EOF
    file2 a
    file2 b
    file2 c
    EOF2
    file3 1
    file3 2
    file3 3
    EOF3
    

    If you want to read file line by line the idiomatic way, always set the IFS, so preserve leading and trailing whitespaces and use -r option not to be surprised by \$ beeing expanded, so use:

    while IFS= read -r line
    

    Also the read command you posted will not work, because you have the -n option, which isn't a POSIX option (and has different meanings in zsh and bash -- you're using the zsh version).

    1. Try not to use echo which is not portable, but use printf.
    2. The ` as command substitution is deprecated in bash (it's not bash I know), use $(...) instead, which allows for nesting.