これがうまくいかない理由を教えてもらえますか?私はファイル記述子で遊んでいるが、少し失われて感じる。ファイルディスクリプタの仕組み
#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4
最初の3行はうまく動作しますが、最後の2行はエラーになります。どうして?
これがうまくいかない理由を教えてもらえますか?私はファイル記述子で遊んでいるが、少し失われて感じる。ファイルディスクリプタの仕組み
#!/bin/bash
echo "This"
echo "is" >&2
echo "a" >&3
echo "test." >&4
最初の3行はうまく動作しますが、最後の2行はエラーになります。どうして?
ファイルディスクリプタ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を参照してください。
これらのファイル記述子が何かを指していないので、失敗しています!通常のデフォルトファイル記述子は、標準入力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.
$
あなたが見ることができるように、余分な出力が要求されたファイルに送られました。
私はそれを端末に入れて出しを書くかもしれない方法はありますか?私はそれをすべてターミナルで見ることができるようにしたいが、私が望むところで出力を送ることができるようにしたい。すなわち./script 2> out.2 3> out.3 4> out.4 – Trcx
@Trcx、あなたが端末に書き込みたい場合は、 'stdout'または' stderr'を使用しています。なぜあなたはそれのために他のファイルを使用する必要がありますか?しかし、私は出力を書き込むためのcrontabを使用することができ、私はcrontabファイルと互換性を持たせるためにしようとしています –
スクリプトは、複数のファイルを書き出す必要がありますが、crontabファイルは、ファイルがスクリプトからに書き込まれる(それのために標準出力からのサポートが欠けている)することはできません。のスクリプトをファイルに保存します。私はスクリプトをさまざまな出力に書き込ませ、crontabにすべてを適切なファイルに分けさせることができると考えていました。私はstdoutを使用してファイルを書き出すためのクリーバーの方法を探していました。しかし、あなたたちのおかげで、私はそれを思い知らされていることがわかった。 (再び:P)助けてくれてありがとう! – Trcx
これは古い質問ですが、の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)であなたは、エラーメッセージが表示されませんでした!
私はそれがどのように記述子とそのリダイレクトの仕事の感覚を得るためにこの方法を試してみることは非常に便利だと思います。
あなたのスクリプトは実際には非常に興味深い例です - 私はは全く壊れていないと主張しています、あなたはそれを間違って使っていました! :)
Vおもしろい応答..ありがとう –
実際には、ファイル記述子1はstdoutです。 stdinはファイル記述子0です。 – programmerjake
@programmerjakeタイプミスが修正されました。それを指摘してくれてありがとう。 – rsp
これは私が探しているものです!ですから、execコマンドを使って一時的な格納場所として使用するファイルを指定し、終了したらファイルを閉じる必要があります。申し訳ありませんが、私はexecコマンドに少し曖昧ですが、私はそれをあまり使用しません。 – Trcx
はい、一時的ではありません。このファイルは、プログラムが完了しても存在します。 – dogbane
それでうまくいくかもしれませんが、私はcrontabタスクスケジューラと互換性があるようにいくつかのスクリプトを移植しようとしていますが、cronがスクリプトのstdoutの配管を許可していないので問題があります。 – Trcx