Less 从 stderr 获取键盘输入?

发布于 2024-08-05 15:49:31 字数 778 浏览 12 评论 0原文

我正在查看“less”实用程序的代码,特别是它如何获取键盘输入。有趣的是,在 ttyin.c 的第 80 行,它设置了要读取的文件描述符:

     /*
      * Try /dev/tty.
      * If that doesn't work, use file descriptor 2,
      * which in Unix is usually attached to the screen,
      * but also usually lets you read from the keyboard.
      */
  #if OS2
      /* The __open() system call translates "/dev/tty" to "con". */
      tty = __open("/dev/tty", OPEN_READ);
  #else
      tty = open("/dev/tty", OPEN_READ);
  #endif
      if (tty < 0)
          tty = 2;

难道文件描述符 2 不是 stderr?如果是这样,那是什么呢?我认为键盘输入是通过标准输入发送的。

有趣的是,即使您执行 ls -l * | less,文件加载完成后,你仍然可以使用键盘上下滚动,但如果你执行ls -l * | vi,那么 vi 就会对你大喊大叫,因为它不从 stdin 读取数据。有什么大创意?我怎么会来到这个奇怪的新领域,其中 stderr 既是向屏幕报告错误又是从键盘读取错误的方式?我想我已经不在堪萨斯了...

I'm taking a look at the code to the 'less' utility, specifically how it gets keyboard input. Interestingly, on line 80 of ttyin.c, it sets the file descriptor to read from:

     /*
      * Try /dev/tty.
      * If that doesn't work, use file descriptor 2,
      * which in Unix is usually attached to the screen,
      * but also usually lets you read from the keyboard.
      */
  #if OS2
      /* The __open() system call translates "/dev/tty" to "con". */
      tty = __open("/dev/tty", OPEN_READ);
  #else
      tty = open("/dev/tty", OPEN_READ);
  #endif
      if (tty < 0)
          tty = 2;

Isn't file descriptor 2 stderr? If so, WTH?! I thought keyboard input was sent through stdin.

Interestingly, even if you do ls -l * | less, after the file finishes loading, you can still use the keyboard to scroll up and down, but if you do ls -l * | vi, then vi will yell at you because it doesn't read from stdin. What's the big idea? How did I end up in this strange new land where stderr is both a way to report errors to the screen and read from the keyboard? I don't think I'm in Kansas anymore...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

暖树树初阳… 2024-08-12 15:49:31
$ ls -l /dev/fd/
lrwx------ 1 me me 64 2009-09-17 16:52 0 -> /dev/pts/4
lrwx------ 1 me me 64 2009-09-17 16:52 1 -> /dev/pts/4
lrwx------ 1 me me 64 2009-09-17 16:52 2 -> /dev/pts/4

当在交互式终端上登录时,所有三个标准文件描述符都指向同一件事:您的 TTY(或伪 TTY)。

$ ls -fl /dev/std{in,out,err}
lrwxrwxrwx 1 root root 4 2009-09-13 01:57 /dev/stdin -> fd/0
lrwxrwxrwx 1 root root 4 2009-09-13 01:57 /dev/stdout -> fd/1
lrwxrwxrwx 1 root root 4 2009-09-13 01:57 /dev/stderr -> fd/2

按照惯例,我们从 0 读取并写入 12。然而,没有什么可以阻止我们采取其他行动。

当您的 shell 运行 ls -l * | 时less,它创建一个从 ls 的文件描述符 1less 的文件描述符 0 的管道代码>.显然,less 无法再从文件描述符 0 读取用户的键盘输入 - 它会尽其所能尝试恢复 TTY。

如果 less 尚未与终端分离,open("/dev/tty") 将为其提供 TTY。

但是,万一失败了……你能做什么? less 最后一次尝试获取 TTY,假设文件描述符 2 附加到与文件描述符 0 附加的同一事物上,如果没有重定向。

不是防故障:

