如何解决我的 C 程序中似乎没有存储或访问字符串令牌
我正在尝试用 C 构建一个简单的 shell 程序。我最近发布了有关此内容的帖子,但从那时起我的程序发生了一些变化。如果需要,我可以编辑我之前的问题,但我真的只是想了解我在这里做错了什么。
我对我的代码看起来有点尴尬。感觉既笨重又复杂,但我不知道该怎么做才能让它变小,但我什至无法像现在这样正确。
我想获取用户输入,并将其分解为最终可以传递给 execvp 的 shell 参数。我需要能够区分没有“|”的输入和 '>' 字符,并用它们输入。目前,我正在做的事情似乎导致了段错误。但我不确定如何处理 gdb 给我的错误消息。
这里不胜感激任何批评或提示。我一直在为这件事绞尽脑汁,现在真的很想了解一下。
Program received signal SIGSEGV,Segmentation fault.
__strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:65
65 ../sysdeps/x86_64/multiarch/strlen-avx2.S: No such file or directory.
(gdb) bt
#0 __strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:65
#1 0x00007ffff7e4b5b4 in __GI__IO_puts (str=0x0) at ioputs.c:35
#2 0x000055555555573a in tokenizeInput ()
#3 0x0000555555555471 in main ()
#include <stdio.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define MAX_BUF_SZ 1024
void checkForPipe(char *string,bool *pipe_bool);
void checkForRedirect(char *string,bool *redirect_bool);
void tokenizeInput(char *string,char **argz,bool *pipe,bool *redirect,int *stats);
void executeCommand(int *stats,char **argz);
int main()
{
char *ptr;
bool is_pipe = false;
bool is_redirect_out = false;
bool is_exit = false;
int status = 0; //Will determine how to execute execvp()
char *args[100] = {NULL};
ptr = (char*)malloc(MAX_BUF_SZ);
while(!is_exit)
{
// Diplay prompt
char cur_dir[MAX_BUF_SZ];
getcwd(cur_dir,MAX_BUF_SZ);
printf("SHELL:%s$ ",cur_dir);
printf("%d\n",status);
fgets(ptr,MAX_BUF_SZ,stdin);
checkForPipe(ptr,&is_pipe);
checkForRedirect(ptr,&is_redirect_out);
printf("pipe flag = %d\n",is_pipe);
printf("redirect flag = %d\n",is_redirect_out);
if(strcmp(ptr,"exit\n") == 0)
{
is_exit = true;
}
tokenizeInput(ptr,args,&is_pipe,&is_redirect_out,&status);
printf("is token being called?\n");
executeCommand(&status,args);
printf("is execute being called?\n");
printf("%d\n",status);
}
return 0;
}
void checkForPipe(char *string,bool *pipe_bool)
{
char *check_for_pipes;
char *clean_compare;
check_for_pipes = (char*)malloc(MAX_BUF_SZ);
clean_compare = (char*)malloc(MAX_BUF_SZ);
strcpy(check_for_pipes,string);
strcpy(clean_compare,string);
char * token = strtok(check_for_pipes,"|");
if(strcmp(token,clean_compare) == 0)
{
free(clean_compare);
free(check_for_pipes);
}
else
{
*pipe_bool = 1;
free(clean_compare);
free(check_for_pipes);
}
}
void checkForRedirect(char *string,bool *redirect_bool)
{
char *check_for_redirects;
char *clean_compare;
check_for_redirects = (char*)malloc(MAX_BUF_SZ);
clean_compare = (char*)malloc(MAX_BUF_SZ);
strcpy(check_for_redirects,string);
char * token = strtok(check_for_redirects,">");
if(strcmp(token,clean_compare) == 0)
{
free(clean_compare);
free(check_for_redirects);
}
else
{
*redirect_bool = 1;
free(clean_compare);
free(check_for_redirects);
}
}
void tokenizeInput(char *string,int *stats)
{
int i = 0;
if(*pipe == false && *redirect == false)
{
char *token = strtok(string," \n");
argz[i] = token;
while (token != NULL)
{
token = strtok(NULL," \n");
i++;
argz[i] = token;
printf("%s\n",argz[i]);
}
i = 0;
*stats = 1; //1 indicates no pipe or redirect
}
else if(*pipe == true && *redirect == false)
{
char *token = strtok(string,"|\n");
argz[i] = token;
while (token != NULL)
{
token = strtok(NULL,"|\n");
i++;
argz[i] = token;
}
*stats = 2; // 2 indicates a sole pipe
*pipe = false;
}
else if(*pipe == false && *redirect == true)
{
char *token = strtok(string,">\n");
argz[i] = token;
while (token != NULL)
{
token = strtok(NULL,">\n");
i++;
argz[i] = token;
}
*stats = 3; // 3 indicates a sole redirect
*redirect = false;
}
else if(*pipe == true && *redirect == true)
{
char *token = strtok(string,"|>\n");
argz[i] = token;
while (token != NULL)
{
token = strtok(NULL,"|>\n");
i++;
argz[i] = token;
}
*stats = 4; // 4 indicates a pipe and a redirect
*pipe = false;
*redirect = false;
}
}
void executeCommand(int *stats,char **argz)
{
if(*stats == 1)
{
printf("this is the value stored in arg 0 %s\n",argz[0]);
printf("this is the value stored in arg 1 %s\n",argz[1]);
pid_t child_pid = fork();
if(child_pid < 0)
{
//Forking Error!
printf("Forking Error Occured!\n");
exit(0);
}
else if(child_pid == 0)
{
printf("%s\n",argz[0]);
if(execvp(argz[0],argz) == -1)
{
printf("Error running command\n");
exit(0);
}
}
else
{
wait(NULL);
}
}
}
解决方法
如何读取错误:
// SIGSEGV means it is a pointer operation.
//
Program received signal SIGSEGV,Segmentation fault.
//
// ... while trying to get the length of a string.
//
__strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:65
65 ../sysdeps/x86_64/multiarch/strlen-avx2.S: No such file or directory.
(gdb) bt
#0 __strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:65
//
// str=0x0 _> ah ah! It's a NULL pointer error. In an I/O put() function
// since you do not open files,that's probably in a call to printf().
// One of its arguments is NULL
//
#1 0x00007ffff7e4b5b4 in __GI__IO_puts (str=0x0) at ioputs.c:35
//
// The function called from main is tokenizeInput().
//
#2 0x000055555555573a in tokenizeInput ()
#3 0x0000555555555471 in main ()
我认为错误出在 tokenizeInput() 中,但是还有更多的候选对象,它们在整个代码中都会以相同的方式失败。你必须在使用之前检查 strtok() 的结果。
您使用 strtok 查找单个符号会强制您分配内存并创建副本,为了在字符串中查找符号,请使用 strchr()。你的 checkForRedirect() 函数然后变成...
// why pass a pointer for the result ? Wouldn't this be easier to work with?
bool checkForRedirect(const char* string)
{
return strchr(string,'>') != NULL;
}
// in yours,this test doesn't quite make any sense.. apart from the fact that
// token could be NULL,and you don't test for it,the string token != clean_compare
// always if token is not NULL at this point.
//
char* token = strtok(check_for_redirects,">"); // token could be NULL
if(strcmp(token,clean_compare) == 0) // SIGSEGV here
// should be:
if (token && strcmp(token,...
这是您的代码现在崩溃的地方,非常相似。
void tokenizeInput(char *string,char **argz,bool *pipe,bool *redirect,int *stats)
{
int i = 0;
if (!*pipe && !*redirect)
{
char *token = strtok(string," \n");
argz[i] = token;
while (token != NULL)
{
token = strtok(NULL," \n");
i++;
argz[i] = token;
// At some point,token is NULL
printf("%s\n",argz[i]); // At some point,token is NULL,this fails.
}
i = 0;
*stats = 1; //1 indicates no pipe or redirect
}
/* ... */
}
也许重写循环会有所帮助。
/* ... */
char *token = NULL;
int i = 0;
for (token = strtok(string," \n"),i = 0; token; token = strtok(NULL,++i)
{
argz[i] = token; // token is never NULL here.
printf(argz[i]);
printf("\n");
}
/* ... */
[编辑]
我认为您从错误的角度解决了这个问题......您真正拥有的是一个命令行,其中包含一个或多个要执行的命令,由管道符号分隔,并可选择通过直接输出到文件符号来终止' >' 和一个文件名。
第一步是将可能的重定向文件与其他文件分开。
第 2 步解析所有命令。
第3步执行命令,创建并连接管道,创建并插入 在可选的重定向文件中。
第 4 步执行操作栈。
第 5 步清理。
/* ... */
// your command data
struct _command_t
{
const char** argv;
int argc;
int in_pipe; // file descriptors for i/O
int out_pipe;
} command_t;
char* input_line;
char* cmd;
char* ptr;
command_t* temp_command;
// an array to store commands.
command_t* commands = (command_t*)malloc(max_commands * sizeof(command_t));
int max_commands = 16;
int commands_count = 0;
// a string for file to redirect output to.
const char* redir_file = NULL;
/* ... */
for (;;)
{
commands_count = 0;
fgets(input_line,MAX_BUF_SZ,stdin);
// first get redirection,if any
if ((str = strchr(input_line,'>') != NULL)
{
if ((redir_file = parse_redir_filename(str + 1)) == NULL)
{
printf("error parsing redir file.\n");
exit(3);
}
*str = 0; // truncate input line
}
// here's one reason strtok is not well-suited for this task...
// what happens if cmd does not have a closing '\n' symbol?
for (cmd = strtok(input_line,"|\n"); cmd; cmd = strtok(NULL,"|\n"))
{
if (commands_count >= max_commands)
{
temp_commands = (command_t*)realloc(commands,(max_commands + 16) * sizeof(command_t));
if (!temp_commands)
{
printf("out of memory\n");
exit(3);
}
commands = temp_commands;
max_commands += 16;
}
memset(&(commands[commands_count]),sizeof(command_t));
// assuming parse_command returns negative on error
if (parse_command(cmd,commands[commands_count] < 0)
{
printf("error parsing command \"%s\".\n",cmd);
exit(3);
}
++commands_count;
}
if (commands_count == 0)
break;
/* process the command stack ... */
}
这是一个相当简单的文件和参数解析器,它应该在大多数情况下工作。
// returns null-terminated token,// on exit,str is advanced to symbol following token.
const char* parse_token(char** str)
{
const char* result = NULL;
// skip whitespace
while (**str != 0 && isspace(**str & 0xFF))
++(*str);
if (**str == 0)
return NULL;
if (**str == '\'')
{
result = ++(*str);
// skip to closing quote
while (**str != 0 && **str != '\'')
++(*str);
// validate and null terminate
if (**str != '\'')
return NULL;
**str = 0;
++(*str);
/* here,you may want to check if it's a file or dir name */
}
else
{
result = *str;
while (**str != 0 && !isspace(**str & 0xFF))
++(*str);
// null terminate (clobbering whitespace)
if (**str != 0)
{
**str = 0;
++(*str);
}
}
return result;
}
解析redir文件名。
const char* parse_redir_filename(char* str)
{
const char* result = parse_token(&str);
if (!result)
return NULL;
// make sure we have either nothing or whitespace following.
while (*str != 0)
if (!isspace(*(str++) & 0xFF))
return NULL;
return result;
}
解析每个单独的命令应该是相似的。调用 parse_token() 并将令牌放置在动态数组comment_t::args 中。请注意我如何在主循环中处理动态数组的大小调整。
int parse_command(char* str,command_t* command)
{
int max_args = 16;
if (!str || !command)
return -1;
command->argv = (char**)malloc(max_args * sizeof(char*));
if (!command->argv)
return -1;
while (*str != 0)
{
/* insert code to resize command->argv as needed */
/* here. */
command->argv[command->argc] = parse_token(&str);
if (!command->argv[command->argc])
break;
++command->argc;
}
if (!command->argc)
{
free(command->argv);
command->argv = NULL;
return -1;
}
return 0; // success!
}
上面的代码未经测试,也未经编译,但它应该可以帮助您更接近您想要实现的目标。它相当幼稚和有限,因为它不处理注释,也不处理多行命令。这些可以直接在主循环中处理。它也不处理环境变量扩展,但这有点超出本讨论的范围,并且可以在步骤 3 和 4 之间进行相当优雅的处理。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。