我需要实现处理多个管道命令的shell。例如,我需要能够处理这个:ls | grep -i cs340 | sort | uniq | cut -c 5
。我假设问题是我没有将前一个命令的输出传递给下一个命令的输入。
当我执行我的代码时,它没有给我输出。我正在使用这个伪代码:
for cmd in cmds
if there is a next cmd
pipe(new_fds)
fork
if child
if there is a previous cmd
dup2(old_fds[0], 0)
close(old_fds[0])
close(old_fds[1])
if there is a next cmd
close(new_fds[0])
dup2(new_fds[1], 1)
close(new_fds[1])
exec cmd || die
else
if there is a previous cmd
close(old_fds[0])
close(old_fds[1])
if there is a next cmd
old_fds = new_fds
if there are multiple cmds
close(old_fds[0])
close(old_fds[1])
以下是处理多个管道的函数的源代码。
void execute_multiple_commands(struct command ** commands_to_exec,
int num_commands_p)
{
pid_t status;
int i, err;
int new_fd[2], old_fd[2];
pid_t pid, cpid;
// creating child process
if ( (cpid = fork()) == -1)
{
fprintf(stderr, "Could not create child process, exiting...");
exit(1);
}
if (cpid == 0) // in the child process we run multiple pipe handling
{
for (i = 0; i < num_commands_p; i++) // for each cmd in cmds
{
if (i+1 < num_commands_p) // if there is next cmd
pipe(new_fd);
if ( (pid = fork()) == -1)
{
fprintf(stderr, "Could not create child process, exiting...");
exit(1);
}
if (pid == 0) // if child
{
if (i != 0) // if there is a previous command
{
dup2(old_fd[0], 0); // setting up old_pipe to input into the child
close(old_fd[0]);
close(old_fd[1]);
}
if (i+1 < num_commands_p) // if there is a next cmd
{
close(new_fd[0]); // setting up new_pipe to get output from child
dup2(new_fd[1], 1);
close(new_fd[1]);
err = execvp(commands_to_exec[i]->args[0], commands_to_exec[i]->args);
status = err;
exit(err);
}
}
else
{
waitpid(pid, &status, 0);
if (status == -1)
exit(1);
if (i != 0) // if there a previous command
{
close(old_fd[0]);
close(old_fd[1]);
}
if (i+1 < num_commands_p) // if there a next cmd
{
old_fd[0] = new_fd[0];
old_fd[1] = new_fd[1];
}
exit(0);
} // end if
} // end for
if (i) // if there a multiple commands
{
close(old_fd[0]);
close(old_fd[1]);
}
}
else // in the parent process we are waiting for child to handle multiple pipes
waitpid(cpid, &status, 0);
}
函数execvp()
采用数组结构。我检查了所有解析部分,它工作正常。这是我遇到麻烦的execute_multiple_commands()
功能。
以下是struct的代码:
// name: command
// desc: holds one command (meaning that it can be
// more than one token in that command)
// "ls -la" will be an example of one command
// holds num of tokens in command array
struct command
{
char ** args;
int num_args;
};
答案 0 :(得分:2)
我建议采用新策略,R2:
function do(commands)
if commands is of size 1
exec commands[0] || die
split commands into c1 (first command) c2 (the rest of them)
open
if fork
close input end of pipe
dup output of pipe to stdin
do (c2) || die
close output end of pipe
dup input of pipe to stdout
exec c1 || die
使用递归函数,尤其是在维护列表时,将帮助您简化逻辑。你不必在这里担心堆栈深度,因为你的整个地址空间都会被覆盖。
在其他新闻中,来自man page:
从其中一个系统调用成功返回后,旧的和 新文件描述符可以互换使用。他们指的是 相同的打开文件描述(参见open(2)),从而共享文件偏移量 和文件状态标志;例如,如果文件偏移量被修改 在其中一个描述符上使用lseek(2),偏移也会改变 为了另一个。
当你说你正在关闭管道的两端时,这意味着什么?你真的在关闭它 - 它和你的程序打算使用的标准输入/输出。
- &GT;多次编辑&lt; -
正如Jonathan Leffler指出的那样,上述信息是正确的。我用以下程序确认了它:
#include <unistd.h>
int main(){
dup2(0, 7);
write(7, "Hey, 1\n", 7);
close(0);
write(7, "Hey, 2\n", 7);
close(7);
write(7, "Hey, 3\n", 7);
}
这导致以下输出:
$ gcc dup2Test.c && ./a.out
Hey, 1
Hey, 2
谢谢,乔纳森!