2011-08-16 13 views
49

これがうまくいかない理由を教えてもらえますか?私はファイル記述子で遊んでいるが、少し失われて感じる。ファイルディスクリプタの仕組み

#!/bin/bash 
echo "This" 
echo "is" >&2 
echo "a" >&3 
echo "test." >&4 

最初の3行はうまく動作しますが、最後の2行はエラーになります。どうして?

答えて

63

ファイルディスクリプタ0,1,2はそれぞれstdin、stdout、stderr用です。

ファイル記述子3,4、... 9は追加ファイル用です。それらを使用するには、まずそれらを開く必要があります。例:

exec 3<> /tmp/foo #open fd 3. 
echo "test" >&3 
exec 3>&- #close fd 3. 

詳細はAdvanced Bash-Scripting Guide: Chapter 20. I/O Redirectionを参照してください。

+1

これは私が探しているものです!ですから、execコマンドを使って一時的な格納場所として使用するファイルを指定し、終了したらファイルを閉じる必要があります。申し訳ありませんが、私はexecコマンドに少し曖昧ですが、私はそれをあまり使用しません。 – Trcx

+1

はい、一時的ではありません。このファイルは、プログラムが完了しても存在します。 – dogbane

+0

それでうまくいくかもしれませんが、私はcrontabタスクスケジューラと互換性があるようにいくつかのスクリプトを移植しようとしていますが、cronがスクリプトのstdoutの配管を許可していないので問題があります。 – Trcx

16

これらのファイル記述子が何かを指していないので、失敗しています!通常のデフォルトファイル記述子は、標準入力0、標準出力1、および標準エラーストリーム2です。あなたのスクリプトは他のファイルをオープンしていないので、他の有効なファイル記述子はありません。 execを使ってファイルをbashで開くことができます。

#!/bin/bash 
exec 3> out1  # open file 'out1' for writing, assign to fd 3 
exec 4> out2  # open file 'out2' for writing, assign to fd 4 

echo "This"  # output to fd 1 (stdout) 
echo "is" >&2 # output to fd 2 (stderr) 
echo "a" >&3  # output to fd 3 
echo "test." >&4 # output to fd 4 

そして今、我々はそれを実行します:ここではあなたの例の修正です

$ ls 
script 
$ ./script 
This 
is 
$ ls 
out1 out2 script 
$ cat out* 
a 
test. 
$ 

あなたが見ることができるように、余分な出力が要求されたファイルに送られました。

+0

私はそれを端末に入れて出しを書くかもしれない方法はありますか?私はそれをすべてターミナルで見ることができるようにしたいが、私が望むところで出力を送ることができるようにしたい。すなわち./script 2> out.2 3> out.3 4> out.4 – Trcx

+0

@Trcx、あなたが端末に書き込みたい場合は、 'stdout'または' stderr'を使用しています。なぜあなたはそれのために他のファイルを使用する必要がありますか?しかし、私は出力を書き込むためのcrontabを使用することができ、私はcrontabファイルと互換性を持たせるためにしようとしています –

+0

スクリプトは、複数のファイルを書き出す必要がありますが、crontabファイルは、ファイルがスクリプトからに書き込まれる(それのために標準出力からのサポートが欠けている)することはできません。のスクリプトをファイルに保存します。私はスクリプトをさまざまな出力に書き込ませ、crontabにすべてを適切なファイルに分けさせることができると考えていました。私はstdoutを使用してファイルを書き出すためのクリーバーの方法を探していました。しかし、あなたたちのおかげで、私はそれを思い知らされていることがわかった。 (再び:P)助けてくれてありがとう! – Trcx

35

これは古い質問ですが、の1つが明確化が必要です。

Carl Norumとdogbaneの回答は正しいですが、はスクリプトを変更してにすることを前提としています。私が指摘したいのですがどのような

は、スクリプトを変更する必要はありませんことです:

#!/bin/bash 
echo "This" 
echo "is" >&2 
echo "a" >&3 
echo "test." >&4 

あなたは違っそれを呼び出す場合、それは動作します:

手段
./fdtest 3>&1 4>&1 

ファイル記述子3と4を1にリダイレクトします(標準出力です)。

点は、スクリプトが、これらの記述子が親プロセスによって提供される場合にだけ1及び2(stdoutとstderr)以外の記述子への書き込みたいにおける完全に微細であることです。このスクリプトは、4つの異なるファイルに書き込むことができますので、

あなたの例では、実際には非常に興味深いものです:

