我已阅读this stackoverflow question,我在重定向stdout方面取得了成功。 但是所提到的代码的工作方式,我完全松开了我的终端,这意味着我无法在其上打印任何内容。
我想要实现的是使用ncurses格式化我的程序的输出(将使用我的一些代码和一些我无法修改的库),并且有一个窗口,其中任何虚假的printf将显示。
有没有办法实现这个目标?
saved_stdout = dup(STDOUT_FILENO); /* save stdout for display later $
if( pipe(out_pipe) != 0 ) { /* make a pipe */
exit(1);
}
dup2(out_pipe[1], STDOUT_FILENO); /* redirect stdout to the pipe */
close(out_pipe[1]);
initscr(); /* Start curses mode */
这样我就无法在屏幕上打印任何内容。
答案 0 :(得分:0)
为了实现我想要的我必须解决一个问题:默认情况下所有输出函数都使用STDOUT,对于大多数输出函数,我无法改变这种行为(想想一个lib中的embbeded printf我没有或者为自己辩护的原因。
因此,如果你修改STDOUT,一切都会受到影响。
但我希望我的程序能够保持对STDOUT的控制。 所以你不得不分开得到真正的STDOUT和什么得到假 STDOUT。 分开的声音很像叉子...... 所以分叉吧。
基本上,您将子程序中的所有程序的大部分分叉,并为其修改STDOUT管道。 父进程保持对STDOUT的控制,并且能够读取管道并对其中的数据执行任何操作。
#include <ncurses.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <iostream>
#include <sstream>
#define MAX_LEN 99
WINDOW *create_newwin(int height, int width, int starty, int startx, int vert, int horz);
char buffer[MAX_LEN+1] = {0};
int out_pipe[2];
int saved_stdout;
volatile sig_atomic_t s_interrupted = 0;
volatile sig_atomic_t alarm_expired = 0;
static void s_signal_handler (int signal_value)
{
if (signal_value == SIGTERM || signal_value == SIGSTOP || signal_value == SIGINT)
s_interrupted = 1;
if (signal_value == SIGALRM)
//signal(SIGALRM,alarm_wakeup);
alarm_expired = 1;
}
static void s_catch_signals (void)
{
struct sigaction action;
action.sa_handler = s_signal_handler;
action.sa_flags = 0;
sigemptyset (&action.sa_mask);
sigaction (SIGINT, &action, NULL);
sigaction (SIGTERM, &action, NULL);
sigaction (SIGALRM, &action, NULL);
}
int main (int arg, char **argv) {
const char mesg[]="Testing stdout redirection";
WINDOW *my_win, *my_win2;
int cycle = 0, pid, p[2], row, col;
if(pipe(p) == -1)
{
perror("pipe call error");
return(1);
}
switch (pid=fork())
{
case -1: perror("error: fork call");
return(2);
case 0: /* if child then write down pipe */
if(dup2(p[1], 1) == -1 ) /* stdout == write end of the pipe */
{
perror( "dup2 failed" );
return(1);
}
s_catch_signals ();
setvbuf(stdout, NULL, _IOLBF, 1000);
while(!s_interrupted)
{
printf("Test %d\n",cycle);
cycle++;
usleep(10000);
}
printf("\n\tChild quitting cleanly\n\n");
break;
default:
s_catch_signals ();
std::string mystr;
if(dup2(p[0], 0 ) == -1 ) /* stdin == read end of the pipe */
{
perror( "dup2 failed" );
return(1);
}
initscr(); /* start the curses mode */
getmaxyx(stdscr,row,col); /* get the number of rows and columns */
mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg);
/* print the message at the center of the screen */
refresh();
my_win2 = create_newwin(10,col,row-10, 0, 0, 0);
wrefresh(my_win2);
my_win = create_newwin(8,col-2,row-9, 1, ' ', ' ');
wrefresh(my_win);
wprintw(my_win,"This screen has %d rows and %d columns\n",row,col);
wprintw(my_win,"Try resizing your window(if possible) and then run this program again\n");
scrollok(my_win,TRUE);
int myy,myx;
std::stringstream ss;
while(!s_interrupted)
{
while( std::getline(std::cin, mystr) )
{
ss.clear();
ss << mystr << std::endl;
mystr = ss.str();
waddstr(my_win,mystr.c_str());
wrefresh(my_win);
}
usleep(100000);
}
endwin();
break;
}
endwin();
printf("\n\tProgram quitting cleanly\n\n");
return (EXIT_SUCCESS);
}
WINDOW *create_newwin(int height, int width, int starty, int startx, int vert, int horz)
{ WINDOW *local_win;
local_win = newwin(height, width, starty, startx);
box(local_win, vert , horz); /* 0, 0 gives default characters
* for the vertical and horizontal
* lines */
wrefresh(local_win); /* Show that box */
return local_win;
}
此程序正好显示了这种行为:主要产生一个子节点,它将自己的STDOUT重定向到一个管道。 然后父管读取该管道,并在受控窗口内输出管道。 屏幕的其余部分可以由父母自由操作,但这是必要的。
switch (pid=fork())
{
case 0: /* if child then write down pipe */
if(dup2(p[1], 1) == -1 ) /* stdout == write end of the pipe */
{
perror( "dup2 failed" );
return(1);
}
当调用fork时,如果我们是子节点(pid == 0),则取出之前创建的管道,并将我们自己的STDOUT切换到该管道的写入端。
setvbuf(stdout, NULL, _IOLBF, 1000);
将缓冲模式更改为line,以便我们的父级可以更轻松地抓取它。
while(!s_interrupted)
{
printf("Test %d\n",cycle);
cycle++;
usleep(10000);
}
生成一些简单的文本以检查它是否有效。
switch (pid=fork())
{
default:
if(dup2(p[0], 0 ) == -1 ) /* stdin == read end of the pipe */
{
perror( "dup2 failed" );
return(1);
}
如果我们是父节点,请将STDIN设置为我们之前创建的管道的读取端。
initscr(); /* start the curses mode */
getmaxyx(stdscr,row,col); /* get the number of rows and columns */
mvprintw(row/2,(col-strlen(mesg))/2,"%s",mesg);
/* print the message at the center of the screen */
refresh();
my_win2 = create_newwin(10,col,row-10, 0, 0, 0);
wrefresh(my_win2);
my_win = create_newwin(8,col-2,row-9, 1, ' ', ' ');
wrefresh(my_win);
wprintw(my_win,"This screen has %d rows and %d columns\n",row,col);
wprintw(my_win,"Try resizing your window(if possible) and then run this program again\n");
scrollok(my_win,TRUE);
初始化ncurses并创建一些窗口。 我们使用my_win2绘制边框,而无边界的my_win实际包含数据。 此外,我们将my_win设置为自动滚动,以便我们可以继续打印它,而不必担心空间不足。
while( std::getline(std::cin, mystr) )
{
ss.clear();
ss << mystr << std::endl;
mystr = ss.str();
waddstr(my_win,mystr.c_str());
wrefresh(my_win);
}
usleep(100000);
在一个轻松的节奏循环中,我们检查我们的STDIN,如果我们有新线,我们只需将它们添加到我们的滚动窗口。 因此,当我们的进程被安排在活动状态时,我们会花时间处理大量输出而不是重复轮询。
这证明是提出问题的有效解决方案。