即使使用类 Unix 操作系统几年后,这种行为仍然让我感到困惑。
当我在包含大量文件的目录中使用 ls 命令时,输出通常会被很好地格式化为多列。以下是示例:
$ ls
a.txt C.txt f.txt H.txt k.txt M.txt p.txt R.txt u.txt W.txt z.txt
A.txt d.txt F.txt i.txt K.txt n.txt P.txt s.txt U.txt x.txt Z.txt
b.txt D.txt g.txt I.txt l.txt N.txt q.txt S.txt v.txt X.txt
B.txt e.txt G.txt j.txt L.txt o.txt Q.txt t.txt V.txt y.txt
c.txt E.txt h.txt J.txt m.txt O.txt r.txt T.txt w.txt Y.txt
但是,如果我尝试将输出重定向到文件或将其通过管道传输到另一个命令,则输出中只会显示一列。使用与上面相同的示例目录,当我将 ls 通过管道传输到 wc 时,我得到的结果如下:
$ ls | wc
52 52 312
换句话说,wc 认为有 52 行,尽管输出到终端的只有 5 行。
我还没有观察到这种行为任何其他命令。你愿意给我解释一下吗?
答案1
它只是检测其输出是否是终端设备,并格式化输出以使其看起来更好。您可以使用选项 -C 将相同格式的 ls 输出打印到文件或管道中。
如何检测输出是否为终端:https://stackoverflow.com/questions/1061575/detect-in-c-if-outputting-to-a-terminal
答案2
当程序的输出提供了某种列表,并且您将该程序的输出重定向到文件,或通过管道传输到另一个程序时,通常第二个程序的目的是循环读取原始程序输出(或文件),一次读取一行。
在这种情况下,读取的每一行包含一个项目(1 个文件名)或 1 个有关单个项目的信息“记录”,而不是必须分开的多个不同项目,这几乎总是更方便。
对于可以包含空格的文件名尤其如此,因为空格也是分隔每个项目(文件名)的分隔符。
例如 ...
首先,我创建一些文件:
% touch "300"
% cp "300" "301"
% cp "300" "302 304"
% cp "300" "303 305"
% cp "300" "306"
% cp "300" "307"
% cp "300" "308"
% cp "300" "309"
% cp "300" "310 312"
% cp "300" "311 313"
% cp "300" "314 316"
% cp "300" "315 317"
% cp "300" "318"
% cp "300" "319"
% cp "300" "320 322"
% cp "300" "321 323"
% cp "300" "324"
% cp "300" "325"
% cp "300" "bar bin"
% cp "300" "baz boo"
% cp "300" "def"
% cp "300" "etc"
% cp "300" "foo mos"
%
现在,我得到了我创建的文件的“长”列表:
% ls -l
-rw-r--r-- 1 username username 0 Nov 28 01:51 300
-rw-r--r-- 1 username username 0 Nov 28 01:51 301
-rw-r--r-- 1 username username 0 Nov 28 01:51 302 304
-rw-r--r-- 1 username username 0 Nov 28 01:51 303 305
-rw-r--r-- 1 username username 0 Nov 28 01:51 306
-rw-r--r-- 1 username username 0 Nov 28 01:51 307
-rw-r--r-- 1 username username 0 Nov 28 01:51 308
-rw-r--r-- 1 username username 0 Nov 28 01:51 309
-rw-r--r-- 1 username username 0 Nov 28 01:51 310 312
-rw-r--r-- 1 username username 0 Nov 28 01:51 311 313
-rw-r--r-- 1 username username 0 Nov 28 01:51 314 316
-rw-r--r-- 1 username username 0 Nov 28 01:51 315 317
-rw-r--r-- 1 username username 0 Nov 28 01:51 318
-rw-r--r-- 1 username username 0 Nov 28 01:51 319
-rw-r--r-- 1 username username 0 Nov 28 01:51 320 322
-rw-r--r-- 1 username username 0 Nov 28 01:51 321 323
-rw-r--r-- 1 username username 0 Nov 28 01:51 324
-rw-r--r-- 1 username username 0 Nov 28 01:51 325
-rw-r--r-- 1 username username 0 Nov 28 01:51 bar bin
-rw-r--r-- 1 username username 0 Nov 28 01:51 baz boo
-rw-r--r-- 1 username username 0 Nov 28 01:51 def
-rw-r--r-- 1 username username 0 Nov 28 01:51 etc
-rw-r--r-- 1 username username 0 Nov 28 01:51 foo mos
%
好的,文件名是什么就很明显了。
现在,我得到了按列列出的文件列表:
% ls
300 302 304 306 308 310 312 314 316 318 320 322 324 bar bin def foo mos
301 303 305 307 309 311 313 315 317 319 321 323 325 baz boo etc
%
如您所见,读取输出的程序(甚至是人类观察者)无法挑选出文件的正确名称,并且可能会认为这是一个包含 3 个字符的文件名的长列表。
如果我们将 ls 的输出导入到 more 中,我们会得到:
% ls | more
300
301
302 304
303 305
306
307
308
309
310 312
311 313
314 316
315 317
318
319
320 322
321 323
324
325
bar bin
baz boo
def
etc
foo mos
%
虽然不是很漂亮,但现在选择正确的文件名变得容易得多,因为每个文件名都在自己的行上。
答案3
实际上发生的事情是终端格式化了 给出的输出ls
。 ls 命令的实际输出如下:
hussain@ai:~/test$ ls # The terminal formats this output by itself
text1 text11 text13 text15 text17 text19 text20 text22 text24 text26 text28 text3 text4 text6 text8
text10 text12 text14 text16 text18 text2 text21 text23 text25 text27 text29 text30 text5 text7 text9
现在,如果我将其通过管道传递给less
命令,则会显示如下内容:
hussain@ai:~/test$ ls | less # This means that the actual output is the below
text1
text10
text11
text12
text13
text14
text15
text16
text17
text18
...
...
...
因此,这意味着如果你将相同的命令提供给wc
任何其他程序,上面给出的输出将是它将工作的输出(即不是花哨的终端输出)。但是,如果你想要将那个花哨的输出传递给,你可以使用-C
带有ls
命令的标志,例如@piokuc 的答案中给出的
除此之外,如果您想查看发送到下一个命令的输出(不使用管道“ |
”符号,您可以使用-w
或--width
标志。
这就是它们之间的区别。
ai:test hussain$ ls # The terminal formats this output by itself as it can be seen in Kevin Fegan's Answer
text1 text11 text13 text15 text17 text19 text20 text22 text24 text26 text28 text3 text4 text6 text8
text10 text12 text14 text16 text18 text2 text21 text23 text25 text27 text29 text30 text5 text7 text9
ai:test hussain$ ls -w0 # width 0 is the default behavior. it comes preformatted to the terminal
text1 text10 text11 text12 text13 text14 text15 text16 text17 text18 text19 text2 text20 text21 text22 text23 text24 text25 text26 text27 text28 text29 text3 text30 text4 text5 text6 text7 text8 text9
不要-w0
在脚本中使用对文件执行操作。而是使用*
( for file_and_dir in *; do ...
)
ai:test hussain$ ls -w1 # The output comes preformatted but in a new line. It is just like you force to see it in this way.
text1
text10
text11
text12
text13
text14
...
...
...