我的 C 程序中似乎没有存储或访问字符串令牌

如何解决我的 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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-