c - (在 C 中模拟 UNIX 外壳)如何在 for 循环中实现多个管道?
问题描述
我正在尝试在 C 程序中模拟 unix shell,它仍处于开始阶段并且最多可用于两个管道。我有一个命令向量 (char *com[3][3]),考虑到字符“|”,它们是分开的,但我的问题是如何在 for 循环中继续处理更多管道?在遵循当前的实现中,我正在尝试执行由管道分隔的 3 个命令:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <sys/types.h>
int main(int argc, char **argv){
//Vector with positions of pipes found, position 0 reserved for the total amount of commands.
char* com[3][3] = { { "/bin/ls", "-la", 0 },
{ "/bin/grep", ".", 0}, { "/usr/bin/wc", "-l", 0 }};
//EXECUTE COMMANDS
pid_t fork1, fork2, fork3;
int fd1[2], fd2[2];
if(pipe(fd1) < 0){
perror("pipe1");
}
if(pipe(fd2) < 0){
perror("pipe2");
}
//COMMAND 1
fork1 = fork();
if(fork1 == 0){
dup2(fd1[1], STDOUT_FILENO);
close(fd1[0]);
close(fd2[0]);
close(fd2[1]);
execvp(com[0][0], com[0]);
perror("execvp 1");
exit(EXIT_FAILURE);
}
//COMMAND 2
fork2 = fork();
if(fork2 == 0){
dup2(fd1[0], STDIN_FILENO);
dup2(fd2[1], STDOUT_FILENO);
close(fd1[1]);
close(fd2[0]);
execvp(com[1][0], com[1]);
perror("execvp 2");
exit(EXIT_FAILURE);
}
//COMMAND 3
fork3 = fork();
if(fork3 == 0){
dup2(fd2[0], STDIN_FILENO);
close(fd2[1]);
close(fd1[0]);
close(fd1[1]);
execvp(com[2][0], com[2]);
perror("execvp 3");
exit(EXIT_FAILURE);
}
close(fd1[0]);
close(fd1[1]);
close(fd2[0]);
close(fd2[1]);
waitpid(-1, NULL, 0);
waitpid(-1, NULL, 0);
waitpid(-1, NULL, 0);
return 0;
}
如何在 for 循环中生成 com[n][3]?
解决方案
“迭代是人类,递归是神圣的”——Anon。
我会用递归方法来解决这个问题。这是成为三星级程序员几乎是正当的极少数情况之一。;)
这是完全未经测试的,但应该让你指出正确的方向。
// You'll need to rearrange your command strings into this three dimensional array
// of pointers, but by doing so you allow an arbitrary number of commands, each with
// an arbitrary number of arguments.
int executePipe(char ***commands, int inputfd)
{
// commands is NULL terminated
if (commands[1] == NULL)
{
// If we get here there's no further commands to execute, so run the
// current one, and send its result back.
pid_t pid;
int status;
if ((pid = fork()) == 0)
{
// Set up stdin for this process. Leave stdout alone so output goes to the
// terminal. If you want '>' / '>>' redirection to work, you'd do that here
if (inputfd != -1)
{
dup2(inputfd, STDIN_FILENO);
close(inputfd);
}
execvp(commands[0][0], commands[0]);
perror("execvp");
exit(EXIT_FAILURE);
}
else if (pid < 0)
{
perror("fork");
exit(EXIT_FAILURE);
}
waitpid(pid, &status, 0);
return status;
}
else
{
// Somewhat similar to the above, except we also redirect stdout for the
// next process in the chain
int fds[2];
if (pipe(fds) != 0)
{
perror("pipe");
exit(EXIT_FAILURE);
}
pid_t pid;
int status;
if ((pid = fork()) == 0)
{
// Redirect stdin if needed
if (inputfd != -1)
{
dup2(inputfd, STDIN_FILENO);
close(inputfd);
}
dup2(fds[1], STDOUT_FILENO);
close(fds[1]);
execvp(commands[0][0], commands[0]);
perror("execvp");
exit(EXIT_FAILURE);
}
else if (pid < 0)
{
perror("fork");
exit(EXIT_FAILURE);
}
// This is where we handle piped commands. We've just executed
// commands[0], and we know there's another command in the chain.
// We have everything needed to execute that next command, so call
// ourselves recursively to do the heavy lifting.
status = executePipe(++commands, fds[0]);
// As written, this returns the exit status of the very last command
// in the chain. If you pass &status as the second parameter here
// to waitpid, you'll get the exit status of the first command.
// It is left as an exercise to the reader to figure how to get the
// the complete list of exit statuses
waitpid(pid, NULL, 0);
return status;
}
}
要使用它,最初调用它commands
并按照描述设置数组,inputfd
最初调用-1
.
如果您想处理<
类型重定向,您可能希望inputfd == -1
在最顶部进行检查,如果请求进行重定向,并inputfd
在输入正文的其余部分之前替换为适当的值。
推荐阅读
- python-3.x - 如何处理此图像以获得清晰的数字
- npm - 使用“npm i curlconverter”安装公共包会导致“致命:无法从远程存储库读取”
- macos - macOS 上 SwiftUI 的内存泄漏和减速
- delphi - 尖括号的虚拟键码
- javascript - 如何使用 Firebase SDK v9(模块化)在 Firebase 实时数据库中读取、写入和查询数据
- swift - 所有子节点的组合边界框
- php - Twilio - 如何在 xml 语音文件中设置语言参数?
- gitlab - 我们可以在 Gitlab 中为单个键添加多个值吗
- java - 反序列化期间未映射标有 @JsonRootName 的类
- node.js - 来自服务器的所有路由都在本地工作,但在部署到 heroku 时返回 404