为什么命令“ls | file”不起作用?

为什么命令“ls | file”不起作用?

我一直在研究命令行,并了解到|(管道)用于将一个命令的输出重定向到另一个命令的输入。那么为什么该命令不起作用ls | file

file输入是多个文件名之一,例如file filename1 filename2

ls输出是文件夹中的目录和文件的列表,所以我认为ls | file应该显示文件夹中每个文件的文件类型。

但是当我使用它时,输出是:

    Usage: file [-bcEhikLlNnprsvz0] [--apple] [--mime-encoding] [--mime-type]
        [-e testname] [-F separator] [-f namefile] [-m magicfiles] file ...
    file -C [-m magicfiles]
    file [--help]

file由于命令的使用存在一些错误

答案1

根本问题是file期望文件名作为命令行参数,而不是标准输入。当您写入时,ls | file的输出ls将作为输入传递给file。不是作为参数,而是作为输入。

有什么不同?

  • 命令行参数是指在命令后写入标志和文件名,如cmd arg1 arg2 arg3。在 shell 脚本中,这些参数可用作变量$1$2$3等。在 C 中,您可以通过char **argv和 的int argc参数访问它们main()

  • 标准输入 stdin 是数据流。有些程序在未指定任何命令行参数时喜欢cat或从 stdin 读取数据。在 shell 脚本中,您可以使用来获取单行输入。在 C 中,您可以使用或等各种选项。wcreadscanf()getchar()

file通常不从 stdin 读取。它期望至少一个文件名作为参数传递。这就是为什么当你写 时它会打印出使用情况ls | file,因为你没有传递参数。

您可以使用xargs将 stdin 转换为参数,如ls | xargs file。不过,terdon 提到,解析ls不是一个好主意。最直接的方法就是:

file *

答案2

因为,正如你所说,输入file必须是文件名。但是, 的输出ls只是文本。它恰好是文件名列表,但这并不能改变它只是文本而不是硬盘上文件位置的事实。

当你看到屏幕上打印的输出时,你看到的是文本。无论该文本是一首诗还是文件名列表,对计算机来说都没有区别。它只知道它是文本。这就是为什么你可以将的输出传递给以ls文本为输入的程序(尽管你真的,真的不应该):

$ ls / | grep etc
etc

因此,要使用将文件名列为文本的命令的输出(例如lsfind)作为接受文件名的命令的输入,您需要使用一些技巧。典型的工具是xargs

$ ls
file1 file2

$ ls | xargs wc
 9  9 38 file1
 5  5 20 file2
14 14 58 total

不过,正如我之前所说,您真的不想解析 的输出ls。类似的东西find更好(在每个文件名后print0打印 a\0而不是 newilne 并且-0让它xargs处理此类输入;这是一个让您的命令处理包含换行符的文件名的技巧):

$ find . -type f -print0 | xargs -0 wc
 9  9 38 ./file1
 5  5 20 ./file2
14 14 58 total

它也有自己的方法来实现这一点,xargs根本不需要:

$ find . -type f -exec wc {} +
 9  9 38 ./file1
 5  5 20 ./file2
14 14 58 total

最后,您还可以使用 shell 循环。但是,请注意,在大多数情况下,xargs这会更快、更高效。例如:

$ for file in *; do wc "$file"; done
 9  9 38 file1
 5  5 20 file2

答案3

了解到“|”(管道)是为了重定向输出从一个命令到另一个命令的输入。

它不会“重定向”输出,而是获取程序的输出并将其用作输入,而文件不接受输入,但文件名作为参数,然后进行测试。重定向不会将这些文件名作为参数传递,也不会管道做,你做的事情就越晚。

您可以做的是,--files-from如果您有一个列出所有要测试的文件的文件,则可以使用该选项从文件中读取文件名,否则只需将文件路径作为参数传递即可。

答案4

通过管道的输出ls是一个实心数据块,其中每行用 0x0a 分隔 - 即换行符 - 并将file其作为一个参数,其中它期望多个字符一次处理一个。

一般来说,永远不要使用它ls来为其他命令生成数据源 - 有一天它会将..输入管道rm,然后你就有麻烦了!

最好使用循环,这样for i in *; do file "$i" ; done可以预测您想要的输出。如果文件名中有空格,则使用引号。

相关内容