- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有一项任务是用 C 语言创建一个 Linux shell。目前,我一直致力于实现重定向和管道。我到目前为止的代码如下。 main() 解析用户的输入。如果命令是内置的,则执行该命令。否则,标记化的输入将传递给 execute()(我知道我可能应该将内置命令拉入它们自己的函数中)。
execute() 的作用是遍历数组。如果遇到<
, >
, 或 |
它应该采取适当的行动。我试图正确工作的第一件事是管道。不过,我肯定做错了什么,因为我什至不能让它为一根管道工作。例如,示例输入/输出:
/home/ad/Documents> ls -l | grep sh
|: sh: No such file or directory
|
我的想法是让每个方向和管道仅针对一种情况起作用,然后通过使函数递归,我希望可以在同一命令行中使用多个重定向/管道。例如,我可以做 program1 < input1.txt > output1.txt
或 ls -l | grep sh > output2.txt
.
我希望有人能指出我在尝试管道时的错误,并可能就如何处理用户输入多个重定向/管道的情况提供一些指导。
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int MAX_PATH_LENGTH = 1024; //Maximum path length to display.
int BUF_LENGTH = 1024; // Length of buffer to store user input
char * delims = " \n"; // Delimiters for tokenizing user input.
const int PIPE_READ = 0;
const int PIPE_WRITE = 1;
void execute(char **argArray){
char **pA = argArray;
int i = 0;
while(*pA != NULL) {
if(strcmp(argArray[i],"<") == 0) {
printf("<\n");
}
else if(strcmp(argArray[i],">") == 0) {
printf(">\n");
}
else if(strcmp(argArray[i],"|") == 0) {
int fds[2];
pipe(fds);
pid_t pid;
if((pid = fork()) == 0) {
dup2(fds[PIPE_WRITE], 1);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
char** argList;
memcpy(argList, argArray, i);
execvp(argArray[0], argArray);
}
if((pid = fork()) == 0) {
dup2(fds[PIPE_READ], 0);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
execvp(argArray[i+1], pA);
}
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
wait(NULL);
wait(NULL);
printf("|\n");
}
else {
if(pid == 0){
execvp(argArray[0], argArray);
printf("Command not found.\n");
}
else
wait(NULL);*/
}
*pA++;
i++;
}
}
int main () {
char path[MAX_PATH_LENGTH];
char buf[BUF_LENGTH];
char* strArray[BUF_LENGTH];
/**
* "Welcome" message. When mash is executed, the current working directory
* is displayed followed by >. For example, if user is in /usr/lib/, then
* mash will display :
* /usr/lib/>
**/
getcwd(path, MAX_PATH_LENGTH);
printf("%s> ", path);
fflush(stdout);
/**
* Loop infinitely while waiting for input from user.
* Parse input and display "welcome" message again.
**/
while(1) {
fgets(buf, BUF_LENGTH, stdin);
char *tokenPtr = NULL;
int i = 0;
tokenPtr = strtok(buf, delims);
if(strcmp(tokenPtr, "exit") == 0){
exit(0);
}
else if(strcmp(tokenPtr, "cd") == 0){
tokenPtr = strtok(NULL, delims);
if(chdir(tokenPtr) != 0){
printf("Path not found.\n");
}
getcwd(path, MAX_PATH_LENGTH);
}
else if(strcmp(tokenPtr, "pwd") == 0){
printf("%s\n", path);
}
else {
while(tokenPtr != NULL) {
strArray[i++] = tokenPtr;
tokenPtr = strtok(NULL, delims);
}
execute(strArray);
}
bzero(strArray, sizeof(strArray)); // clears array
printf("%s> ", path);
fflush(stdout);
}
}
最佳答案
部分问题出在管道处理代码中 - 如您所料。
else if (strcmp(argArray[i], "|") == 0) {
int fds[2];
pipe(fds);
pid_t pid;
if ((pid = fork()) == 0) {
dup2(fds[PIPE_WRITE], 1);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
char** argList;
memcpy(argList, argArray, i);
execvp(argArray[0], argArray);
}
if ((pid = fork()) == 0) {
dup2(fds[PIPE_READ], 0);
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
execvp(argArray[i+1], pA);
}
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
wait(NULL);
wait(NULL);
printf("|\n");
}
第一个 execvp()
可能打算使用 argList
,因为您刚刚在那里复制了一些 Material 。但是,您复制了 i
字节,而不是 i
字符指针,并且您没有确保管道被 zapped 和 替换为空指针。
memcpy(argList, argArray, i * sizeof(char *));
argList[i] = 0;
execvp(argList[0], argList);
请注意,这还没有验证 请注意,没有为argList
上没有缓冲区溢出;argList
分配空间;如果你使用它,你应该在执行 memcpy()
之前分配内存。
或者,更简单的是,您可以不使用副本。由于您处于子进程中,您可以简单地 zap 将 argArray[i]
替换为空指针,而不会影响父进程或其他子进程:
argArray[i] = 0;
execvp(argArray[0], argArray);
您可能还注意到 execvp()
的第二次调用使用了一个看不见的变量 pA
;它几乎肯定是错误初始化的。作为一个比较好的经验法则,你应该写:
execvp(array[n], &array[n]);
上面的调用不符合这个模式,但如果你遵循它,你就不会出错。
您还应该有基本的错误报告和 exit(1)
(或者可能是 _exit(1)
或 _Exit(1)
)在每个 execvp()
之后,这样子进程在执行失败时就不会继续。 execvp()
没有成功返回,但 execvp()
肯定可以返回。
现在,这些对 execvp()
的调用大概应该是您进行递归调用的地方。在尝试处理其他 I/O 重定向之前,您需要先处理管道。请注意,在标准 shell 中,您可以执行以下操作:
> output < input command -opts arg1 arg2
这是一种常规用法,但实际上是允许的。
一件好事 - 您已确保 pipe()
中的原始文件描述符在所有三个进程(父进程和两个子进程)中都已关闭。这是您避免犯的常见错误;干得好。
关于c - 制作 Linux shell 时的流重定向和管道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7549637/
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,
Linux 管道可以缓冲多少数据?这是可配置的吗? 如果管道的两端在同一个进程中,但线程不同,这会有什么不同吗? 请注意:这个“同一个进程,两个线程”的问题是理论上的边栏,真正的问题是关于缓冲的。 最
我找到了here [最后一页] 一种有趣的通过 Linux 启动 Linux 的方法。不幸的是,它只是被提及,我在网上找不到任何有用的链接。那么有人听说过一种避免引导加载程序而使用 Linux 的方法
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
我试图了解 ld-linux.so 如何在 Linux 上解析对版本化符号的引用。我有以下文件: 测试.c: void f(); int main() { f(); } a.c 和 b.c:
与 RetroPie 的工作原理类似,我可以使用 Linux 应用程序作为我的桌面环境吗?我实际上并不需要像实际桌面和安装应用程序这样的东西。我只需要一种干净简单的方法来在 RaspberryPi 上
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎不是关于 a specific programming problem, a softwar
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 10 年前。 Improve thi
有什么方法可以覆盖现有的源代码,我应该用 PyQt、PyGTK、Java 等从头开始构建吗? 最佳答案 如果您指的是软件本身而不是它所连接的存储库,那么自定义应用程序的方法就是 fork 项目。据我所
我的情况是:我在一个磁盘上安装了两个 linux。我将第一个安装在/dev/sda1 中,然后在/dev/sda2 中安装第二个然后我运行第一个系统,我写了一个脚本来在第一个系统运行时更新它。
我在 i2c-0 总线上使用地址为 0x3f 的系统监视器设备。该设备在设备树中配置有 pmbus 驱动程序。 问题是,加载 linux 内核时,这个“Sysmon”设备没有供电。因此,当我在总线 0
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 11 年前。 Improve thi
我正试图在 linux 模块中分配一大块内存,而 kalloc 做不到。 我知道唯一的方法是使用 alloc_bootmem(unsigned long size) 但我只能从 linux 内核而不是
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 这个问题似乎不是关于 a specific programming problem, a softwar
我有 .sh 文件来运行应用程序。在该文件中,我想动态设置服务器名称,而不是每次都配置。 我尝试了以下方法,它在 CentOS 中运行良好。 nohup /voip/java/jdk1.8.0_71/
我是在 Linux 上开发嵌入式 C++ 程序的新手。我有我的 Debian 操作系统,我在其中开发和编译了我的 C++ 项目(一个简单的控制台进程)。 我想将我的应用程序放到另一个 Debian 操
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 4 年前。 Improve this ques
我使用4.19.78版本的稳定内核,我想找到带有企鹅二进制数据的C数组。系统启动时显示。我需要在哪里搜索该内容? 我在 include/linux/linux_logo.h 文件中只找到了一些 Log
我知道可以使用 gdb 的服务器模式远程调试代码,我知道可以调试针对另一种架构交叉编译的代码,但是是否可以更进一步,从远程调试 Linux 应用程序OS X 使用 gdbserver? 最佳答案 当然
是否有任何可能的方法来运行在另一个 Linux 上编译的二进制文件?我知道当然最简单的是在另一台机器上重建它,但假设我们唯一能得到的是一个二进制文件,那么这可能与否? (我知道这可能并不容易,但我只是
我是一名优秀的程序员,十分优秀!