ここで問題は勇敢な次の文。私がこれをやろうとしているとしましょう:
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規格では:
file1
、file2
比較するファイルのパス名。file1
ORfile2
オペランドがある場合は、標準-
入力を代わりに使用する必要があります。
答え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/stdin
diff
-
<(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/stdin
dup(0)
open()
その他の関連Q&Aを読む:
bash
¹今日はほとんどありませんが、ないシステムよりも多くのシステムがあります。/dev/fd/x
²実際には、ファイルを開かずに標準入力を意味するgawk
ものとしてそれ自体で処理または処理できるコマンドなどのコマンドは除外されます。/dev/stdin
-
3 Linuxのパイプ実装では、ksh93がいくつかの(あいまいな)最適化を実行するために必要なものが何であるかを判断できないためです。