2014-02-20 27 views
0

我想讓這個shell解析。如何讓程序以某種方式實現解析,以便引用的命令將根據開始和結束引號進行解析,並將其視爲一個令牌?在我打印出令牌的第二個while循環期間,我想我需要提供某種if語句,但我不太確定。任何意見/建議,不勝感激。如何讓這個shell在C語句中用引號分析它們?

#include <stdio.h>    //printf 
#include <unistd.h>    //isatty 
#include <string.h>    //strlen,sizeof,strtok 

int main(int argc, char **argv[]){ 

    int MaxLength = 1024;   //size of buffer 
    int inloop = 1;    //loop runs forever while 1 
    char buffer[MaxLength];  //buffer 
    bzero(buffer,sizeof(buffer)); //zeros out the buffer 
    char *command;    //character pointer of strings 
    char *token;     //tokens 
    const char s[] = "-,+,|, "; 

    /* part 1 isatty */ 
    if (isatty(0)) 
    { 

     while(inloop ==1)    // check if the standard input is from terminal 
     { 
      printf("$"); 
      command = fgets(buffer,sizeof(buffer),stdin); //fgets(string of char pointer,size of,input from where 
      token = strtok(command,s); 

      while (token !=NULL){ 

       printf(" %s\n",token); 

       token = strtok(NULL, s);  //checks for elements  
      } 


      if(strcmp(command,"exit\n")==0) 
       inloop =0; 

     }  

    } 
    else 
     printf("the standard input is NOT from a terminal\n"); 

    return 0; 
} 
+1

我不會使用'strtok',而是親自手動編寫解析。查看現有免費軟件shell的源代碼,例如'sash'或'bash' –

+0

如果您仍然需要使用'strtok()',則可以通過在*** ***字符上進行標記來完成。 – ryyker

回答

1

對於任意的命令行語法,strtok是不是最好的功能。它適用於簡單的情況,其中的單詞由特殊字符或空格分隔,但會有一段時間,您想要將這種類似ls>out的內容分成三個標記。 strtok無法處理此問題,因爲它需要將其終止零置於某處。

這裏有一個快速和骯髒的自定義命令行解析器:

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

int error(const char *msg) 
{ 
    printf("Error: %s\n", msg); 
    return -1; 
} 

int token(const char *begin, const char *end) 
{ 
    printf("'%.*s'\n", end - begin, begin); 
    return 1; 
} 

int parse(const char *cmd) 
{ 
    const char *p = cmd; 
    int count = 0; 

    for (;;) { 
     while (isspace(*p)) p++; 
     if (*p == '\0') break; 

     if (*p == '"' || *p == '\'') { 
      int quote = *p++; 
      const char *begin = p; 

      while (*p && *p != quote) p++; 
      if (*p == '\0') return error("Unmachted quote"); 
      count += token(begin, p); 
      p++; 
      continue; 
     } 

     if (strchr("<>()|", *p)) { 
      count += token(p, p + 1); 
      p++; 
      continue; 
     } 

     if (isalnum(*p)) { 
      const char *begin = p; 

      while (isalnum(*p)) p++; 
      count += token(begin, p); 
      continue; 
     } 

     return error("Illegal character"); 
    } 

    return count; 
} 

此代碼理解由空格分隔的單詞,用單或者雙引號和單字符運算符分開的話。它不理解引號內的轉義引號和非字母數字字符,如單詞中的點。

該代碼不難理解,您可以輕鬆地將其擴展爲理解雙字符運算符,如>>或註釋。

如果您想要使用引號,則必須識別parse中的轉義字符,並忽略它以及token中的其他轉義序列。

0

關於你提到的具體要求:即是在引號命令將根據起始和結束的報價進行解析。

可以使用strtok()通過令牌化的字下面是如何:

char a[]={"\"this is a set\" this is not"}; 
char *buf; 
buf = strtok(a, "\""); 

在該代碼片段,buf將包含」這是一組「

注意th e使用\允許將字符用作令牌分隔符。

此外,不是你的主要問題,但你需要:

更改此:

const char s[] = "-,+,|, "; //strtok will parse on -,+| and a " " (space) 

要:

