私はあなたがファイル記述子を作成し、それに出力をリダイレクトできることを知っています。例えば.
exec 3<> /tmp/foo # open fd 3.
echo a >&3 # write to it
exec 3>&- # close fd 3.
しかし、ファイル記述子がなくても同じことができます。
FILE=/tmp/foo
echo a > "$FILE"
追加のファイル記述子を使用する必要がある場合の良い例を探しています。
ほとんどのコマンドには、単一の入力チャネル(標準入力、ファイル記述子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
興味深い他の例:
そしてさらに多くの例については:
io-redirection
のタグが付いた質問file-descriptors
のタグが付いた質問追伸これは、fd 3を介したリダイレクトを使用するサイトでの 最も投票された投稿の著者からの意外な質問です !
以下は、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"
名前付きパイプ(fifos)のコンテキストでは、追加のファイル記述子を使用すると、非ブロッキングパイプ動作を有効にできます。
(
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
余分なファイル記述子は、変数のstdoutをキャッチしたいが、画面に出力したい場合(bashスクリプトのユーザーインターフェイスなど)に適しています。
arg1 string to echo
arg2 flag 0,1 print or not print to 3rd fd stdout descriptor
function ecko3 {
if [ "$2" -eq 1 ]; then
exec 3>$(tty)
echo -en "$1" | tee >(cat - >&3)
exec 3>&-
else
echo -en "$1"
fi
}
(Bashで)追加のファイル記述子を使用することが適切であると思われる場合のさらに別のシナリオを次に示します。
コマンドラインパラメータのシェルスクリプトパスワードセキュリティ
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
1つの例は、ファイルロックを利用して、システム全体でスクリプトを強制的に逐次実行することです。これは、同じ種類の2つのスクリプトで同じファイルを操作したくない場合に便利です。そうしないと、2つのスクリプトが互いに干渉し、データが破損する可能性があります。
#exit if any command returns a non-zero exit code (like flock when it fails to lock)
set -e
#open file descriptor 3 for writing
exec 3> /tmp/file.lock
#create an exclusive lock on the file using file descriptor 3
#exit if lock could not be obtained
flock -n 3
#execute serial code
#remove the file while the lock is still obtained
rm -f /tmp/file.lock
#close the open file handle which releases the file lock and disk space
exec 3>&-
このロック/ロック解除ロジックを再利用可能な関数にラップすることもできます。次の trap
Shell builtin は、スクリプトが終了すると(エラーまたは成功)、ファイルロックを自動的に解放します。 trap
は、ファイルロックのクリーンアップに役立ちます。パス /tmp/file.lock
は、ハードコードされたパスである必要があります。これにより、複数のスクリプトがパスをロックしようとする可能性があります。
# obtain a file lock and automatically unlock it when the script exits
function lock() {
exec 3> /tmp/file.lock
flock -n 3 && trap unlock EXIT
}
# release the file lock so another program can obtain the lock
function unlock() {
# only delete if the file descriptor 3 is open
if { >&3 ; } &> /dev/null; then
rm -f /tmp/file.lock
fi
#close the file handle which releases the file lock
exec 3>&-
}
上記のunlock
ロジックは、ロックが解除される前にファイルを削除することです。これにより、ロックファイルがクリーンアップされます。ファイルが削除されたため、このプログラムの別のインスタンスがファイルロックを取得できます。
次の例のようにスクリプトで使用できます。
#exit if any command returns a non-zero exit code (like flock when it fails to lock)
set -e
#try to lock (else exit because of non-zero exit code)
lock
#system-wide serial locked code
unlock
#non-serial code
ロックできるようになるまでコードを待機させたい場合は、次のようにスクリプトを調整できます。
set -e
#wait for lock to be successfully obtained
while ! lock 2> /dev/null; do
sleep .1
done
#system-wide serial locked code
unlock
#non-serial code
具体的な例として、サブコマンドからのタイミング情報を必要とするスクリプトを書いただけです。追加のファイル記述子を使用すると、サブコマンドのstdoutまたはstderrを中断することなく、time
コマンドのstderrをキャプチャできました。
(time ls -9 2>&3) 3>&2 2> time.txt
これは、ls
のstderrをfd 3にポイントし、fd 3をスクリプトのstderrにポイントし、time
のstderrをファイルにポイントします。スクリプトを実行すると、そのstdoutとstderrはサブコマンドと同じになり、通常どおりリダイレクトできます。 time
の出力のみがファイルにリダイレクトされます。
$ echo '(time ls my-example-script.sh missing-file 2>&3) 3>&2 2> time.txt' > my-example-script.sh
$ chmod +x my-example-script.sh
$ ./my-example-script.sh
ls: missing-file: No such file or directory
my-example-script.sh
$ ./my-example-script.sh > /dev/null
ls: missing-file: No such file or directory
$ ./my-example-script.sh 2> /dev/null
my-example-script.sh
$ cat time.txt
real 0m0.002s
user 0m0.001s
sys 0m0.001s