我想创建一个shell,其中子进程运行linux命令(在execvp的帮助下),如“ls”等。我也希望能够运行带有“ls -a”或“ls”等参数的命令ls -l / tmp“ 父必须等待子进程使用“waitpid”执行给定的命令。 当我试图用“ls -a”运行shell时,它将它作为2个单独的命令。输出:ls $ -a $
#include "stdio.h"
#include "unistd.h"
#include "stdlib.h"
int main(int argc, char **argv)
{
char input[64];
pid_t pid;
char *status;
char *args[64];
char **next = args;
char *temp;
while (1) {
printf("$");
fgets(input,"exit",stdin);
if (strcmp(input, "exit") == 0) {
exit(0)
}
pid = fork();
if (pid == 0) {
//child
*temp = strtok(input, " ");
while (temp != NULL) {
*next++ = temp;
*temp = strtok(NULL, " ");
}
if (execvp(args[0], args) == -1) {
perror("Execvp Error");
}
exit(0);
}else if (pid < 0) {
printf("error during fork");
}else {
//parent
waitpid(pid, &status, 0);
}
}
}
答案 0 :(得分:0)
如果编写一些辅助函数并提出合适的数据结构,那就变得微不足道了。例如:
main.c
:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "stringlist.h"
#include "io.h"
int main(void)
{
StringList list = NULL;
get_input(&list);
while ( strcmp(stringlist_string_at_index(list, 0), "exit") ) {
pid_t p = fork();
if ( p < 0 ) {
perror("fork() error");
exit(EXIT_FAILURE);
}
else if ( p == 0 ) {
char ** args = stringlist_raw_list(list);
execvp(args[0], args);
switch ( errno ) {
case EACCES:
printf("Error: access denied.\n");
break;
case ENOENT:
printf("Error: file not found.\n");
break;
default:
printf("Error: couldn't fulfill request.\n");
break;
}
exit(EXIT_FAILURE);
}
else {
int status;
waitpid(p, &status, 0);
}
get_input(&list);
}
stringlist_destroy(list);
return EXIT_SUCCESS;
}
包含帮助文件:
io.h
:
#ifndef IO_H
#define IO_H
#include "stringlist.h"
void get_input(StringList * list);
#endif /* IO_H */
io.c
:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "io.h"
#include "stringlist.h"
#define MAX_INPUT_LENGTH 256
static void get_input_line(char * buffer, const size_t buffer_size);
static void output_prompt(void);
static StringList tokenize_input(void);
/* Prompts for and gets input from standard input.
*
* If the StringList pointed to by `list` is not NULL, it will
* be destroyed. The StringList pointed to by `list` will be
* modified to point to a new StringList created from the input.
* If no input is entered, function will prompt for it again.
*
* Note: the input is tokenized purely by space characters, so input
* resembling:
*
* cat "Filename with spaces"
*
* will return four tokens, not two. This simple method of tokenizing
* will be unsuitable for many applications.
*/
void get_input(StringList * list)
{
if ( *list ) {
stringlist_destroy(*list);
}
do {
output_prompt();
*list = tokenize_input();
} while ( stringlist_length(*list) == 0 );
}
/* Gets a line of input from standard input.
*
* Function strips the trailing newline, if present, and
* exits the program on error.
*/
static void get_input_line(char * buffer, const size_t buffer_size)
{
if ( !fgets(buffer, buffer_size, stdin) ) {
fprintf(stderr, "error getting input\n");
exit(EXIT_FAILURE);
}
const size_t len = strlen(buffer);
if ( len > 0 && buffer[len - 1] == '\n' ) {
buffer[len - 1] = 0;
}
}
/* Outputs the shell prompt */
static void output_prompt(void)
{
printf("shell$ ");
fflush(stdout);
}
/* Gets a line of input from standard input and tokenizes it */
static StringList tokenize_input(void)
{
StringList list = stringlist_create();
char input[MAX_INPUT_LENGTH];
get_input_line(input, MAX_INPUT_LENGTH);
char * t = strtok(input, " ");
while ( t ) {
stringlist_add(list, t);
t = strtok(NULL, " ");
}
return list;
}
stringlist.h
:
#ifndef STRING_LIST_H
#define STRING_LIST_H
#include <stddef.h>
#include <stdbool.h>
typedef struct stringlist * StringList;
StringList stringlist_create(void);
bool stringlist_delete_last(StringList list);
void stringlist_destroy(StringList list);
size_t stringlist_length(StringList list);
char * stringlist_string_at_index(StringList list, const size_t index);
char ** stringlist_raw_list(StringList list);
void stringlist_add(StringList list, const char * str);
#endif /* STRING_LIST_H */
stringlist.c
:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "stringlist.h"
#define DEFAULT_LIST_SIZE 8
struct stringlist {
char ** list; /* Pointer to list of strings */
size_t size; /* Current capacity of list */
size_t top; /* Lowest empty element of list */
};
/* Creates a new list of default capacity */
StringList stringlist_create(void)
{
struct stringlist * new_list = malloc(sizeof *new_list);
if ( !new_list ) {
perror("memory allocation failed");
exit(EXIT_FAILURE);
}
new_list->size = DEFAULT_LIST_SIZE;
new_list->top = 0;
char ** list = calloc(new_list->size, sizeof *list);
if ( !list ) {
perror("memory allocation failed");
exit(EXIT_FAILURE);
}
new_list->list = list;
return new_list;
}
/* Deletes the last string in the list.
*
* Returns false if the list was empty, otherwise true.
*/
bool stringlist_delete_last(StringList list)
{
if ( list->top ) {
list->top -= 1;
free(list->list[list->top]);
list->list[list->top] = NULL;
return true;
}
return false;
}
/* Destroys the list and frees all resources */
void stringlist_destroy(StringList list)
{
while ( stringlist_delete_last(list) ) {
;
}
free(list->list);
free(list);
}
/* Returns the number of strings currently in the list */
size_t stringlist_length(StringList list)
{
return list->top;
}
/* Returns the string at the specified index of the list */
char * stringlist_string_at_index(StringList list, const size_t index)
{
return list->list[index];
}
/* Returns a pointer to the raw list of strings.
*
* This raw list will be NULL-terminated, that is, if the raw
* list contains `length` strings, then raw_list[length] == NULL.
* This makes the raw list suitable for passing, for instance, to
* execv() and friends.
*/
char ** stringlist_raw_list(StringList list)
{
return list->list;
}
/* Adds a string to the list.
*
* The raw list will be dynamically resized, if necessary.
*/
void stringlist_add(StringList list, const char * str)
{
if ( list->top + 1 >= list->size ) {
char ** new_array = realloc(list->list,
list->size * 2 * sizeof *new_array);
if ( !new_array ) {
perror("memory allocation failed");
exit(EXIT_FAILURE);
}
list->list = new_array;
list->size *= 2;
}
char * duped = strdup(str);
if ( !duped ) {
perror("memory allocation failed");
exit(EXIT_FAILURE);
}
list->list[list->top] = duped;
list->top += 1;
list->list[list->top] = NULL;
}
这会为空输入添加一些错误检查,并以更有意义的方式响应失败的execvp()
调用。
示例会话:
Paul@Pauls-iMac:~/Documents/src/sandbox/simple_shell$ ./ss
shell$
shell$
shell$ ./Fakefile
Error: file not found.
shell$ ./Makefile
Error: access denied.
shell$ ls -alF
total 96
drwxr-xr-x 12 Paul staff 408 Nov 12 21:18 ./
drwxr-xr-x 6 Paul staff 204 Nov 12 20:42 ../
-rw-r--r-- 1 Paul staff 368 Nov 12 21:07 Makefile
-rw-r--r-- 1 Paul staff 2016 Nov 12 21:18 io.c
-rw-r--r-- 1 Paul staff 113 Nov 12 21:10 io.h
-rw-r--r-- 1 Paul staff 2240 Nov 12 21:18 io.o
-rw-r--r-- 1 Paul staff 1214 Nov 12 21:08 main.c
-rw-r--r-- 1 Paul staff 1608 Nov 12 21:11 main.o
-rwxr-xr-x 1 Paul staff 10032 Nov 12 21:18 ss*
-rw-r--r-- 1 Paul staff 2799 Nov 12 20:52 stringlist.c
-rw-r--r-- 1 Paul staff 504 Nov 12 20:53 stringlist.h
-rw-r--r-- 1 Paul staff 2492 Nov 12 21:11 stringlist.o
shell$ ps
PID TTY TIME CMD
75221 ttys002 0:00.19 -bash
75678 ttys002 0:00.00 ./ss
shell$ echo Hello, world!
Hello, world!
shell$ cat "Tokenizing input with spaces is generally bad.txt"
cat: "Tokenizing: No such file or directory
cat: input: No such file or directory
cat: with: No such file or directory
cat: spaces: No such file or directory
cat: is: No such file or directory
cat: generally: No such file or directory
cat: bad.txt": No such file or directory
shell$ exit
Paul@Pauls-iMac:~/Documents/src/sandbox/simple_shell$
答案 1 :(得分:-2)
我认为purkavlos在这里给了我们一个示例代码(它还没有完全正常运行),以便我们向他展示通过子进程运行命令的逻辑...只是纠正部分代码并不是很有帮助..
解决方案: 通过使用gets()而不是scanf()将解决您的第一个问题,以便您可以接受带有空格的字符串,如Paul所说。 检查是否适合您,因为我可以看到标记化是正确的。 之后命令应该运行但我不确定输出,你是通过孩子得到的吗?