私は起動時に実行されるいくつかのスクリプトを作業していますrc.local
が、出力リダイレクトが非常に奇妙な方法で動作することを発見しました。
そのように書くと、echo "foo" >&1
システムログに書き込まれ、すべてが正常です。
echo "foo" >>/dev/stdout
しかし、またはと書くと、echo "foo" >>/proc/self/fd/1
No such device or addressというエラーが発生します。
さらなる調査によると、これは/proc/self/fd/1
実際にソケットでした。ls -l /proc/self/fd
印刷コンテンツにはrc.local
以下が含まれます。
lr-x------. 1 root root 64 Jul 14 05:28 0 -> /dev/null
lrwx------. 1 root root 64 Jul 14 05:28 1 -> socket:[18485]
lrwx------. 1 root root 64 Jul 14 05:28 2 -> socket:[18485]
lr-x------. 1 root root 64 Jul 14 05:28 255 -> /etc/rc.d/rc.local
説明された動作はを使用して簡単に再現できますnc
。まず、どのファイル記述子がソケットにバインドされているかを調べます。nc -l -p 25566 -c "ls -l /proc/self/fd"
最初の端末で発行し、telnet localhost 25566
別の端末で発行してこれを行うことができます。私の場合は5番目の記述子です。
[OK] その後、最初の端末で問題を再現するには、次の手順を実行します。
nc -l -p 25566 -c "echo 'hello' >&5"
2番目のターミナルで:
telnet localhost 25566
接続を確立および終了するためのTelnetメッセージ間の2番目の端末出力に「hello」が表示されます。
これで起こるのは、ファイル記述子擬似ファイルが次から来るということです/proc
。
最初の端末:
nc -l -p 25566 -c "echo 'hello' >/proc/self/fd/5"
2番目のターミナル:
telnet localhost 25566
2番目の端末には接続が確立され、すぐに閉じるためのTelnetメッセージのみが含まれ、最初の端末にはエラーが表示されますsh: /proc/self/fd/5: No such device or address
。
編集する:オペレーティングシステムはFedora 23 i686サーバーです。
さて、ここで問題が発生します。>&1
これらとそしての違いは何ですか>/proc/self/fd/1
?現在の標準出力と正確に一致するいくつかのファイルに出力をリダイレクトする一般的で信頼性の高い方法はありますか?
ありがとうございます。
編集2:
明確にするために、上記のシナリオでは、Fedora 23 i686の正確な入力/出力は次のとおりです。
Terminal 1 | Terminal 2
$ nc -l -p 25566 -c 'ls -go /proc/self/fd' |
| $ telnet localhost 25566
| Trying ::1...
| Connected to localhost.
| Escape character is '^]'.
| total 0
| lr-x------ 1 64 Jul 14 08:56 0 -> pipe:[19687]
| l-wx------ 1 64 Jul 14 08:56 1 -> pipe:[19688]
| lrwx------ 1 64 Jul 14 08:56 2 -> /dev/tty2
| lr-x------ 1 64 Jul 14 08:56 3 -> pipe:[19687]
| lr-x------ 1 64 Jul 14 08:56 4 -> /proc/1285/fd
| lrwx------ 1 64 Jul 14 08:56 5 -> socket:[19686]
| l-wx------ 1 64 Jul 14 08:56 7 -> /pipe:[19688]
| Connection closed by foreign host.
$ nc -l -p 25566 -c "echo 'hi' >&5" |
| $ telnet localhost 25566
| Trying ::1...
| Connected to localhost.
| Escape character is '^]'.
| hi
| Connection closed by foreign host.
$ nc -l -p 25566 -c "echo 'hi' >/proc/self/fd/5" |
| $ telnet localhost 25566
| Trying ::1...
| Connected to localhost.
sh: /proc/self/fd/5: No such device or address | Escape character is '^]'.
| Connection closed by foreign host.
$ | $
答え1
使用>&N
。これは移植可能で、見てわかるように、実際にソケットと連携して動作します。
使用する唯一の理由/proc/self/fd
は、ファイル名が必要なプログラムを実行していて、すでに開いているファイル記述子を使用するように指示されない場合です。たとえば、ほとんど<(cmd...)
すべてのコマンドラインユーティリティは名前で指定されたファイルを開くことができますが、すべてのコマンドラインユーティリティが事前に開かれたファイル記述子を直接サポートしているわけではないため、リダイレクトはそれを使用します。
ただし、シェルは既存のファイル記述子を使用できるため/proc
。
また、これはLinuxに固有のものであり、私のLinuxシステムには、および他のものはなどへのシンボリックリンクなので、インストールする必要があります/proc/NNN/fd/
。他のUnixでは異なる場合があります。 ~によると/proc
/dev/stdout
/dev/fd/*
/proc/self/fd/*
/proc
古い質問への回答 /dev/stdout
POSIX の外部に明示的にリストされます。
リダイレクトが必要に応じて機能しない理由は次のとおりです。を試してください。 2strace
つの違いは、>&N
リダイレクトの場合はファイル記述子がbash
呼び出され、.txtを使用して通常のファイルとして開こうとすることです。 。このような新しいソケットコピーを開くことは明らかにサポートされていないため、呼び出しは失敗します。ストリームソケットはほぼポイントツーポイントリンクなので、新しいコピーを開くことを無効にすることはそれほど不自然に見えません。しかし、なぜパイプやと一緒に動作するのかわかりません。dup()
>/proc/self/fd/N
open()
proc
dup
$ ls -l /proc/self/fd/3
lrwx------ 1 itvirta itvirta 64 Jul 14 18:24 /proc/self/fd/3 -> socket:[168157]
$ strace bash -c "echo foo >>/proc/self/fd/3" 2>&1 | grep open.*proc/self
open("/proc/self/fd/3", O_WRONLY|O_CREAT|O_APPEND, 0666) = -1 ENXIO (No such device or address)
返品この回答移植性に関するいくつかの情報があります。/proc/NNN/fd
答え2
3>&1
既存のリダイレクトの繰り返しと同様ファイル記述子:これは同じオープンファイル(同じファイル、同じフラグ、同じ場所など)を取得し、プログラムの他の「出力ポート」(別のファイル記述子番号)に挿入します。 (より正確には、同じファイル記述子を指す新しいファイル記述子を生成します。)ファイル説明しかし、私たちはそうではありません)
>&1
記述子をそれ自体にコピーし、一部のシェルはそれを完全に最適化します。
>/proc/$pid/fd/1
ファイルを開くなどのリダイレクトです/proc/$pid/fd/1
。これにより、新しいファイル記述子が生成されます。のファイルは、/proc/*/fd
ファイルを開くときにデフォルトで既存のファイル記述子のデータをコピーするという点で特別です。これらのファイルはシンボリックリンクですが、「魔法」です。たとえば、削除されたファイルまたはパイプは壊れたシンボリックリンクとして表示されますが、カーネルに/proc/*/fd
アイテムを処理する特別なコードが含まれているため、まだ既存のファイルのように開くことができます。したがって、ほとんどの場合と>&1
は>/proc/self/fd/1
同じです。しかし、ソケットは異なって扱われます。。
/*
* In theory you can't get an open on this inode, but /proc provides
* a back door. Remember to keep it shut otherwise you'll let the
* creepy crawlies in.
*/
static int sock_no_open(struct inode *irrelevant, struct file *dontcare)
{
return -ENXIO;
}
(最近のバージョンではコードが再構成されていますが、まだソケットを通過しても開くことはできません/proc/PID/fd/NUM
。)
他のほとんどのファイル形式のようにソケットを開くことができない理由は、ソケットに詳細情報が添付されていることを確認し、ソケットを開く方法を教えてください。たとえば、TCPソケットを開くと、ソースポートが割り当てられます。場合によっては、これは意味があるかもしれませんが、Linuxカーネルは他のプロセスのソケットを開くことを許可しません。同じプロセス(/proc/self/fd/NUM
代わりに)の場合、/proc/OTHERPID/fd/NUM
呼び出しはopen
一般的なファイル記述子の冗長性に変換できますが、/proc/self/fd
最初に開くのは珍しいことであり、通常は機能の余裕がないため、ソケットリダイレクトは行われません。したがって、カーネルはこの合理的ですが、役に立たない例外をサポートするようには設計されていません。