为什么从 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
所以,问题是,这是怎么回事,为什么上面的最后两次运行实际上读取了终端上键入的内容?
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。
我遇到了一个奇怪的情况,当 运行 终端中的程序时,从 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
所以,问题是,这是怎么回事,为什么上面的最后两次运行实际上读取了终端上键入的内容?
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。