Bashプロセスの置き換え:標準入力を移植可能に参照する方法は?

Bashプロセスの置き換え:標準入力を移植可能に参照する方法は?

ここで問題は勇敢な次の文。私がこれをやろうとしているとしましょう:

diff -u <(some command) {mystery-syntax}

stdinの出力をsome command端末のコピー/貼り付けなどの一部のデータと比較しています。このように:

$ diff -u <(echo foo) {mystery-syntax}
bar
[Ctrl-D]
--- /dev/fd/63  2021-11-04 11:28:19.360366909 -0700
+++ /dev/fd/62  2021-11-04 11:28:19.360366909 -0700
@@ -1 +1 @@
-foo
+bar

{mystery-syntax}メタトークンはどのような実際の構文を扱いますか?

このdiffユーティリティは標準入力指定規則を使用しないため-使用できません。

1つの答えは次のとおりです/dev/fd/0

$ diff -u <(echo foo) /dev/fd/0
bar
[Ctrl-D]
--- /dev/fd/63  2021-11-04 11:31:02.837040697 -0700
+++ /dev/fd/0   2021-11-04 11:30:56.807964644 -0700
@@ -1 +1 @@
-foo
+bar

さて、私たちはLinuxベースのシステムで一方向に動作するようにしました/dev/fdしかし、ファイルシステムを参照しない方法はありますか/dev(これはシステム固有の実装の詳細です)?私は例外があると思います/dev/fd。このマニュアルには次の内容が記載されています。

プロセス置換は、名前付きパイプ(FIFO)または/ dev / fd名前付きファイルを開く方法をサポートするシステムでサポートされています。

たとえば、次のようになります。

diff <(echo foo) <(cat)

何らかの理由で動作しません。<(cat)プロセスが必要な出力のみを置き換えても、catまだcat標準入力なしで呼び出されます。

$ diff -u <(echo foo) <(cat)
cat: -: Input/output error
--- /dev/fd/63  2021-11-04 11:33:59.303347814 -0700
+++ /dev/fd/62  2021-11-04 11:33:59.303347814 -0700  
@@ -1 +0,0 @@
-foo

ここにも「スマートな」行動があります。明示的に入力/dev/fd/0にリダイレクトしても、まだ退屈です。cat

$ diff -u <(echo foo) <(cat < /dev/fd/0)
cat: -: Input/output error

しかし!ここで文書を標準入力として提供することはcat妨げられません。

$ diff -u <(echo foo) <(cat <<<"bar")
--- /dev/fd/63    2021-11-04 11:40:03.612616179 -0700
+++ /dev/fd/62    2021-11-04 11:40:03.612616179 -0700
@@ -1 +1 @@
-foo
+bar

答え1

diffコマンドラインで指定されたファイル名の1つが-(ダッシュ)の場合、ユーティリティは標準入力からその場所を読み取ります。

$ echo hello | diff -u <( echo ok ) -
--- /dev/fd/12  Fri Nov 12 21:25:18 2021
+++ -   Fri Nov 12 21:25:18 2021
@@ -1 +1 @@
-ok
+hello

GNUdiffマニュアルから:

a がFILEあれば-標準入力を読みます。

OpenBSDマニュアルから:

file1または、この場合、file2標準-入力が代わりに使用されます。

POSIX規格では:

file1file2
比較するファイルのパス名。file1ORfile2オペランドがある場合は、標準-入力を代わりに使用する必要があります。

答え2

diff -u <(echo foo) <(cat)bashプロセス置換コマンドがフォアグラウンドにインポートされず、端末デバイスから読み取れないため、対話的に機能しません。cat &WHEREがバックグラウンドで実行されている場合cat(ターミナルデバイスのフォアグラウンドプロセスグループではない)と同様に、実行中のプロセスはcat制御端末からデータを読み取ろうとするとすぐに信号によって一時停止されます。SIGTTIN

echo hello | diff -u <(echo ok) <(cat)これは、2つのサブシェルで構成されたパイプ全体が前景にあり、catとにかく端子の代わりにパイプから読み取られるために機能します。プロセスの置き換えは、フォアグラウンドのサブシェル(このパイプラインの2番目のコンポーネント)によって開始されるため、フォアグラウンドで終了します。

