实现一个简单的shell

时间:2014-02-22 20:15:19

标签: c string shell fork execvp

创建一个简单的shell,使用fork和execvp函数从stdin行运行命令。

但是,ls之类的内容有效,但不是ls -all -S。 它将执行ls,但不会为ls -all

打印任何内容

我能想到的唯一想法就是命令中的某个地方有一个“\ n”,但我不知道怎么把它拿出来,甚至不知道它在哪里.......

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
//Libs ^^^^ Defs vvvvvvvv
#define comlen 4096                     //Max command length
#define comarg 32             //Max argument length

int main(int argc, char *argv[]) 
{
  char buff; //command buffer
    char* comand[comlen];
    int i;
  do
  {
        i = 0;
    printf("simsh: ");

        char* whtspc = strtok (fgets(&buff, comlen, stdin)," ");  //get input and tokenize
        printf("[%lu]   ::   %s------------\nEND OF BUFF TEST\n", strlen(&buff), &buff);

    while (whtspc != NULL)
    {
          comand[i]=(char*)malloc((sizeof(char)*strlen(whtspc))); //alloctie mem for commands
          strncpy(comand[i], whtspc, strlen(whtspc)-1);                     //coppy comand token to array index i
          whtspc = strtok (NULL, " ");                                                      //grab next token
            i++;                                                                                                        //incriment
            /*trying to change new line character to NULL so that commands can be passed properly*/
//          if (comand[strlen(comand[i]) - 1] == "\n") 
//          {
//              comand[strlen(comand[i]) - 1] = '\0';
//          }
            //breka out incase index oversteps
            if (i == 4096)
                break;
    }
        //last entry in command should be null
        comand[i] = NULL;
        //fork to run in background
        pid_t pid = fork();

        if (pid == 0)
        {
            //testing and pass comands to execvp
            printf("START OF COMAND TEST\n!!!!!!!!!%s!!!!!!!!!!!!!!!!\n %lu\nEND OF COMAND TEST\n\n",comand[1], strlen(comand[0]));
            execvp(comand[0], &comand);
        }

        else
        {
            //parent wait on child.
            waitpid(pid, &i, WUNTRACED | WCONTINUED);
        }
    }
    while(1);

  return 0;
}

欢迎任何帮助。

如果它有帮助,这里是代码::

的终端输出
simsh: ls
[3]   ::   ls
------------
END OF BUFF TEST
START OF COMAND TEST
!!!!!!!!!(null)!!!!!!!!!!!!!!!!
 2
END OF COMAND TEST

chop_line.c  chop_line.h  list.c  list.h  Makefile  Makefile~  One  simsh1  simsh1.c  simsh1.c~
simsh: ls -all
[2]   ::   ls------------
END OF BUFF TEST
START OF COMAND TEST
!!!!!!!!!-all!!!!!!!!!!!!!!!!
 1
END OF COMAND TEST

simsh: echo
[5]   ::   echo
------------
END OF BUFF TEST
START OF COMAND TEST
!!!!!!!!!(null)!!!!!!!!!!!!!!!!
 4
END OF COMAND TEST


simsh: echo all
[4]   ::   echo------------
END OF BUFF TEST
START OF COMAND TEST
!!!!!!!!!all!!!!!!!!!!!!!!!!
 3
END OF COMAND TEST

simsh: echo echo
[4]   ::   echo------------
END OF BUFF TEST
START OF COMAND TEST
!!!!!!!!!echo!!!!!!!!!!!!!!!!
 3
END OF COMAND TEST

2 个答案:

答案 0 :(得分:1)

fgets的第一个参数应该是指向将字符串复制到的缓冲区的指针。您正在将指针传递给单个char

其次,execvp需要两个参数:一个文件名和一个以空值终止的命令行参数列表,按照惯例,它以文件名本身开头。

我冒昧地对你的代码做了一些修改,既解决了我上面提到的问题,也使它更具可读性。

请注意下面的代码中存在内存泄漏(修复它:)。可能还有其他问题我没有注意到。

我刚刚实现了一个shell;如果你想看一下,我的GitHub网址就在我的个人资料中(请注意:丑陋的大学作业代码)。

希望它有所帮助!

#include <stdio.h>                                                                                      
#include <stdlib.h>                                                                                     
#include <string.h>                                                                                     
#include <unistd.h>                                                                                     
#include <time.h>
#include <sys/types.h>                                                                                  
#include <sys/wait.h>                                                                                   

#define COMLEN 4096                                                                                     
#define COMARG_N 32                                                                                     
#define TRUE 1

int main(int argc, char *argv[]) 
{                                                                                                       
    char *token;                                                                                        
    char *args[COMARG_N];                                                                               
    char *buff;                                                                                         
    int i;                                                                                              
    pid_t pid;                                                                                          

    while(TRUE) {                                                                                       
        printf("simsh: ");                                                                            

        buff = (char*) malloc(sizeof(char) * COMLEN);                                                 
        fgets(buff, COMLEN, stdin);                                                                   

        if (buff[strlen(buff) - 1] == '\n')                                                           
            buff[strlen(buff) - 1] = '\0';                                                            

        i = 0;                                                                                        
        token = strtok (buff, " ");                                                                   

        while (token != NULL && i < COMARG_N - 1) {                                                   
            args[i] = token;                                                                          
            token = strtok (NULL, " ");                                                               
            i++;                                                                                      
        }                                                                                             

        args[i] = NULL;                                                                               

        pid = fork();                                                                                 
        if (pid == 0)                                                                                 
            execvp(args[0], &args[0]);                                                                
        else                                                                                          
            waitpid(pid, &i, WUNTRACED | WCONTINUED);                                                 

        free(buff);                                                                                   
    }                                                                                                 

    return 0;                                                                                             
}   

答案 1 :(得分:0)

首先,您的fgets正在阅读字符buff。你应该读入一个字符缓冲区。其次,fgets将换行符保留在读取字符串的末尾,因此您可能希望先将其删除,例如:

char buff[4096];
if (!fgets(buff, sizeof(buff), stdin)) {
    // error or EOF
    return 1;
}
int len = strlen(buff);
if (len > 0 && buff[len-1] == '\n') {
    buff[--len] = '\0';
}
char *whtspc = strtok(buff, " ");

您还必须将所有对&buff的引用替换为buff

除此之外,您的malloc也是错误的,并且分配了一个小于所需的字符(strlen没有终止NUL):

if (!(comand[i] = malloc(strlen(whtspc)+1))) {
    return 1; // out of memory
}
(void) strcpy(comand[i], whtspc);

相应地,您的strncpy正在复制少于一个字符的字符。这使得您的原始代码意外地用于单字输入,因为它在这种情况下具有为您移除尾随'\n'的效果,但在其他每种情况下它都删除了单词本身的最后一个字符。

execvp的第二个参数应该只是comand(sic)数组:

execvp(comand[0], comand);