![为什么有些命令不从标准输入读取?](https://linux22.com/image/39190/%E4%B8%BA%E4%BB%80%E4%B9%88%E6%9C%89%E4%BA%9B%E5%91%BD%E4%BB%A4%E4%B8%8D%E4%BB%8E%E6%A0%87%E5%87%86%E8%BE%93%E5%85%A5%E8%AF%BB%E5%8F%96%EF%BC%9F.png)
我想知道什么时候我们应该使用管道,什么时候不应该使用管道。
例如,要终止处理 pdf 文件的某些进程,使用管道将无法执行以下操作:
ps aux | grep pdf | awk '{print $2}'|kill
相反,我们只能通过以下方式来实现:
kill $(ps aux| grep pdf| awk '{print $2}')
或者
ps aux | grep pdf | awk '{print $2}'| xargs kill
根据man bash
(版本4.1.2
):
The standard output of command is connected via a pipe to the standard input of command2.
对于上述场景:
- 的标准输入
grep
是 的标准输出ps
。这样可行。 - 的标准输入
awk
是 的标准输出grep
。这样可行。 - 的标准输入
kill
是 的标准输出awk
。那是行不通的。
以下命令的标准输入始终从前一个命令的标准输出获取输入。
- 为什么它不能与
kill
or一起使用rm
? kill
,rm
输入 和grep
,输入之间有什么不同awk
?- 有什么规则吗?
答案1
这是一个有趣的问题,它涉及 Unix/Linux 哲学的一部分。
grep
那么,像, sed
,sort
一方面和kill
, rm
,ls
另一方面这样的程序有什么区别呢?我看到两个方面。
这筛选方面
第一类程序也称为过滤器。它们从文件或 STDIN 获取输入,修改它,并生成一些输出,主要是 STDOUT。它们旨在与其他程序作为源和目标的管道中使用。
第二种程序对输入起作用,但它们给出的输出通常与输入无关。
kill
正常工作时没有输出,也没有rm
。只是有一个返回值来表明成功。它们通常不从 STDIN 获取输入,但大多向 STDOUT 提供输出。
对于像 之类的程序ls
,过滤器方面的效果不太好。它当然可以有一个输入(但不需要),并且输出与该输入密切相关,但它不能充当过滤器。然而,对于此类程序,另一方面仍然有效:
这语义方面
对于过滤器,它们的输入有没有语义意义。他们只是读取数据、修改数据、输出数据。这是数值列表、某些文件名还是 HTML 源代码并不重要。该数据的含义仅由代码给出你向过滤器提供: 的正则表达式
grep
、 的规则awk
或 Perl 程序。对于其他程序,例如
kill
或ls
,它们的输入有一个意义, A表示。kill
需要进程号、ls
文件名或路径名。它们无法处理任意数据,而且它们也无意这样做。其中许多甚至不需要任何输入或参数,例如ps
.它们通常不从 STDIN 读取。
人们可能可以将这两个方面结合起来:过滤器是一种程序,其输入对该程序不具有语义意义。
我确信我在某处读过有关这一哲学的内容,但我目前不记得任何来源,抱歉。如果有人有一些来源,请随时编辑。
答案2
有两种常见的方式为程序提供输入:
- 向进程的 STDIN 提供数据
- 指定命令行参数
kill
仅使用命令行参数。它不从 STDIN 读取。程序喜欢从 STDINgrep
读取awk
数据(如果没有给出文件名作为命令行参数),并根据其命令行参数(模式、语句、标志等)处理数据。
您只能通过管道传输到其他进程的 STDIN,而不能传输到命令行参数。
通用规则是,程序使用 STDIN 来处理任意数量的数据。所有额外的输入参数,或者如果通常只有很少的输入参数,都通过命令行参数传递。如果命令行可能变得很长,例如对于长awk
程序文本,通常可以从额外的程序文件中读取这些文本(-f
的选项awk
)。
要将程序的 STDOUT 用作命令行参数,请使用$(...)
或 如果有大量数据xargs
。find
也可以直接用这个-exec ... {} +
。
为了完整起见:要将命令行参数写入 STDOUT,请使用echo
.
答案3
不存在这样的“规则”。有些程序接受来自 STDIN 的输入,有些程序则不接受。如果程序可以从 STDIN 获取输入,则可以通过管道传输到它,如果不能,则不能。
通常,您可以通过考虑程序的作用来判断程序是否接受输入。如果程序的工作是以某种方式操纵内容文件(例如、grep
等)的文件,通常从 STDIN 获取输入。如果它的工作是操作文件本身(例如,,)或进程(例如,,)或返回有关某些内容的信息(例如,,, ) ,那么它就不会。sed
awk
mv
rm
cp
kill
lsof
top
find
ps
另一种思考方式是参数和输入之间的区别。例如:
mv foo bar
在上面的命令中,mv
没有这样的输入。它给出的是两个论点。它不知道也不关心这两个文件中的内容,它只知道这些是它的参数,并且应该操纵它们。
另一方面
sed -e 's/foo/bar/' < file
--- -- ------------ ----
| | | |-> input
| | |------------> argument
| |--------------------> option/flag/switch
|------------------------> command
这里,sed
已给出输入和参数。由于它需要输入,因此它可以从 STDIN 读取它,并且可以通过管道传输到它。
当争论可以时,事情会变得更加复杂是输入。例如
cat file
这里,file
是给出的论点cat
。准确地说,该文件姓名 file
是论据。然而,由于cat
是一个操作文件内容的程序,因此它的输入是内部的任何内容file
。
这可以使用strace
跟踪进程进行的系统调用的程序来说明。如果我们运行cat foo
via strace
,我们可以看到文件foo
已打开:
$ strace cat foo 2| grep foo
execve("/bin/cat", ["cat", "foo"], [/* 44 vars */]) = 0
open("foo", O_RDONLY)
上面的第一行显示该程序/bin/cat
被调用,其参数为cat
和foo
(第一个参数始终是程序本身)。后来,该参数foo
以只读模式打开。现在,将其与
$ strace ls foo 2| grep foo
execve("/bin/ls", ["ls", "foo"], [/* 44 vars */]) = 0
stat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
lstat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
write(1, "foo\n", 4foo
这里也ls
以自身和foo
作为论据。但是,没有open
调用,参数不被视为输入。相反,ls
调用系统的stat
库(与命令不同stat
)来获取有关文件的信息foo
。
总之,如果您正在运行的命令将读取其输入,您可以通过管道传输到它,如果不能,则不能。
答案4
- 为什么它不能与kill或rm一起使用?
kill
并且rm
不需要 STDIN。
- Kill、rm 输入与 grep、awk 输入有什么区别?
对于kill
和rm
,用户提供他们的自定义信息作为参数,并$(cmd)
帮助获取 STDOUTcmd
并将其转换为 info 参数。
对于grep
和awk
,用户提供参数,此外,还提供STDIN
将由命令处理的常规文件。 STDIN
可以通过管道传递|
,也可以手动输入。
- 有什么规则吗?
阅读手册或源代码。如果你没有找到你需要的东西,你可以做一个简单但可能危险的测试:
只需输入您感兴趣的命令,并加上您已经理解的参数,然后查看命令是否暂停(什么都没有发生)。如果它暂停,它实际上是在等待 STDIN(您可以尝试cat
查看echo
不同之处)。您手动输入Ctrl-D
,命令将继续(显示结果或错误)并返回。在这种情况下,这样的命令需要 STDIN(加上您提供的参数)。
同一命令在不同情况下可能不需要 STDIN(例如,cat
等待 STDIN 但cat file.txt
不等待)。