ファイル記述子が有効かどうかのテスト


12

bashスクリプトに、ファイル記述子(FD)が開いているときに、3以上の追加情報を出力させたいと思います。FDが開いているかどうかをテストするために、次のトリックを考案しました。

if (printf '' 1>&3) 2>&-; then
  # File descriptor 3 is open
else
  # File descriptor 3 is not open
fi

私のニーズにはこれで十分ですが、FDが有効かどうかをテストするより慣用的な方法があるかどうかについて知りたいです。私は特にのマッピングが存在するか否かについての興味fcntl(1)(FDフラグの検索を可能にするシェルコマンドにシステムコールを、O_WRONLY及び O_RDWRFDが書き込み可能であるかどうかをテストし、O_RDONLY及び O_RDWRFDが読み取り可能であるかどうかをテストします)。

回答:


12

ではksh(両方のAT&Tとpdkshの変種)またはzsh、あなたが行うことができます。

if print -nu3; then
  echo fd 3 is writeable
fi

それらはそのfdに何も書き込みませんが、fdが(を使用してfcntl(3, F_GETFL))書き込み可能かどうかを確認し、それ以外の場合はエラーを報告します。

$ ksh -c 'print -nu3' 3< /dev/null
ksh: print: -u: 3: fd not open for writing

(これにリダイレクトできます/dev/null)。

bash、あなたの唯一のオプションはdup()、あなたのアプローチのように成功したかどうかをチェックすることですが、それはfdが書き込み可能であることを保証しません(または外部ユーティリティ(zsh/ perl...)を呼び出して)を行いますfcntl())。

bash(ほとんどのシェルと同様に)では、の(...)代わりにを使用すると{...;}、追加のプロセスがフォークされることに注意してください。以下を使用できます。

if { true >&3; } 2<> /dev/null

代わりに、フォークを回避します(複合コマンドをリダイレクトすると常にサブシェルが発生するBourneシェルを除く)。使用しないでください:代わりにtrueそれはだとして特別 bashはPOSIX準拠モードであるときに組み込み、その出口までの殻を引き起こします。

ただし、次のように短縮できます。

if { >&3; } 2<> /dev/null

@ mikeserve、re:あなたの編集、それは何<>ですか?シェルはstderrから読み取らないので、なぜそれをread + writeで開きますか?内因性に何が起こったかとはどういう意味ですか?
ステファンChazelas

7

POSIX アプリケーションの使用法の説明には、次のものがあります。command

場合によっては、特別な組み込み機能の特別な特性を抑制することにはいくつかの利点があります。例えば:

command exec > unwritable-file

非インタラクティブスクリプトが異常終了しないので、スクリプトで出力ステータスを確認できます。

これがあなたがただできる理由です:

if    command >&3
then  echo 3 is open >&3
else  ! echo 3 is not open
fi    2<>/dev/null

または...

{ command >&3
  printf %s\\n%.0d  string "0$(($??8:0))" >&"$(($??1:3))"
} 2<>/dev/null

書きますどの文字列が続い\nSTDOUTにまたは3のいずれかewline、まだ数学の上で行わので3が開いていないときにゼロ以外の終了ステータスに渡す$?進変換するのに失敗し、最大風08まで%の小数まったく何にもなく、切り捨て8進数00

または...

command exec >&3 || handle_it

ただし、を使用している場合はksh93、次のようにすることができます。

fds

開いているファイル記述子のリスト。追加-lして、彼らがどこに行くのかを確認します。


3

開いているファイル記述子は、にあり/proc/<pid>/fdます。たとえば、発行できる現在のシェルの開いているファイル記述子を一覧表示すると、ls -l /proc/$$/fd次のようになります。

total 0
lrwx------ 1 testuser testuser 64 jun  1 09:11 0 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:11 1 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:11 2 -> /dev/pts/3
lrwx------ 1 testuser testuser 64 jun  1 09:39 255 -> /dev/pts/3

次を使用してファイルを開く場合:

touch /tmp/myfile
exec 7</tmp/myfile

新しいリストに表示されますls -l /proc/$$/fd

lr-x------ 1 testuser testuser 64 jun  1 09:11 7 -> /tmp/myfile

再び使用しexec 7>&-てファイル記述子を閉じると、リストに表示され/proc/$$/fdなくなります。


2
これはすべてLinuxに固有のものです。FWIW。
lcd047

1
LinuxおよびSolaris(10および11)でテストしました。違いは、Linuxで接続pfiles <pid>ls -l表示しながら、どのファイル記述子がどのファイルに接続されているかを確認するために使用する必要があることです。
ランバート

のコンパクトさは[ -e /proc/$$/fd/3 ]好きですが、FreeBSDや他のun * cesでも非推奨となっているため、procfsに依存しないことを好みます。
Witiko

1
使用する代わりに、pfiles <pid>またはlsof -p <pid>どのファイル記述子が開いているかを確認します。
ランバート

1
/procOpenBSDにはまったく存在しません。FreeBSDおよびNetBSDではmount、明示的に-edする/proc/<PID>必要があり、サブディレクトリはありませんfd
lcd047

3

あなたのトリックはかわいいですね。しかし、慣用的な方法で、なぜあなたが使用しなかったのか疑問に思います:

if ( exec 1>&3 ) 2>&-

これは確かに、よりクリーンな方法です。
Witiko

5
これはサブシェルを作成しますが、これはほとんどのシェルがプロセスをフォークすることを意味します。これはfdが書き込み可能であることを保証するものではありません。{ true >&3; } 2> /dev/nullフォークを回避するために使用できます。または{ command exec >&3; } 2> /dev/null、stdoutをそれにリダイレクトする場合。
ステファンChazelas

@Stephane; @Witikoが発明したサブシェルトリックは、リダイレクトを使用してリダイレクトを取得するときに、現在の環境のファイル記述子に影響を与えないことでした。- あなたが言及する「書き込み可能なfd」について詳しく教えてください。
Janis

2
{ true >&3; } 2> /dev/null現在の環境にも影響せず、フォークしません(Bourneシェルを除く)。つまり(exec 1>&3) 2>&-、読み取り専用モードでfdを開くとtrueが返されます。
ステファンChazelas

1
exec特別な組み込みであると、失敗するとシェルを終了します(bashの場合、POSIX準拠モードの場合のみ)。command execそれを防ぎます。true特別な組み込みではありません。現在の環境に影響を与えることに注意しexeccommand execください(それが、標準出力をそこにリダイレクトする場合に私が言った理由です)。
ステファンChazelas

-1

繰り返し使用するためにフォークの少ないソリューションに興味がある場合は、この機能をお勧めします。

checkfd(){
    exec 2> / dev / null
    execの場合>&3; その後
        exec 1> / dev / tty
        エコー "fd3 OK"
    そうしないと
        エコー "fd3 KO"
    fi
    exec 2> / dev / tty
}

そして、これは次のもので生成されますzsh

$ checkfd            
fd3 KO
$ checkfd 3> / dev / null
fd3 OK
ドル

ほとんどのシェルでexec >&3は、3が開いていないときにシェルを強制終了します。
mikeserv 2015年

少なくともそれが取り組んでいるzshbash。失敗のexec原因となったシェルを提供できますexitか?
dan

うん。bash行いset -o posix、再試行してください。でzsh...私はそれが環境POSIX_BUILTINS変数をnullでない値に設定することの問題だと思います-しかし私は手放し忘れます。いずれにしても、zshはPOSIX準拠を試みるシェルではないため、明らかに非標準です。これらのシェルはどちらも、一部のユーザーが便利だと信じているものとの互換性を避けています。
mikeserv 2015年

プレーンボーンシェルにも取り組んでいます。
dan

bashではset -o posix、試してみると成功します。
dan

-1

これは非常に簡単に見えます(コメントを参照):

[ -r /proc/$$/fd/$FD ] && echo "File descriptor $FD is readable"
[ -w /proc/$$/fd/$FD ] && echo "File descriptor $FD is writable"

追加として... [-r file]テストは、実際に読み取りを待機しているデータがあるかどうかを示しません(/ dev / nullがこのテストに合格します(コメントを参照))。

[ -r /proc/$$/fd/4 ] \
  && [ read -t 0.0001 -N 0 <&4 ] \
  && echo "Data is waiting to be read from file descriptor 4"

タイムアウト引数(read -t)にはいくつかの小さな数値が必要です。そうでない場合、何らかの計算が必要なデータが失われる可能性があります。読み取り可能なテスト([-r file])が必要です。そうでない場合、ファイルが読み取り可能でない場合、読み取りコマンドが爆破します。バイト数がゼロであるため、これは実際にはデータを読み取りません(-N 0を読み取ります)。


Linuxシステムを想定している場合は/proc/<pid>/fdinfo/<fd>、ですべてのオープンファイルモードがリストされているもflags:ご覧くださいこちらを参照してください。なぜ第2部(明白な間違いを修正した後でも)の理由:read -t .1 -N0 <&4fd 4で読み取るデータがあるかどうかはわかりません:だけ試してください4</dev/null
モスビー

そしてもちろん、[ -r /proc/$$/fd/$FD ]ファイルディスクリプタがいるかどうかを教えてくれていない$FD読みやすいですが、それはから開いていたファイルがあることができれば、再び開いて読み取るため、別のファイルディスクリプタで、:exec 7>/tmp/foo; [ -r /proc/$$/fd/7 ] && echo fd 7 can be read from && cat <&7
mosvy

-1

質問はかなり古いです-とにかく-なぜビルトインを使用しないのですか?

for i in {0..5} ; do if [ -t $i ]; then echo "$i is a valid FD"; else echo "$i is INVALID FD"; fi; done

出力:

0 is a valid FD
1 is a valid FD
2 is a valid FD
3 is INVALID FD
4 is INVALID FD
5 is INVALID FD

だから、質問に答えるために-提案するでしょう:

if [ -t 3 ]; then
  # File descriptor 3 is open
else
  # File descriptor 3 is not open
fi

-tファイル記述子が有効かどうかはテストしませんが、ttyに接続されているかどうかはテストします。echo yup |スクリプトの前にa を付けると、と表示されますが0 is INVALID FD、実際には非常に有効なfd、パイプです。
モスビー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.