const char s[] = "-+| "; //strtok will parse on only -+| and a " " (space) 

strtok()會解析出任何你必須在分隔字符串,其中包括「」

1

首先,您聲明argv是指向...指針的數組。實際上,這是一系列指向char的指針。所以:

int main(int argc, char **argv){ 

的趨勢是要達到[],它把你變成不正確的代碼在這裏,但在C/C++的成語是更普遍使用指針語法,如:

const char* s = "-+| "; 

FWIW。 另外,請注意fgets()在文件結束時會返回NULL(例如,用戶在* nix上鍵入CTRL-D或在DOS/Windows上鍵入CTRL-Z)。發生這種情況時,您可能不希望發生分段違規。

此外,bzero()是非便攜功能(你可能在這方面不關心),如果你問它的C編譯器會很樂意初始化數組以零爲你(可能是值得我們在乎;語法如下所示) 。

接下來,只要您允許帶引號的字符串,立即出現的下一個語言問題是:「我如何引用報價?」。然後,您立即離開可以用strtok()乾淨地處理的領土。我不是100%確定你想如何將你的字符串分解爲令牌。以你所使用的方式使用strtok(),我認爲字符串「a | b」會產生兩個令牌,「a」和「b」,讓你忽略「|」。你正在對待「|」和「 - 」和「+」就像空白一樣,被忽略,這通常不是殼體的功能。例如,鑑於此命令行:

echo 'This isn''t so hard' | cp -n foo.h .. >foo.out 

我可能會想要得到令牌的以下列表:

echo 
'This isn''t so hard' 
| 
cp 
-n 
foo.h 
.. 
> 
foo.out 

通常情況下,像「+」和字符「 - 」不是特供大多數shell的標記化過程(不同於'|'和'&'和'<'等,它們是shell生成的命令永遠不會看到的指令)。他們被傳遞到應用程序,然後可以自由決定「' - '表明這個詞是一個選項,而不是文件名」或其他。接下來是你的代碼的一個版本,它產生我所描述的輸出(可能或不可能完全是你想要的),並且允許使用雙引號或單引號的參數(也可以擴展來處理備份)可以包含相同類型的引號等。

#include <stdio.h>    //printf 
#include <unistd.h>    //isatty 
#include <string.h>    //strlen,sizeof,strtok 

#define MAXLENGTH 1024 

int main(int argc, char **argv[]){ 

    int inloop = 1;    //loop runs forever while 1 
    char buffer[MAXLENGTH] = {'\0'};  //compiler inits entire array to NUL bytes 
// bzero(buffer,sizeof(buffer)); //zeros out the buffer 
    char *command;    //character pointer of strings 
    char *token;     //tokens 
    char* rover; 
    const char* StopChars = "|&<> "; 
    size_t toklen; 

    /* part 1 isatty */ 
    if (isatty(0)) 
    { 

     while(inloop ==1)    // check if the standard input is from terminal 
     { 
      printf("$"); 
      token = command = fgets(buffer,sizeof(buffer),stdin); //fgets(string of char pointer,size of,input from where 
      if(command) 
       while(*token) 
        { 
        // skip leading whitespace 
        while(*token == ' ') 
         ++token; 
        rover = token; 
        // if possible quoted string 
        if(*rover == '\'' || *rover == '\"') 
         { 
         char Quote = *rover++; 
         while(*rover) 
          if(*rover != Quote) 
           ++rover; 
          else if(rover[1] == Quote) 
           rover += 2; 
          else 
           { 
           ++rover; 
           break; 
           } 
         } 
        // else if special-meaning character token 
        else if(strchr(StopChars, *rover)) 
         ++rover; 
        // else generic token 
        else 
         while(*rover) 
          if(strchr(StopChars, *rover)) 
           break; 
          else 
           ++rover; 
        toklen = (size_t)(rover-token); 
        if(toklen) 
         printf(" %*.*s\n", toklen, toklen, token); 
        token = rover; 
        } 
      if(strcmp(command,"exit\n")==0) 
       inloop =0; 
     }  

    } 
    else 
     printf("the standard input is NOT from a terminal\n"); 

    return 0; 
} 
+0

+1爲你的努力。非常完整的例子。 – ryyker

相關問題