在创建 bash 脚本时,我发现此代码ls
将所有文件放在一行上:
pi@raspberrypi:~/ptlrestinterface$ ls
update.sh web.config MyApp.runtimeconfig.json
仍然ls | head -n1
只打印第一个文件:
pi@raspberrypi:~/ptlrestinterface$ ls | head -n1
update.sh
我希望它输出整行,而不是第一个文件。
当通过管道传输输出时hexdump
,我看到ls
总是在每个条目后输出一个0a
,但是在控制台上,它将它们放在彼此旁边。
显然ls
对控制台(或控制台ls
)有一些了解。它是如何工作的?我在哪里可以找到有关此行为的文档?
答案1
它调用文件描述符isatty()
上的 libc 函数stdout
来确定输出是否到达终端。
在内部,尝试使用特定于 tty 设备的调用isatty()
之一- 最有可能(检索命令显示的通用 tty 设置);如果成功,则该文件就是一个 tty 设备。ioctl()
ioctl(TCGETS)
stty
$ python
>>> sys.stdout.isatty()
True
这会影响 ls 是否默认将其输出列化,是否默认使用颜色,以及一些其他事项,例如是否将文件名加引号。
如果stdout
确实是终端,则ls
使用另一个 tty 特定的方法ioctl(TIOCGWINSZ)
来检索其尺寸(在现代系统中,这些尺寸已由终端仿真器存储在那里)以找出它可以容纳多少列。该stty size
命令也会获取这些;尝试在 下运行它strace
。
全屏程序还会对SIGWINCH
内核每次终端仿真器更新 tty 尺寸时发送的信号做出反应,表明它们需要以新的尺寸重新绘制所有内容。
答案2
为了获得不太技术性的解释,请观察尝试这两个命令时发生的情况:
$ ls
update.sh web.config MyApp.runtimeconfig.json
$ ls | cat
update.sh
web.config
MyApp.runtimeconfig.json
通过管道传输cat
“不做任何事情”,只是ls
“注意到”它的输出正在被管道传输到某处。因此它会相应地改变其行为。
一旦你理解了ls
输出的内容,你就可以直接知道当它经过时会发生什么head -n1
。