./fdtest >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt 

今、あなたは4つの別々のファイルに出力があります。もっと面白いです何

$ for f in file*; do echo $f:; cat $f; done 
file1.txt: 
This 
file2.txt: 
is 
file3.txt: 
a 
file4.txt: 
test. 

あなたのプログラムは実際にそれらを開くわけではないので、それらのファイルに対する書き込み権限を持っている必要はありません。例えば

、私は、ユーザーをrootに変更するルートとしてディレクトリを作成し、このように私の通常のユーザー(私の場合はRSP)として、次のコマンドを実行しようとするために、sudo -sを実行します。

# su rsp -c '../fdtest >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt' 

私はsuの外にリダイレクトを行う場合

bash: file1.txt: Permission denied 

しかし:私はエラーを取得する

# su rsp -c '../fdtest' >file1.txt 2>file2.txt 3>file3.txt 4>file4.txt 

(単一引用符の違いに注意してください)それはを動作し、私が手:

ルートが所有するディレクトリにrootが所有4つのファイルです
# ls -alp 
total 56 
drwxr-xr-x 2 root root 4096 Jun 23 15:05 ./ 
drwxrwxr-x 3 rsp rsp 4096 Jun 23 15:01 ../ 
-rw-r--r-- 1 root root 5 Jun 23 15:05 file1.txt 
-rw-r--r-- 1 root root 39 Jun 23 15:05 file2.txt 
-rw-r--r-- 1 root root 2 Jun 23 15:05 file3.txt 
-rw-r--r-- 1 root root 6 Jun 23 15:05 file4.txt 

- スクリプトは持っていなかったにもかかわらず、アクセス許可を使用してこれらのファイルを作成します。

もう1つの例では、chroot jailまたはコンテナを使用し、ルートとして実行されていてもそれらのファイルにアクセスできないプログラムを実行し、実際にはアクセスせずに必要に応じて外部に引き続きリダイレクトしますファイルシステム全体またはこのスクリプトのその他のものに適用されます。

ポイントはです。あなたは非常に興味深い便利なメカニズムを発見しました。他の回答で示唆されているように、スクリプト内のすべてのファイルを開く必要はありません。スクリプト呼び出し中にリダイレクトすると便利なことがあります。

echo "This" >&1 

としてプログラムを実行している:

echo "This" 

に実際に等価です:これは、それをまとめると

./program >file.txt 

は同じです:

./program 1>file.txt 

数字1はデフォルトの数字であり、標準出力です。

しかし、たとえこのプログラム:

#!/bin/bash 
echo "This" 

は "悪い記述子" エラーを生成することができます。どうやって?実行する場合:

./fdtest2 >&- 

出力は次のようになります

./fdtest2: line 2: echo: write error: Bad file descriptor 

は(1>&-と同じである)、標準出力を閉じる手段>&-を追加します。 2>&-を追加すると、stderrを閉じることになります。

さらに複雑なことすることもできます。あなたの元のスクリプト:

#!/bin/bash 
echo "This" 
echo "is" >&2 
echo "a" >&3 
echo "test." >&4 

だけで実行します。

./fdtest 

プリント:

This 
is 
./fdtest: line 4: 3: Bad file descriptor 
./fdtest: line 5: 4: Bad file descriptor 

しかし、あなたは記述子3と4の作業を行うことができますが、数1が実行して失敗します。

./fdtest 3>&1 4>&1 1>&- 

これは、出力:

./fdtest: line 2: echo: write error: Bad file descriptor 
is 
a 
test. 

あなたがディスクリプタをしたい場合は1と2の両方が、このようにそれを実行し、失敗:

./fdtest 3>&1 4>&1 1>&- 2>&- 

あなたが得る:

a 
test. 

なぜ?何も失敗しませんでしたか? それはをしましたが、無標準エラー出力(ファイルディスクリプタ番号2)であなたは、エラーメッセージが表示されませんでした!

私はそれがどのように記述子とそのリダイレクトの仕事の感覚を得るためにこの方法を試してみることは非常に便利だと思います。

あなたのスクリプトは実際には非常に興味深い例です - 私はは全く壊れていないと主張しています、あなたはそれを間違って使っていました! :)

+0

Vおもしろい応答..ありがとう –

+0

実際には、ファイル記述子1はstdoutです。 stdinはファイル記述子0です。 – programmerjake

+0

@programmerjakeタイプミスが修正されました。それを指摘してくれてありがとう。 – rsp