1. 尝试自行设计一个C语言小程序,完成最基本的shell角色:给出命令行提示符、能够逐次接受命令;对于命令分成三种,内部命令(例如help命令、exit命令等)、外部命令(常见的ls、cp等,以及其他磁盘上的可执行程序HelloWorld等)以及无效命令(不是上述三种命令)将上述shell进行扩展,使得你编写的shell程序具有支持管道的功能,也就是说你的shell中输入“dir |more”能够执行dir命令并将其输出通过管道将其输入传送给more作为标准输入。。
时间: 2023-11-22 16:55:26 浏览: 344
以下是一个简单的C语言实现的基本shell程序,它支持内部命令和外部命令:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_COMMAND_LENGTH 100
#define MAX_ARGUMENTS 10
char* read_command() {
char* line = NULL;
ssize_t buffer_size = 0;
getline(&line, &buffer_size, stdin);
return line;
}
int execute_command(char** args) {
pid_t pid;
int status;
pid = fork();
if (pid == 0) {
// child process
execvp(args[0], args);
// if execvp returns, it was not successful
perror("execvp");
exit(EXIT_FAILURE);
} else if (pid < 0) {
// fork error
perror("fork");
exit(EXIT_FAILURE);
} else {
// parent process
do {
waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
return status;
}
int main() {
char* command_line;
char* command;
char* arguments[MAX_ARGUMENTS + 1]; // extra element for NULL terminator
char* token;
int i;
while (1) {
printf("myshell> ");
command_line = read_command();
// split command line into command and arguments
token = strtok(command_line, " \n");
if (token == NULL) {
// empty command line
continue;
}
command = token;
arguments[0] = token;
for (i = 1; i <= MAX_ARGUMENTS; i++) {
token = strtok(NULL, " \n");
arguments[i] = token;
if (token == NULL) {
break;
}
}
arguments[i] = NULL;
// execute command
if (strcmp(command, "exit") == 0) {
// internal command: exit shell
exit(EXIT_SUCCESS);
} else if (strcmp(command, "help") == 0) {
// internal command: print help message
printf("This is a simple shell program.\n");
printf("Supported commands:\n");
printf(" help: print this help message\n");
printf(" exit: exit the shell\n");
} else {
// external command: execute program
execute_command(arguments);
}
free(command_line);
}
return 0;
}
```
接下来,我们可以扩展这个程序以支持管道。我们需要对输入的命令行进行解析,找到管道符号 `|`,将其左边和右边的命令分别执行,并将左边命令的输出作为右边命令的输入。以下是一个简单的实现:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_COMMAND_LENGTH 100
#define MAX_ARGUMENTS 10
char* read_command() {
char* line = NULL;
ssize_t buffer_size = 0;
getline(&line, &buffer_size, stdin);
return line;
}
int execute_command(char** args, int input_fd, int output_fd) {
pid_t pid;
int status;
int pipe_fds[2];
if (input_fd != STDIN_FILENO) {
dup2(input_fd, STDIN_FILENO);
close(input_fd);
}
if (output_fd != STDOUT_FILENO) {
dup2(output_fd, STDOUT_FILENO);
close(output_fd);
}
if (pipe(pipe_fds) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
pid = fork();
if (pid == 0) {
// child process
close(pipe_fds[0]);
if (output_fd != STDOUT_FILENO) {
dup2(pipe_fds[1], STDOUT_FILENO);
close(pipe_fds[1]);
}
execvp(args[0], args);
// if execvp returns, it was not successful
perror("execvp");
exit(EXIT_FAILURE);
} else if (pid < 0) {
// fork error
perror("fork");
exit(EXIT_FAILURE);
} else {
// parent process
close(pipe_fds[1]);
if (input_fd != STDIN_FILENO) {
close(input_fd);
}
if (output_fd != STDOUT_FILENO) {
close(output_fd);
}
do {
waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
fprintf(stderr, "Command exited with status %d\n", WEXITSTATUS(status));
}
return pipe_fds[0];
}
}
int main() {
char* command_line;
char* command;
char* arguments[MAX_ARGUMENTS + 1]; // extra element for NULL terminator
char* token;
int i;
int input_fd = STDIN_FILENO;
int output_fd = STDOUT_FILENO;
while (1) {
printf("myshell> ");
command_line = read_command();
// check for pipe symbol
char* pipe_symbol = strstr(command_line, "|");
if (pipe_symbol != NULL) {
// split command line into two commands
*pipe_symbol = '\0';
char* left_command_line = command_line;
char* right_command_line = pipe_symbol + 1;
// parse and execute left command
token = strtok(left_command_line, " \n");
if (token == NULL) {
// empty command line
continue;
}
command = token;
arguments[0] = token;
for (i = 1; i <= MAX_ARGUMENTS; i++) {
token = strtok(NULL, " \n");
arguments[i] = token;
if (token == NULL) {
break;
}
}
arguments[i] = NULL;
output_fd = execute_command(arguments, input_fd, STDOUT_FILENO);
// parse and execute right command
token = strtok(right_command_line, " \n");
if (token == NULL) {
// empty command line
continue;
}
command = token;
arguments[0] = token;
for (i = 1; i <= MAX_ARGUMENTS; i++) {
token = strtok(NULL, " \n");
arguments[i] = token;
if (token == NULL) {
break;
}
}
arguments[i] = NULL;
input_fd = execute_command(arguments, output_fd, STDIN_FILENO);
} else {
// no pipe symbol: parse and execute command
token = strtok(command_line, " \n");
if (token == NULL) {
// empty command line
continue;
}
command = token;
arguments[0] = token;
for (i = 1; i <= MAX_ARGUMENTS; i++) {
token = strtok(NULL, " \n");
arguments[i] = token;
if (token == NULL) {
break;
}
}
arguments[i] = NULL;
input_fd = execute_command(arguments, input_fd, STDOUT_FILENO);
output_fd = STDOUT_FILENO;
}
free(command_line);
}
return 0;
}
```
注意,这只是一个简单的实现,仍然有很多限制和潜在的错误,例如:
- 只支持一个管道符号,不支持多个管道连接。
- 不支持重定向符号 `<` 和 `>`。
- 没有处理管道连接中的错误,例如左边命令执行失败会导致右边命令被阻塞。
阅读全文