如何解决将命令输出解析为带有文件描述符的变量
我想让我的程序执行 md5sum 命令以生成给定文件的哈希值,然后将哈希值存储到数组 (char *
) 变量中。
我已经了解了 popen()
,但它涉及 FILE *
变量,我只想使用文件描述符。
有没有办法做到这一点?
解决方法
正如我在我的 comment 中所指出的,完全有可能实现 popen()
的功能,但让函数返回一个文件描述符而不是像 popen()
那样的文件流。该任务没有标准库函数。您需要创建一个管道和叉子。孩子会做管道,以便命令的标准输出进入管道的写端(读端关闭),然后执行命令。父进程将关闭管道的写入端,从管道的读取端读取响应,然后关闭它。并不是真的那么难——只是有点繁琐,仅此而已。
对应于 pclose()
的代码有点棘手。代码应该等待孩子死亡,还是至少尝试收集僵尸?如果是这样,它如何知道哪个 PID 适合等待?很容易只说“使用返回的文件描述符调用 close()
”,但这可能会留下僵尸。它应该等待孩子死,还是如果孩子死了就直接收集尸体,让其他代码处理僵尸?下面代码中实现的解决方案:
- 将文件描述符限制为 128 个(包括标准 I/O 通道)。
- 在固定大小的数组
pids
中记录与文件描述符关联的 PID。 - 使用
waitpid()
和已保存的与文件描述符关联的 PID,以 0(无条件等待)或WNOHANG
等待子进程。 - 如果孩子已经退出,则报告孩子的状态。
- 否则报告成功 - 0。
改变设计以便动态分配每个文件描述符的 PID 值数组是可行的。您可以控制 dpclose()
函数是等待子进程退出,还是在它尚未退出时不等待。
该代码不进行信号处理。这是另一层复杂性。
/* SO 6557-1879 */
/* #include "dpopen.h" */
#ifndef DPOPEN_H_INCLUDED
#define DPOPEN_H_INCLUDED
#include <fcntl.h> /* O_RDONLY or O_WRONLY for mode in dpopen() */
#include <sys/wait.h> /* WNOHANG for options in dpclose() */
/* dpopen() - similar to popen(),but returning a file descriptor */
/* The value in mode must be O_RDONLY or O_WRONLY */
extern int dpopen(char *cmd,int mode);
/* dpclose() - similar to pclose(),but working with a file descriptor returned by dpopen() */
/* The value in options must be 0 or WNOHANG */
/* The return value is the exit status of the child if available,0 if not,or -1 if there is a problem */
extern int dpclose(int fd,int options);
#endif /* DPOPEN_H_INCLUDED */
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
enum { MAX_PDOPEN_FD = 128 };
static pid_t pids[MAX_PDOPEN_FD];
int dpopen(char *cmd,int mode)
{
if (cmd == 0 || (mode != O_RDONLY && mode != O_WRONLY))
{
errno = EINVAL;
return -1;
}
int fd[2];
if (pipe(fd) != 0)
return -1;
/*
** Avoid embarrassment in debug builds if someone closed file
** descriptors too enthusiastically,and double check at run-time
** for non-debug builds. In some ways,it isn't very necessary as a
** runtime check - the circumstances are implausible. It is
** possible to code around fd[0] == STDIN_FILENO and fd[1] ==
** STDERR_FILENO,etc,but it is very messy to do so (having to
** avoid closing file descriptors,etc). It is simpler to close the
** two new file descriptors and return -1 with errno set to EINVAL
** if they overlap with the standard I/O descriptors. If this
** problem is detected,the program is already screwed up because at
** least one of standard input,standard output or standard error
** was closed.
*/
assert(fd[0] > STDERR_FILENO && fd[1] > STDERR_FILENO);
if (fd[0] <= STDERR_FILENO || fd[1] <= STDERR_FILENO)
{
close(fd[0]);
close(fd[1]);
errno = EINVAL;
return -1;
}
if (fd[0] >= MAX_PDOPEN_FD || fd[1] >= MAX_PDOPEN_FD)
{
close(fd[0]);
close(fd[1]);
errno = EMFILE;
return -1;
}
/*
** Prepare for forking - minimal step. See SO 5011-0992
** (https://stackoverflow.com/q/50110992 and
** https://stackoverflow.com/a/50112169/): "Why does forking my
** process cause the file to be read infinitely?"
** See also SO 0297-9209 (https://stackoverflow.com/q/2979209 and
** https://stackoverflow.com/a/34247021) "Using fflush(stdin)",** noting that Standard C and POSIX diverge somewhat; POSIX mandates
** behaviour that the C standard does not. It would be possible to
** ensure standard input is 'clean' using code such as:
**
** if (lseek(fileno(stdin),0L,SEEK_CURR) >= 0)
** fflush(stdin);
**
** Standard error is normally not a problem; by default,it is not
** fully buffered.
*/
fflush(stdout);
pid_t pid = fork();
if (pid < 0)
{
close(fd[0]);
close(fd[1]);
return -1;
}
if (pid == 0)
{
/* Child */
if (mode == O_RDONLY)
dup2(fd[1],STDOUT_FILENO);
else
dup2(fd[0],STDIN_FILENO);
close(fd[0]);
close(fd[1]);
char *argv[] = { "/bin/sh","-c",cmd,0 };
execv(argv[0],argv);
exit(EXIT_FAILURE);
}
/* Parent */
if (mode == O_RDONLY)
{
close(fd[1]);
pids[fd[0]] = pid;
return fd[0];
}
else
{
close(fd[0]);
pids[fd[1]] = pid;
return fd[1];
}
}
int dpclose(int fd,int options)
{
if (fd <= STDERR_FILENO || fd >= MAX_PDOPEN_FD || pids[fd] == 0 ||
(options != 0 && options != WNOHANG))
{
errno = EINVAL;
return -1;
}
if (close(fd) != 0)
return -1;
pid_t corpse;
int status;
pid_t child = pids[fd];
pids[fd] = 0;
if ((corpse = waitpid(child,&status,options)) == child)
return status;
return 0;
}
int main(void)
{
int fd1 = dpopen("ls -ltr",O_RDONLY);
int fd2 = dpopen("cat > ls.out; sleep 10",O_WRONLY);
if (fd1 < 0 || fd2 < 0)
{
fprintf(stderr,"failed to create child processes\n");
exit(EXIT_FAILURE);
}
char buffer[256];
ssize_t rbytes;
while ((rbytes = read(fd1,buffer,sizeof(buffer))) > 0)
{
ssize_t wbytes = write(fd2,rbytes);
if (wbytes != rbytes)
{
fprintf(stderr,"Failed to write data\n");
close(fd1);
close(fd2);
exit(EXIT_FAILURE);
}
}
if (dpclose(fd1,WNOHANG) < 0 || dpclose(fd2,0) < 0)
{
fprintf(stderr,"failed to close pipes correctly\n");
exit(EXIT_FAILURE);
}
return 0;
}
- 另见 Why does forking my process cause the file to be read infinitely? 和 我的answer。
- 另见 Using
fflush(stdin)
和 我的 answer,还要注意 SO 5011-0992 中的信息。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。