这里的问题是大胆的下面的句子。假设我正在尝试这样做:
diff -u <(some command) {mystery-syntax}
我正在比较标准输入的输出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
实用程序不使用-
指定标准输入的约定,因此我们无法使用它。
一个答案是:/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
它一个here-document作为它的标准输入,就不会产生干扰:$ 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
如果命令行上给出的文件名之一是-
(破折号),则实用程序将从标准输入中读取其位置。
$ 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
或file2
操作数是-
,则应使用标准输入代替它。
答案2
diff -u <(echo foo) <(cat)
在交互中不起作用,bash
因为进程替换中的命令不会放在前台,因此无法从终端设备读取。与在后台运行的cat &
wherecat
一样(不在终端设备的前台进程组中),一旦尝试从其控制终端读取数据,运行的进程cat
就会被一个信号挂起。SIGTTIN
echo hello | diff -u <(echo ok) <(cat)
之所以有效,是因为由两个子壳组成的整个管道位于前台,并且无论如何都是cat
从管道而不是终端读取。进程替换最终位于前台,因为它们是由前台的子 shell(该管道的第二个组件)启动的。
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
上面第一种情况下如何在不同的进程组中运行,以及第二种情况下如何在与其余进程相同的进程组中运行。
如果你运行:
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
当我们在前台启动一个完整的子 shell 时,一切又恢复正常了。
因此,这里最简单的解决方法是在子 shell 中运行管道。
无论如何,这仅适用于交互式 shell(正在执行终端作业控制)以及 stdin 是会话的控制终端时。
在脚本中(您通常只关心脚本中的可移植性),即使 stdin 是终端也没有问题,diff -u <(echo ok) <(cat)
因为 shell 是非交互式的并且不进行作业控制。
然而,这cat
没有任何用处,它与以下中的 UUOC 相同:
cat | diff -u <(echo ok) -
或者
cat | diff -u <(echo ok) /dev/stdin
在不支持/dev/fd/x
//dev/stdin
的系统上,对于与 不同的命令diff
,不支持-
指定 stdin,<(cat)
在 bash 或 zsh 中(但不是在该功能来自的 ksh93u 之前的 ksh 版本中)使用临时命名管道,因此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²
不适用于 Linux 上的 ksh93,因为ksh93
's|
使用套接字对而不是管道³,并且在 Linux(和 Cygwin)上,与其他系统相反,打开/dev/stdin
不像dup(0)
,它打开与在标准输入上打开相同的文件,并且您可以不是open()
插座。
更多阅读相关问答:
bash
¹ 如今已经很少了,而且没有的系统可能比没有的系统多得多/dev/fd/x
² 除了那些命令,例如gawk
那些命令本身视为或可以视为/dev/stdin
含义标准输入,而无需实际打开文件,与-
.
³ 因为 Linux 的管道实现不允许您查看 ksh93 需要执行某些(可疑)优化的内容。