$ ls -l * | setsid less 2>/dev/null

这里,less被赋予了自己的会话(因此它不再是终端活动进程组的一部分,导致open("/ dev/tty") 失败),并且其文件描述符 2 已更改 - 现在 less 立即退出,因为它正在输出到 TTY 但它无法获取任何用户输入。

$ ls -l /dev/fd/
lrwx------ 1 me me 64 2009-09-17 16:52 0 -> /dev/pts/4
lrwx------ 1 me me 64 2009-09-17 16:52 1 -> /dev/pts/4
lrwx------ 1 me me 64 2009-09-17 16:52 2 -> /dev/pts/4

When logged in at an interative terminal, all three standard file descriptors point to the same thing: your TTY (or pseudo-TTY).

$ ls -fl /dev/std{in,out,err}
lrwxrwxrwx 1 root root 4 2009-09-13 01:57 /dev/stdin -> fd/0
lrwxrwxrwx 1 root root 4 2009-09-13 01:57 /dev/stdout -> fd/1
lrwxrwxrwx 1 root root 4 2009-09-13 01:57 /dev/stderr -> fd/2

By convention, we read from 0 and write to 1 and 2. However, nothing prevents us from doing otherwise.

When your shell runs ls -l * | less, it creates a pipe from ls's file descriptor 1 to less's file descriptor 0. Obviously, less can no longer read the user's keyboard input from file descriptor 0 – it tries to get the TTY back however it can.

If less has not been detached from the terminal, open("/dev/tty") will give it the TTY.

However, in case that fails... what can you do? less makes one last attempt at getting the TTY, assuming that file descriptor 2 is attached to the same thing that file descriptor 0 would be attached to, if it weren't redirected.

This is not failproof:

$ ls -l * | setsid less 2>/dev/null

Here, less is given its own session (so it is no longer a part of the terminal's active process group, causing open("/dev/tty") to fail), and its file descriptor 2 has been changed – now less exits immediately, because it is outputting to a TTY yet it fails to get any user input.

指尖上得阳光 2024-08-12 15:49:31

嗯...首先,您似乎错过了打开“/dev/tty”的 open() 调用。如果调用 open() 失败,它仅使用文件描述符 2。在标准 Linux 系统(可能还有许多 Unices)上,“/dev/tty”存在并且不太可能导致失败。

其次,顶部的注释对为什么它们回退到文件描述符 2 提供了有限的解释。我的猜测是 stdinstdout无论如何,stderr 几乎都连接到“/dev/tty/”,除非重定向。并且由于最常见的是 stdin 和/或 stdout 重定向(通过管道或 < / >),但较少用于 stderr ,很有可能使用 stderr 最有可能仍然连接到“键盘”。

Well... first off, you seem to missing the open() call which opens '/dev/tty'. It only uses file descriptor 2 if the call to open() fails. On a standard Linux system, and probably many Unices, '/dev/tty' exists and is unlikely to cause a fail.

Secondly, the comment at the top provides a limited amount of explanation as to why they fall back to file descriptor 2. My guess is that stdin, stdout, and stderr are pretty much connected to '/dev/tty/' anyway, unless redirected. And since the most common redirections for for stdin and/ or stdout (via piping or < / >), but less often for stderr, odds on are that using stderr would be most likely to still be connect to the "keyboard".

暖风昔人 2024-08-12 15:49:31

同样的问题最终来自提问者的答案,位于 linuxquestions 尽管它们引用的来源与 less 略有不同。不,我不明白其中的大部分内容,所以我无能为力:)

The same question with an answer ultimately from the person who asked it is on linuxquestions although they quote slightly different source from less. And no, I don't understand most of it so I can't help beyond that :)

柠檬色的秋千 2024-08-12 15:49:31

这似乎是 Linux 特定功能,可将键盘输入发送到 FD 2。

It appears to be Linux specific functionality that sends keyboard input to FD 2.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文