1

我想特别理解这段代码 parse_args。这段代码就像运行 pwd cat 等基本 linux 命令的简单 shell。我想了解 parse_args 是如何工作的。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#define BUFFER_SIZE 1<<16
#define ARR_SIZE 1<<16

void parse_args(char *buffer, char** args, 
                size_t args_size, size_t *nargs)
{
    char *buf_args[args_size]; /* You need C99 */
    char **cp;
    char *wbuf;
    size_t i, j;

    wbuf=buffer;
    buf_args[0]=buffer; 
    args[0] =buffer;

    for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
        if ((*cp != '\0') && (++cp >= &buf_args[args_size]))
            break;
    }

    for (j=i=0; buf_args[i]!=NULL; i++){
        if(strlen(buf_args[i])>0)
            args[j++]=buf_args[i];
    }

    *nargs=j;
    args[j]=NULL;
}


int main(int argc, char *argv[], char *envp[]){
    char buffer[BUFFER_SIZE];
    char *args[ARR_SIZE];

    int *ret_status;
    size_t nargs;
    pid_t pid;

    while(1){
        printf("$ ");
        fgets(buffer, BUFFER_SIZE, stdin);
        parse_args(buffer, args, ARR_SIZE, &nargs); 

        if (nargs==0) continue;
        if (!strcmp(args[0], "exit" )) exit(0);       
        pid = fork();
        if (pid){
            printf("Waiting for child (%d)\n", pid);
            pid = wait(ret_status);
            printf("Child (%d) finished\n", pid);
        } else {
            if( execvp(args[0], args)) {
                puts(strerror(errno));
                exit(127);
            }
        }
    }    
    return 0;
}
4

3 回答 3

4

好好玩。该函数将缓冲区分隔为一个字符串数组,并在其中遇到一个空白分隔符。

void parse_args(char *buffer, char** args, 
                size_t args_size, size_t *nargs)
{
    char *buf_args[args_size]; /* You need C99 */
    char **cp;
    char *wbuf;
    size_t i, j;

初始化代码...

    wbuf=buffer;
    buf_args[0]=buffer; 
    args[0] =buffer;

将所有指针设置为缓冲区的开头

    for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
  1. 将写指针设置为指向cpbuf_args 数组中的第一个元素。
  2. 将 *cp 设置为缓冲区中非空白标记的下一个实例,并前进wbuf到其结束位置

        if ((*cp != '\0') && (++cp >= &buf_args[args_size]))
            break;
    

当返回 NULL 时停止strsep,或者当它指向缓冲区的末尾,或者当它指向超出 buf_args[] 的末尾时

    }

    for (j=i=0; buf_args[i]!=NULL; i++){
        if(strlen(buf_args[i])>0)
            args[j++]=buf_args[i];
    }

此循环创建 buf_args[] 数组的压缩副本:它仅复制非空字符串的参数。我想如果您的源缓冲区有一系列连续的空白字符,可能会发生这种情况。

    *nargs=j;
    args[j]=NULL;
}

在输出数组的末尾放置一个 NULL 并设置nargs输出参数。

于 2012-11-22T07:38:47.950 回答
3

好的,让我们来看看 parse args 中的一些行

wbuf=buffer;
buf_args[0]=buffer; 
args[0] =buffer;

这将wbuf指向buffer. 它还将数组中的第一个指针设置buf_argsbuffer. 它也对第一个元素做同样的事情args

for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
    if ((*cp != '\0') && (++cp >= &buf_args[args_size]))
        break;
}

该函数strsep查找wbuf分隔符空格、换行符或制表符,并返回指向标记开头的指针,并wbuf更新到标记的结尾。如果字符串中没有分隔符,则函数返回NULL. 因此,语句的中间部分for一直持续到*cpequals NULL

所以,指针cp最初指向的字符串(指针)在buf_args[0]. 字符串分隔符函数填充*cp令牌的地址。然后,该if语句检查 1)for循环是否超过了 的容量buf_args,通过检查指针cp是否超过最后一个元素和 2) 返回的标记的第一个字符是否是字符串的结尾字符。

注意:我认为那条线应该是*(*cp) != '\0'

for (j=i=0; buf_args[i]!=NULL; i++){
    if(strlen(buf_args[i])>0)
        args[j++]=buf_args[i];
}

然后,它遍历所有的,buf_args直到找到NULL一个。如果 指向的字符串buf_args长于 0,即它有字符,则数组args获取指向该标记的指针的副本。

*nargs=j;
args[j]=NULL;

填充条目之后的最后一个元素args设置为NULL。并且 的值设置为数组nargs中填充元素数的长度。args

于 2012-11-22T07:45:51.653 回答
2

在这段代码中

for(cp=buf_args; (*cp=strsep(&wbuf, " \n\t")) != NULL ;){
        if ((*cp != '\0') && (++cp >= &buf_args[args_size]))
            break;
 }

检查 wbuf 数组中的“\n\t”分隔符(首先是空格)并将其位置保存在 buf_args 然后保存在

for (j=i=0; buf_args[i]!=NULL; i++){
        if(strlen(buf_args[i])>0)
            args[j++]=buf_args[i];
    }

将参数提取到 args 数组以将结果从函数中提取出来的部分

于 2012-11-22T07:43:57.367 回答