为什么有些命令不从标准输入读取?

为什么有些命令不从标准输入读取?

我想知道什么时候我们应该使用管道,什么时候不应该使用管道。

例如,要终止处理 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。那是行不通的。

以下命令的标准输入始终从前一个命令的标准输出获取输入。

  • 为什么它不能与killor一起使用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 程序。

  • 对于其他程序,例如killls,它们的输入有一个意义, A表示kill需要进程号、ls文件名或路径名。它们无法处理任意数据,而且它们也无意这样做。其中许多甚至不需要任何输入或参数,例如ps.它们通常不从 STDIN 读取。

人们可能可以将这两个方面结合起来:过滤器是一种程序,其输入对该程序不具有语义意义。

我确信我在某处读过有关这一哲学的内容,但我目前不记得任何来源,抱歉。如果有人有一些来源,请随时编辑。

答案2

有两种常见的方式为程序提供输入:

  • 向进程的 STDIN 提供数据
  • 指定命令行参数

kill仅使用命令行参数。它不从 STDIN 读取。程序喜欢从 STDINgrep读取awk数据(如果没有给出文件名作为命令行参数),并根据其命令行参数(模式、语句、标志等)处理数据。

您只能通过管道传输到其他进程的 STDIN,而不能传输到命令行参数。

通用规则是,程序使用 STDIN 来处理任意数量的数据。所有额外的输入参数,或者如果通常只有很少的输入参数,都通过命令行参数传递。如果命令行可能变得很长,例如对于长awk程序文本,通常可以从额外的程序文件中读取这些文本(-f的选项awk)。

要将程序的 STDOUT 用作命令行参数,请使用$(...)或 如果有大量数据xargsfind也可以直接用这个-exec ... {} +

为了完整起见:要将命令行参数写入 STDOUT,请使用echo.

答案3

不存在这样的“规则”。有些程序接受来自 STDIN 的输入,有些程序则不接受。如果程序可以从 STDIN 获取输入,则可以通过管道传输到它,如果不能,则不能。

通常,您可以通过考虑程序的作用来判断程序是否接受输入。如果程序的工作是以某种方式操纵内容文件(例如、grep等)的文件,通常从 STDIN 获取输入。如果它的工作是操作文件本身(例如,,)或进程(例如,,)或返回有关某些内容的信息(例如,,, ) ,那么它就不会。sedawkmvrmcpkilllsoftopfindps

另一种思考方式是参数和输入之间的区别。例如:

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 foovia strace,我们可以看到文件foo已打开:

$ strace cat foo 2| grep foo
execve("/bin/cat", ["cat", "foo"], [/* 44 vars */]) = 0
open("foo", O_RDONLY)     

上面的第一行显示该程序/bin/cat被调用,其参数为catfoo(第一个参数始终是程序本身)。后来,该参数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 输入有什么区别?

对于killrm,用户提供他们的自定义信息作为参数,并$(cmd)帮助获取 STDOUTcmd并将其转换为 info 参数。

对于grepawk,用户提供参数,此外,还提供STDIN将由命令处理的常规文件。 STDIN可以通过管道传递|,也可以手动输入。

  • 有什么规则吗?

阅读手册或源代码。如果你没有找到你需要的东西,你可以做一个简单但可能危险的测试:

只需输入您感兴趣的命令,并加上您已经理解的参数,然后查看命令是否暂停(什么都没有发生)。如果它暂停,它实际上是在等待 STDIN(您可以尝试cat查看echo不同之处)。您手动输入Ctrl-D,命令将继续(显示结果或错误)并返回。在这种情况下,这样的命令需要 STDIN(加上您提供的参数)。

同一命令在不同情况下可能不需要 STDIN(例如,cat等待 STDIN 但cat file.txt不等待)。

相关内容