bash-5.0$ diff -u <(echo ok) <(ps -o pid,ppid,pgid,args)
--- /dev/fd/63  2021-11-12 22:39:41.985207894 +0000
+++ /dev/fd/62  2021-11-12 22:39:41.985207894 +0000
@@ -1 +1,6 @@
-ok
+    PID    PPID    PGID COMMAND
+   9779    9772    9779 /bin/zsh
+1957388    9779 1957388 bash --norc
+1958861 1957388 1957388 bash --norc
+1958862 1957388 1958862 diff -u /dev/fd/63 /dev/fd/62
+1958863 1958861 1957388 ps -o pid,ppid,pgid,args
--- /dev/fd/63  2021-11-12 22:39:01.017237420 +0000
+++ /dev/fd/62  2021-11-12 22:39:01.017237420 +0000
@@ -1 +1,7 @@
-ok
+    PID    PPID    PGID COMMAND
+   9779    9772    9779 /bin/zsh
+1957388    9779 1957388 bash --norc
+1958325 1957388 1958324 diff -u /dev/fd/63 /dev/fd/62
+1958326 1958325 1958324 [bash] <defunct>
+1958327 1958325 1958324 bash --norc
+1958328 1958327 1958324 ps -o pid,ppid,pgid,args

ps上記の最初のケースが他のプロセスグループでどのように実行され、2番目のケースが残りのプロセスと同じプロセスグループで実行されるかを確認します。

実行する場合:

bash-5.0$ (diff -u <(echo ok) <(ps -o pid,ppid,pgid,args))
--- /dev/fd/63  2021-11-12 22:42:04.761112701 +0000
+++ /dev/fd/62  2021-11-12 22:42:04.761112701 +0000
@@ -1 +1,6 @@
-ok
+    PID    PPID    PGID COMMAND
+1960605    9772 1960605 /bin/zsh
+1960612 1960605 1960612 bash --norc
+1960757 1960612 1960757 diff -u /dev/fd/63 /dev/fd/62
+1960759 1960757 1960757 bash --norc
+1960760 1960759 1960757 ps -o pid,ppid,pgid,args

フォアグラウンドで完全なサブシェルを起動すると、すべてが正常に戻ります。

したがって、ここで最も簡単な解決策は、サブシェルでパイプを実行することです。

とにかく、これは対話型シェル(ターミナルジョブ制御が実行されている)とstdinがセッションの制御端末である場合にのみ機能します。

diff -u <(echo ok) <(cat)通常、移植性のみを考慮するスクリプトでは、シェルが非対話型でジョブ制御を実行しないため、stdinが端末であっても問題になりません。

しかし、これはcat有用ではなく、次のUUOCです。

cat | diff -u <(echo ok) -

または

cat | diff -u <(echo ok) /dev/stdin

bashまたはzshで一時的な名前付きパイプを使用する以外のコマンドに対して/dev/fd/x/をサポートしていないシステムでは、stdin指定はサポートされていません(ただし、この機能が提供されているksh93uより前のバージョンのkshではサポートされていません)。したがって、そのシステムでは引き続き機能します。働く/dev/stdindiff-<(cat)diff -u <(echo ok) <(cat)

移植性について言及したので、次のことをzsh行う必要があります。

echo hello | { diff <(echo ok) <(cat); }

toへの標準入力catもパイプから来ますecho

また注:

echo hello | cmd /dev/stdin # or /dev/fd/0, /proc/self/fd/0²

ksh93はパイプの代わりにソケットペアを使用しているため、Linuxksh93では機能しません3、Linux(およびCygwin)では、他のシステムとは異なり、標準入力で開くのと同じファイルを開くため、接続できません。 。|/dev/stdindup(0)open()

その他の関連Q&Aを読む:


bash¹今日はほとんどありませんが、ないシステムよりも多くのシステムがあります。/dev/fd/x

²実際には、ファイルを開かずに標準入力を意味するgawkものとしてそれ自体で処理または処理できるコマンドなどのコマンドは除外されます。/dev/stdin-

3 Linuxのパイプ実装では、ksh93がいくつかの(あいまいな)最適化を実行するために必要なものが何であるかを判断できないためです。

関連情報