为什么某些程序(如 readlink)不能从管道中获取输入?

Nat*_*ace 7 pipe

我在我$PATH想要编辑的文件中有一个指向脚本的符号链接。我忘记了文件路径,所以我尝试执行以下操作:

$ which my_script_link | readlink
Run Code Online (Sandbox Code Playgroud)

我希望输出文件路径,但它输出

> readlink: missing operand
> Try 'readlink --help' for more information
Run Code Online (Sandbox Code Playgroud)

我之前在其他情况下也看到过类似的行为(比如尝试将文件列表通过管道传输到 vim 中进行编辑)。我知道有一些解决方法,比如 subshel​​l readlink $(which my_script_link),但我想了解为什么管道在这种情况下不能像我认为的那样工作。

谢谢!

ter*_*don 11

简单的说,因为程序不是这样写的。由程序员决定他们的软件是否可以从 STDIN 读取或是否需要输入文件。在 的情况下readlink,其man页面说明(强调我的):

readlink - 打印解析的符号链接或规范文件名

概要
阅读链接 [选项]...文件...

作为一般规则,从 STDIN 获取输入的程序旨在以某种方式解析该输入。当您将数据通过管道传输给readlink它时,它会收到一个文本流,但不知道如何处理它,因为它只处理文件而不是它们的内容。对于像lscdcp等这样的程序也是如此。只有处理文本/数据流的程序才能接受来自管道的输入。


Mat*_*att 7

程序stdin是从命令行参数获取输入还是作为命令行参数获取输入取决于设计者。这两种方法都有其优点。但是,对于严格操作文件的程序,将文件名作为命令行参数传递通常比通过stdin.

最明显的原因是常见的情况,即在文件上运行程序,更容易输入:readlink file而不是echo file | readlink.

一个更微妙的问题是正确性。问题是当文件名通过时stdin,程序需要能够区分一个文件名和另一个文件名。通常,这是通过假设文件名由空格或换行符分隔来完成的,但这是不正确的,因为文件名可以包含空格。更好的方法是用空字节分隔文件名,但生成由空值分隔的文件列表可能不方便。

在命令行上传递文件名可以避免这个问题,因为 shell 处理程序的所有解析和引用。您可以键入touch $'foo\nbar'touch正确地将其视为包含换行符的文件名,而无需处理任何特殊的解析或引用本身。

话虽如此,如果您想stdin为特定程序传递文件,则可以。这xargs就是为了。xargs允许你使用一个只接受命令行参数的程序,而是让它通过stdin.

换句话说,它可以让你做到这一点:which my_script | xargs readlink

如果您想始终以readlink这种方式工作,您可以创建一个别名:alias readlink="xargs readlink". 这将允许您which my_script | readlink按原先想要的方式输入 。