C中嵌套循环strtok_r问题,用';'分隔和''

时间:2015-04-02 02:39:58

标签: c string

我想执行多个命令,例如./shell ls > test.txt;ls > test1.txt;ls > test2.txt;,它们应该在提到的文件中打印输出3次。但是,不知怎的,我的代码只打印一次。

我用';'分隔了char缓冲区使用strtok_r。

另外,我看了另一个与我的问题类似的例子: Nested strtok function problem in C

这是我的代码

void call_system(char *argv[],int argc)
{
    struct sigaction sa;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sa.sa_handler = child_handler;
    sigaction(SIGCHLD, &sa, NULL);

    int pid;
    int background;
    /*two process are created*/
    pid=fork();
    background = 0;

    if(pid<0)
    {
        fprintf(stderr,"unsuccessful fork /n");
        exit(EXIT_SUCCESS);
    }
    else if(pid==0)
    {
        chdir("h/Ravi Griffith/Sem 1-2015/SP/Assignment1/Assignment1");

        char *bname;
        char *path2 = strdup(*argv);
        bname = basename(path2);

        execvp(bname, argv);
    }
    else if(pid>0)
    {
        /*it will wait untill the child process doesn't finish*/
        child_handler(pid);
        exit(EXIT_SUCCESS); 
    }
}

int main(int argc,char *argv[])
{
    if(argc>1)
    {
    /*it will check whether a user has entered exit then the code will be executed successfully.*/
        if(strcmp( argv[1], "exit")==0)
        {
        exit(EXIT_SUCCESS);
        }
    }
    else
    {
        printf("Enter Shell Command -- ");   
        char buffer[80];

        fgets(buffer, sizeof(buffer), stdin);
        //it will replace the newline character with null
        buffer[strlen(buffer) - 1] = '\0';
        char *end_str;
        char* token= strtok_r(buffer, ";", &end_str);

        /* string will be split in individual argument array */
        while(token != NULL)
        {
            int i;
            char *endtoken;
            printf("a = %s\n", token);
            char *array[strlen(buffer)];
            i = 0;
            char *token2 = strtok_r(token," ", &endtoken);  
            while (token2 != NULL)
            {
                array[i++] = token2;
                token2 = strtok_r(NULL, " ", &endtoken);
            }

            if(sizeof(array)>16)
            {
                char *arrow=array[strlen(buffer)-1];
                char *filename;
                filename=array[strlen(buffer)];
                /*printf("%s",arrow);
                printf("%s",filename);*/
                if(strcmp( arrow, ">") == 0)
                {
                    freopen( filename, "w", stdout );
                }
                else if(strcmp( arrow, "<") == 0)
                {
                    freopen( filename, "rb", stdin );
                }
            }   

            splittoarray(buffer,argv);

            call_system(argv,argc);
            token = strtok_r(NULL, ";",&end_str);
        }
    }
}

1 个答案:

答案 0 :(得分:1)

鉴于讨论命令行以strtok_r的嵌套方式拆分为单个参数,我将以下示例放在一起概述其对您的问题的应用。 (我已经省略了,你的具体代码与拆分无关,我已根据需要进行了更改)。原始拆分代码的最大问题是,在声明灵活的指针数组以保存指向strtok_r返回的标记的指针之后,您只需将strtok_r的返回值分配给指针数组,而无需分配内存和制作token/token2指向的字符串的副本。

虽然strtok_r将返回指向每个令牌的指针,但token指向的内存位置可能会在下次调用strtok_r时发生更改。因此,为了保留令牌供以后使用,您需要在下次调用token/token2之前复制strtok_r指向的字符串。

要注意的另一个问题是,在使用;拆分后,当您将单个命令拆分为args时,您不能简单地传递token令牌数组中的字符串({{1修改原始)。您必须复制strtok_r数组中的每个字符串,并将指向该副本的指针传递给token。这样,您可以将起始地址保留到每个已分配的strtok_r字符串,以防止您在将指针传递给token时使您的程序SegFault成为free后面。

这是一个简短的例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc,char *argv[])
{
    if(argc > 1 && strcmp (argv[1], "exit")==0) {
        exit(EXIT_SUCCESS);
    }

    printf("Enter Shell Command -- ");   
    char buffer[80];

    fgets(buffer, sizeof(buffer), stdin);

    /* replace the newline character with null */
    buffer[strlen(buffer) - 1] = '\0';

    char *cmdarray[strlen (buffer)];
    size_t cmdidx = 0;
    size_t n = 0;
    char *end_str = NULL;
    char* token= strtok_r(buffer, ";", &end_str);

    /* initialize command string array */
    for (n = 0 ; n < strlen (buffer); n++)
        cmdarray[n] = NULL;

    /* split into individual command string */
    while (token)
    {
        cmdarray[cmdidx++] = strdup (token);
        token = strtok_r (NULL, ";", &end_str);
    }

    /* loop to process command strings into args */
    for (n = 0; n < cmdidx; n++)
    {
        size_t i = 0;
        size_t cmdsz = strlen (cmdarray[n]);
        size_t arridx = 0;
        char *token2 = NULL;
        char *endtoken = NULL;
        char *array[cmdsz];
        char *cmdcopy = strdup (cmdarray[n]);
        char *sp = cmdcopy;

        /* initialize argument array */
        for (i = 0; i < cmdsz; i++)
            array[i] = NULL;

        /* split each command string into argument array */
        for (token2 = strtok_r (sp, " ", &endtoken); token2; token2 = strtok_r (NULL, " ", &endtoken))
            array[arridx++] = strdup (token2);

        /* main processing of commands and args here
        * (print example of all values tokenized) 
        */
        printf ("\ncmdarray[%zu] : %s\n", n, cmdarray[n]);
        for (i = 0; i < arridx; i++)
            printf ("  array[%zu] : %s\n", i, array[i]);

        /* free memory allocated to array */
        for (i = 0; i < arridx; i++)
            if (array[i])
                free (array[i]);

        /* free copy of command used to tokenize arguments */
        if (cmdcopy)
            free (cmdcopy);
    }

    /* free command memory */
    n = 0;
    while (cmdarray[n]) free (cmdarray[n++]);

    return 0;
}

嵌套命令拆分示例

$ ./bin/strtok_r_nested
Enter Shell Command -- ./shell ls > test.txt;ls > test1.txt;ls > test2.txt

cmdarray[0] : ./shell ls > test.txt
  array[0] : ./shell
  array[1] : ls
  array[2] : >
  array[3] : test.txt

cmdarray[1] : ls > test1.txt
  array[0] : ls
  array[1] : >
  array[2] : test1.txt

cmdarray[2] : ls > test2.txt
  array[0] : ls
  array[1] : >
  array[2] : test2.txt

确认无泄漏

$ valgrind ./bin/strtok_r_nested
==19622== Memcheck, a memory error detector
==19622== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==19622== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==19622== Command: ./bin/strtok_r_nested
==19622==
Enter Shell Command -- ./shell ls > test.txt;ls > test1.txt;ls > test2.txt
  <snip>

==19622==
==19622== HEAP SUMMARY:
==19622==     in use at exit: 0 bytes in 0 blocks
==19622==   total heap usage: 16 allocs, 16 frees, 156 bytes allocated
==19622==
==19622== All heap blocks were freed -- no leaks are possible
==19622==
==19622== For counts of detected and suppressed errors, rerun with: -v
==19622== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)