我正在尝试使用
popen()
来捕获调用的 stderr,但当然它似乎并没有这样做。有什么想法吗?
我的代码看起来或多或少像这样:
popen("nedit", "r");
但是我的屏幕上出现了所有关于非 utf8 的垃圾...
popen
为您提供进程标准输出的文件句柄,而不是其标准错误。它的第一个参数被解释为 shell 命令,因此您可以在其中进行重定向:
FILE *p = popen("prog 2>&1", "r");
或者,如果您根本不需要标准输出,
FILE *p = popen("prog 2>&1 >/dev/null", "r");
(除了
/dev/null
之外的任何其他文件也可以。)
如果你想放弃所有的错误消息,那么你可以使用:
popen("nedit 2>/dev/null", "r");
如果您同时需要 stdout 和 stderr 流,您可以打开一个额外的管道并将子 stderr 重定向到它:
if (pipe(pfd) < 0)
return -1;
perr = fdopen(pfd[0], "r");
snprintf(command, LINE_LEN, "... 2>&%d", pfd[1]);
if (pout = popen(command, "r")) {
close(pfd[1]);
while (fgets(line, LINE_LEN, pout) != NULL)
...
while (fgets(line, LINE_LEN, perr) != NULL)
...
pclose(pout);
}
fclose(perr);
close(pfd[0]), close(pfd[1]);
正如 Andrew 指出的,如果
command
生成大量输出/错误,则必须更仔细地处理流(请参阅下面的评论)。
Andre Luis da Silva Monteiro 的解决方案非常出色。 只是为了提供额外的选择,我添加了下面的提案。取代了 popen(),而是创建了一个新进程,提供所有连接管道 - stdin、stdout 和 stderr。 Andrew Henle 的评论也适用。
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/wait.h>
#include <stdlib.h>
/* These definitions are for issuing error messages.
*/
#define WarningMessage( ErrMess) fprintf(stderr, "%s() (file %s, line %d): Warning: %s\n", \
__func__, __FILE__, __LINE__, (ErrMess) )
#define ErrorMessage( ErrMess) fprintf(stderr, "%s() (file %s, line %d): Error: %s\n", \
__func__, __FILE__, __LINE__, (ErrMess) )
#define ErrorMessageFromCall(Func) fprintf(stderr, "%s() (file %s, line %d): Error from function %s(): Errno: (%d) [%s]\n", \
__func__, __FILE__, __LINE__, (Func), errno, strerror(errno))
#define ErrorMessageFromCall1(Func) fprintf(stderr, "%s() (file %s, line %d): Error from function %s()\n", \
__func__, __FILE__, __LINE__, (Func))
#define buffLen 1024
#define MAX_EVENTS 10
int main()
{
int pip0[2], pip1[2], pip2[2];
int FDChildStdin, FDChildStdout, FDChildStderr;
int iret, i;
pid_t PID, PID1;
/* This is the command to execute and its arguments. See execve() for more details */
char Command[]="/usr/bin/ls";
char *argv[3]={ [0]="ls", "-l", };
char *envp[1]={};
char buff[buffLen];
int FDEpollInst;
struct epoll_event ev, events[MAX_EVENTS];
int nfds;
iret= pipe( pip0);
if( iret)
{
ErrorMessageFromCall( "pipe()");
return -1;
}
iret= pipe( pip1);
if( iret)
{
ErrorMessageFromCall( "pipe()");
return -1;
}
iret= pipe( pip2);
if( iret)
{
ErrorMessageFromCall( "pipe()");
return -1;
}
PID= fork();
if( PID < 0)
{
ErrorMessageFromCall("fork()");
return -2;
}
if( PID == 0)
{
/* here we are in the child process
This child process will be reading from pip0, writing to pip2 and pip3.
* This will be the new stdin, stdout and stderr.
* The process should not know about the previous file descriptors. */
close( pip0[1]);
close( pip1[0]);
close( pip2[0]);
iret= dup2(pip2[1], 2);
if( iret == -1)
{ /* This will be probably the last message written to the original stderr from this process... */
ErrorMessageFromCall( "In child process: pipe()");
return -1;
}
iret= dup2(pip1[1], 1);
if( iret == -1)
{ /* This will already go to the stderr pipe. See the previous call. */
ErrorMessageFromCall( "pipe()");
return -1;
}
iret= dup2(pip0[0], 0);
if( iret == -1)
{
ErrorMessageFromCall( "pipe()");
return -1;
}
close( pip0[0]);
close( pip1[1]);
close( pip2[1]);
iret= execve(Command, argv, envp);
if( iret == -1)
{
ErrorMessageFromCall( "execve()");
return -1;
}
/* Now, the Command runs in this process. No more control in this code. */
} else
{
/* here we are in the parent process */
/* The child process will be writing to pip0, reading from pip2 and pip3.
* This process will use the oposite ends of the pipes. */
FDChildStdin= pip0[1];
FDChildStdout= pip1[0];
FDChildStderr= pip2[0];
FDEpollInst= epoll_create1(0);
if (FDEpollInst == -1)
{
ErrorMessageFromCall( "epoll_create1()");
return -2;
}
ev.events = EPOLLIN;
ev.data.fd = FDChildStdout;
if(epoll_ctl(FDEpollInst, EPOLL_CTL_ADD, FDChildStdout, &ev) == -1)
{
ErrorMessageFromCall( "epoll_ctl()");
return -2;
}
ev.events = EPOLLIN;
ev.data.fd = FDChildStderr;
if(epoll_ctl(FDEpollInst, EPOLL_CTL_ADD, FDChildStderr, &ev) == -1)
{
ErrorMessageFromCall( "epoll_ctl()");
return -2;
}
for( ;; )
{
/* Wait for data from child. Timeout 10 seconds.
* Would be better to decrease timeout in subsequent calls,
* if loop is repeated because of interrupt (errno == EINTR). */
nfds = epoll_wait(FDEpollInst, events, MAX_EVENTS, 10000);
if( (nfds == -1) && (errno != EINTR))
{
ErrorMessageFromCall("epoll_wait()");
return -2;
}
if( nfds > -1) break;
}
if( nfds == 0)
{
printf( "No data from child available within the timeout.\n");
}
for( i= 0; i< nfds; i++)
{
if( events[i].events & EPOLLIN) printf( "file is available for read(2) operations.\n");
if( events[i].events & EPOLLOUT) printf( "file is available for write(2) operations.\n");
if( events[i].events & EPOLLRDHUP) printf( "Stream socket peer closed connection, or shut down writing half of connection.\n");
if( events[i].events & EPOLLPRI) printf( "exceptional condition on the file descriptor.\n");
if( events[i].events & EPOLLERR) printf( "Error condition happened on the associated file descriptor\n");
if( events[i].events & EPOLLHUP) printf( "Hang up happened on the associated file descriptor.\n");
if( events[i].data.fd == FDChildStdout)
{
printf("Event in stdout of child.\n" );
iret= read(FDChildStdout, buff, buffLen);
/* Do not be tempted to read again, when not all data have been read at once.
* This is a blocking fd! It is necessary to check for presence of data before another
* attempt of read. Else it may hang forever.
* Same below for stderr. */
fprintf( stdout, "stdout from child: ");
write( 1, buff, iret);
} else if( events[i].data.fd == FDChildStderr)
{
printf("Event in stderr of child.\n" );
iret= read(FDChildStderr, buff, buffLen);
fprintf( stdout, "stderr from child: ");
write( 1, buff, iret);
} else
{
printf("Event in unknown fd!\n" );
}
}
/* Wait for child to catch return code. */
PID1= wait(&iret);
if (PID1 < 0)
printf("Failed to wait for process %d (errno = %d)\n", (int)PID, errno);
else if (PID1 != PID)
printf("Got return status of process %d (status 0x%.4X) when expecting PID %d\n",
PID1, iret, (int)PID);
else if (WIFEXITED(iret))
printf("Process %d exited with normal status 0x%.4X (status %d = 0x%.2X)\n",
PID1, iret, WEXITSTATUS(iret), WEXITSTATUS(iret));
else if (WIFSIGNALED(iret))
printf("Process %d exited because of a signal 0x%.4X (signal %d = 0x%.2X)\n",
PID1, iret, WTERMSIG(iret), WTERMSIG(iret));
else
printf("Process %d exited with status 0x%.4X which is %s\n",
PID1, iret, "neither a normal exit nor the result of a signal");
}
return 0;
}