我一直在研究命令行,并了解到|
(管道)用于将一个命令的输出重定向到另一个命令的输入。那么为什么该命令不起作用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 中,您可以使用或等各种选项。wc
read
scanf()
getchar()
file
通常不从 stdin 读取。它期望至少一个文件名作为参数传递。这就是为什么当你写 时它会打印出使用情况ls | file
,因为你没有传递参数。
您可以使用xargs
将 stdin 转换为参数,如ls | xargs file
。不过,terdon 提到,解析ls
不是一个好主意。最直接的方法就是:
file *
答案2
因为,正如你所说,输入file
必须是文件名。但是, 的输出ls
只是文本。它恰好是文件名列表,但这并不能改变它只是文本而不是硬盘上文件位置的事实。
当你看到屏幕上打印的输出时,你看到的是文本。无论该文本是一首诗还是文件名列表,对计算机来说都没有区别。它只知道它是文本。这就是为什么你可以将的输出传递给以ls
文本为输入的程序(尽管你真的,真的不应该):
$ ls / | grep etc
etc
因此,要使用将文件名列为文本的命令的输出(例如ls
或find
)作为接受文件名的命令的输入,您需要使用一些技巧。典型的工具是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
答案4
通过管道的输出ls
是一个实心数据块,其中每行用 0x0a 分隔 - 即换行符 - 并将file
其作为一个参数,其中它期望多个字符一次处理一个。
一般来说,永远不要使用它ls
来为其他命令生成数据源 - 有一天它会将..输入管道rm
,然后你就有麻烦了!
最好使用循环,这样for i in *; do file "$i" ; done
可以预测您想要的输出。如果文件名中有空格,则使用引号。