私はUnixのコマンドラインを知りません。私が見つけたこの回答標準出力に出力せずにコマンドを使用するには、tee
次のように「標準出力オフ」を渡します>&-
。
echo 'hello world' | tee aa bb cc >&-
たくさん研究して見つけました。その他の投稿演算子の機能についてお問い合わせください。残念ながら、詳細な回答が見つかりません。
私が知っているのは閉鎖されたstdout
だけですが、それが正確に何を意味するのか、なぜ誰かがそうしようとするのかはわかりません。これが役に立つかもしれない他のシナリオが何であるかを知りたいです。
その演算子の内部操作についてさらに学ぶことができるリソースはありますか?
答え1
引用するsh
言語のPOSIX仕様:
2.7.6 出力ファイル記述子のコピー
リダイレクト演算子:
[n]>&word
出力ファイル記述子は他の出力ファイル記述子からコピーする必要があります。または1つ閉じる。単語が1つ以上の数値として評価されると、nまたは標準出力(nが指定されていない場合)で表されるファイルディスクリプタは、単語の数字がForを使用して開かれたファイルを表していない場合、単語として表示されるファイルディスクリプタのコピーになります。出力ファイル記述子でリダイレクトエラーが発生します。シェルエラーの結果を参照してください。単語が「-」と評価されると、ファイル記述子nまたは標準出力(nが指定されていない場合)が閉じます。開いていないファイル記述子を閉じようとすることは、エラーと見なすべきではありません。単語が異なると評価された場合、アクションは指定されません。
(強調)。
stdoutのファイル記述子である1の略語も同じ>&-
です。1>&-
だから:
cmd >&-
シェルはclose(1)
後で実行されるプロセスでこれを行いますcmd
。したがってcmd
、fd 0と2は起動時に開かれます(それぞれstdinとstderrなので、stdoutは通常、慣例に従ってコマンドがデフォルトで入力を許可し、デフォルトで間違った場所を送信すると予想するため、常に何かにあります)。 )、しかしFD 1はオフになっています。ファイルディスクリプタ3以上も閉じることができます。
つまり、cmd
ファイルが開かれると(たとえばopen("some-file", O_WRONLY, 0600)
)、最初の無料ファイル記述子で開き、fdは1になります。これはsome-file
後で標準出力の位置になることを意味します。たとえば、printf("Please enter your name: ")
後で発生した場合some-file
!
これは(数十年間)setuid実行可能ファイルを悪用する方法でした。
たとえば、chsh
setuid 実行可能ファイルを/etc/passwd
編集して、ユーザーの要求に応じてユーザーのログインシェルを変更できます。
そして:
chsh >&-
chsh
/etc/passwd
fd 1でaを開き、そこにいるユーザーに送信するメッセージを書くこともできます。
この問題を認識した後、ソフトウェアは起動時にfd 0、1、または2が閉じられていることを確認し、閉じている場合は開いて/dev/null
それを防止し始めました。
私はこれが少なくともsetuid実行可能ファイルまたは他の機密性の高いコンテキストで実行されていることを検出するときにGNU libc(GNUシステムのすべての実行可能ファイルで使用される)が実行する操作であることを覚えているようですが、今は確認できません。 I 彼らはそれを誤って覚えたか、行動を変えました。
とにかくtee
今GNUソースコードを見ると、書き込み用にファイルを開くときに次のように使用します。fopen_safer()
gnulibのバリアント(GNU libcではない)fopen()
、fd fopen()
<= 2を使用している場合は、2よりも高いfdに移動します。
したがって、少なくともtee aa bb cc >&-
この実装を使用すると、2(おそらく3)以上の最初のfdが開き、次のfdが開くように進み、1は閉じたままになるため、次のことがわかります。tee
aa
bb
$ echo test | tee aa bb cc >&-
tee: 'standard output': Bad file descriptor
tee
fd 1(stdout)が閉じている間に書き込めないときにエラーを報告します。
busyboxの場合はgnulibのものをtee
使わず、fopen_safer()
$ echo test | busybox tee aa bb cc >&-
$ cat aa
test
test
aa
実際にはfd 1にあり、これがtest
そこに2回記録される理由を説明します。一度はstdout(fd 1)に書き込まれたため、もう1つは開かれたfd aa
(またfd 1)に書き込まれたからです。
tee
stdout(または他のコマンド)を削除するには、次のことが必要であることを明確にしてください。いいえ閉じて/ dev / nullにリダイレクトします。
echo test | tee aa bb cc > /dev/null
しかし、ここでは捨てるのではなく、次のようにすることができます。
echo test | tee aa bb > cc
echo test | tee aa bb cc >&-
tee
前述のように、保護対策としてstdoutが閉じられたことを検出して/dev/nullで再度開く実装の場合にのみ、これはGNUtee
やbusyboxの場合ではありませんtee
(少なくとも現在のバージョンのGNUライブラリ)。
stdout(またはstdinまたはstderr)をオフにするいくつかの理由は次のとおりです。
実行時に権限を向上させますが、これらの病理学的ケースでは正しく機能しないsetuid / setgidまたはより一般的なソフトウェアを悪用します。
ソフトウェアをテストして、これらの条件で正しく機能していることを確認してください。
in では、
zsh
出力cmd1 > file | cmd2
をパイプとパイプのcmd1
両方に送信します(内部的に実行され、より柔軟であることを除いて、ingと似ています)。file
cmd2
cmd1 | tee file | cmd2
tee
cmd1
stderrをに送信してstdoutをに送信しようとすると、stderrはに送信され、stdoutはに送信cmd2
できません。file
cmd1 2>&1 > file | cmd2
cmd2
file
cmd2
tee
以下を実行して無効にできます。cmd1 2>&1 >&- > file | cmd2
次のようにすることもできます。
{ cmd1 2>&1 > file; } | cmd2
または
cmd1 > file 2> >(cmd2)
あるいは、1<&-
その問題との間>&-
の唯一の違いは、指定されてい<&-
ない場合に機能するfdです(それぞれ1と0)。彼らはすべて1つだけを作りましたclose(fd)
。