追加のファイル記述子はいつ使用されますか?

追加のファイル記述子はいつ使用されますか?

ファイル記述子を作成し、出力をそのファイルにリダイレクトできることを知っています。例えば

exec 3<> /tmp/foo # open fd 3.
echo a >&3 # write to it
exec 3>&- # close fd 3.

ただし、ファイル記述子がなくても同じことができます。

FILE=/tmp/foo
echo a > "$FILE"

追加のファイル記述子を使用する必要がある場合の良い例を探しています。

答え1

ほとんどのコマンドは、入力チャンネル(標準入力、ファイル記述子0)と出力チャンネル(標準出力、ファイル記述子1)を持っているか、独自に開く複数のファイルで動作します(したがってファイル名を渡します)。 (これは通常ユーザーにフィルタリングする標準エラー(fd 2)に追加されます。)ただし、時には複数のソースまたは複数のターゲットでフィルタとして機能する単一のコマンドを使用するのが便利な場合があります。たとえば、次は、ファイル内の奇数行と偶数行を区別する単純なスクリプトです。

while IFS= read -r line; do
  printf '%s\n' "$line"
  if IFS= read -r line; then printf '%s\n' "$line" >&3; fi
done >odd.txt 3>even.txt

では、奇数行と偶数行に異なるフィルタを適用したいとしましょう(しかし、再結合はしません。これは他の問題になる可能性があり、通常はシェルでは実行できません)。シェルでは、あるコマンドの標準出力を別のコマンドにのみパイプできます。他のファイル記述子をパイプするには、まずfd 1にリダイレクトする必要があります。

{ while … done | odd-filter >filtered-odd.txt; } 3>&1 | even-filter >filtered-even.txt

もう一つの簡単なユースケースはコマンドのエラー出力フィルタリング

exec M>&Nスクリプトの残りの部分に対して、あるファイル記述子を別のファイル記述子にリダイレクトします(または別のコマンドがファイル記述子を再変更するまで)。exec M>&Nとの間には機能がいくつか重複していますsomecommand M>&N。このexec形式は入れ子にする必要がないため、より強力です。

exec 8<&0 9>&1
exec >output12
command1
exec <input23
command2
exec >&9
command3
exec <&8

興味があるかもしれない他の例:

より多くの例があります:

PS これは著者が提起した驚くべき質問です。fd 3を介したリダイレクトを使用するサイトで最も承認された投稿

答え2

以下は、bashスクリプトチャットコントロールとして追加のFDを使用する例です。

#!/bin/bash

log() {
    echo $* >&3
}
info() {
    echo $* >&4
}
err() {
    echo $* >&2
}
debug() {
    echo $* >&5
}

VERBOSE=1

while [[ $# -gt 0 ]]; do
    ARG=$1
    shift
    case $ARG in
        "-vv")
            VERBOSE=3
        ;;
        "-v")
            VERBOSE=2
        ;;
        "-q")
            VERBOSE=0
        ;;
        # More flags
        *)
        echo -n
        # Linear args
        ;;
    esac
done

for i in 1 2 3; do
    fd=$(expr 2 + $i)
    if [[ $VERBOSE -ge $i ]]; then
        eval "exec $fd>&1"
    else
        eval "exec $fd> /dev/null"
    fi
done

err "This will _always_ show up."
log "This is normally displayed, but can be prevented with -q"
info "This will only show up if -v is passed"
debug "This will show up for -vv"

答え3

名前付きパイプのコンテキストでは、非ブロックパイプの動作は追加のファイル記述子を使用して達成されます。

(
rm -f fifo
mkfifo fifo
exec 3<fifo   # open fifo for reading
trap "exit" 1 2 3 15
exec cat fifo | nl
) &
bpid=$!

(
exec 3>fifo  # open fifo for writing
trap "exit" 1 2 3 15
while true;
do
    echo "blah" > fifo
done
)
#kill -TERM $bpid

望むより:名前付きパイプはスクリプトで早期に閉じられましたか?

答え4

これは、追加のファイル記述子を使用することが適切に見える別のケースです。

コマンドラインパラメータのシェルスクリプトパスワードセキュリティ

env -i bash --norc   # clean up environment
set +o history
read -s -p "Enter your password: " passwd
exec 3<<<"$passwd"
mycommand <&3  # cat /dev/stdin in mycommand

関連情報