为什么从 STDOUT 读取有效?

Why does reading from STDOUT work?

我遇到了一个奇怪的情况,当 运行 终端中的程序时,从 STDOUT 读取有效。问题是,为什么以及如何?让我们从代码开始:

#include <QCoreApplication>
#include <QSocketNotifier>
#include <QDebug>
#include <QByteArray>

#include <unistd.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    const int fd_arg = (a.arguments().length()>=2) ? a.arguments().at(1).toInt() : 0;
    qDebug() << "reading from fd" << fd_arg;

    QSocketNotifier n(fd_arg, QSocketNotifier::Read);

    QObject::connect(&n, &QSocketNotifier::activated, [](int fd) {
        char buf[1024];
        auto len = ::read(fd, buf, sizeof buf);
        if (len < 0) { qDebug() << "fd" << fd << "read error:" << errno; qApp->quit(); }
        else if (len == 0)  { qDebug() << "fd" << fd << "done"; qApp->quit(); }
        else {
            QByteArray data(buf, len);
            qDebug() << "fd" << fd << "data" << data.length() << data.trimmed();
        }
    });

    return a.exec();
}

这里是 qmake .pro 文件为了方便,如果有人想测试上面的代码:

QT       += core
QT       -= gui
CONFIG += c++11
TARGET = stdoutreadtest
CONFIG   += console
CONFIG   -= app_bundle
TEMPLATE = app
SOURCES += main.cpp

这是 4 次执行的输出:

$ ./stdoutreadtest 0 # input from keyboard, ^D ends, works as expected
reading from fd 0
typtyptyp
fd 0 data 10 "typtyptyp"
fd 0 done

$ echo pipe | ./stdoutreadtest 0 # input from pipe, works as expected
reading from fd 0
fd 0 data 5 "pipe"
fd 0 done

$ ./stdoutreadtest 1 # input from keyboard, ^D ends, works!?
reading from fd 1
typtyp
fd 1 data 7 "typtyp"
fd 1 done

$ echo pipe | ./stdoutreadtest 1 # input from pipe, still reads keyboard!?
reading from fd 1
typtyp
fd 1 data 7 "typtyp"
fd 1 done

所以,问题是,这是怎么回事,为什么上面的最后两次运行实际上读取了终端上键入的内容?

我也尝试查看 QSocketNotifier 来源 here leading to here,但没有真正获得任何见解。

fd 0,1,2 没有区别,如果不重定向,这三个 fd 都指向终端,它们完全相同!

程序通常使用 0 表示输入,1 表示输出,2 表示错误,但它们都可以是不同的方式。

例如,对于less,普通用法:

prog | less

现在 less 的输入被重定向到 prog,并且 less 无法从 stdin 读取任何用户输入,所以 less获取用户输入,例如滚动 up/down 或页面 up/down ?

当然 less 可以从 stdout 读取用户输入,这正是 less 所做的。

因此,当您知道如何bash处理这些 fds 时,您可以明智地使用这些 fds。