2016-03-19 33 views
3

Bir kabuk yazıyor ve boruları ve IO'ları uygulamak zorundayım. Tam programı yazdım ve yönlendirmeler çalışıyor. Bir komutun çıktısını bir dosyaya yazabilirim ve bir dosyadan bir komut için girdiyi okuyabilirim. Boruları uygulamakta gerçekten çok zorlanıyorum.Küçük kabukta boruların uygulanması

/bin/ls | /usr/bin/sort gibi bir komutu alabilmem gerekiyor. Bu, /bin/ls çıktısını almalı ve /usr/bin/sort'a girdi olarak sağlamalıdır. Yukarıdaki komutu çalıştığınızda alıyorum bu:

ls: |: No such file or directory 
/usr/bin/sort 

Daha önce belirtildiği gibi, tek tek bu görevi başarmak mümkün.

Kodum:

/* 
* eval - Evaluate the command line that the user has just typed in 
* 
* If the user has requested a built-in command (quit, jobs, bg or fg) 
* then execute it immediately. Otherwise, fork a child process and 
* run the job in the context of the child. If the job is running in 
* the foreground, wait for it to terminate and then return. Note: 
* each child process must have a unique process group ID so that our 
* background children don't receive SIGINT (SIGTSTP) from the kernel 
* when we type ctrl-c (ctrl-z) at the keyboard. 
*/ 

void eval(char *cmdline) { 
    int bg;        // Background or foreground 
    struct cmdline_tokens cmd_tokens; // Command line tokens 
    pid_t pid;       // Processing ID 
    int fd;        // File discriptor 
    int oldfd;       // Temp file discriptor 
    struct job_t *pjob;     // Job pointer 
    sigset_t sigchld_mask;    // SIGCHLD mask 
    sigset_t sigint_mask;    // SIGINT mask 
    sigset_t sigtstp_mask;    // SIGTSTP mask 

    /* Initializing Masks */ 
    if (sigemptyset(&sigchld_mask)) { 
     unix_error("main: sigemptyset error"); 
     exit(1); 
    } 
    if (sigaddset(&sigchld_mask, SIGCHLD)) { 
     unix_error("main: sigaddset error"); 
     exit(1); 
    } 
    if (sigemptyset(&sigint_mask)) { 
     unix_error("main: sigemptyset error"); 
     exit(1); 
    } 
    if (sigaddset(&sigint_mask, SIGINT)) { 
     unix_error("main: sigaddset error"); 
     exit(1); 
    } 
    if (sigemptyset(&sigtstp_mask)) { 
     unix_error("main: sigemptyset error"); 
     exit(1); 
    } 
    if (sigaddset(&sigtstp_mask, SIGTSTP)) { 
     unix_error("main: sigaddset error"); 
     exit(1); 
    } 

    bg = parseline(cmdline, &cmd_tokens) + 1; 
    if (bg == 0 || cmd_tokens.argv[0] == NULL) { 
    // If line is empty, i.e., there are no arguments 
     return; 
    } 
    if (!builtin_cmd(cmd_tokens)) { 
     if (addjob(jobs, 1, bg, cmdline)) { 
     // If job list is not full 
     // TODO: find tutorial link and add here 
      if (sigprocmask(SIG_BLOCK, &sigchld_mask, NULL)) { 
       unix_error("eval: sigprocmask error"); 
       exit(1); 
      } 
      if (sigprocmask(SIG_BLOCK, &sigint_mask, NULL)) { 
       unix_error("eval: sigprocmask error"); 
       exit(1); 
      } 
      if (sigprocmask(SIG_BLOCK, &sigtstp_mask, NULL)) { 
       unix_error("eval: sigprocmask error"); 
       exit(1); 
      } 

      if ((pid = fork()) == 0) { 
       if (sigprocmask(SIG_UNBLOCK, &sigchld_mask, NULL)) { 
        unix_error("eval: sigprocmask error"); 
        exit(1); 
       } 

       if (sigprocmask(SIG_UNBLOCK, &sigint_mask, NULL)) { 
        unix_error("eval: sigprocmask error"); 
        exit(1); 
       } 

       if (sigprocmask(SIG_UNBLOCK, &sigtstp_mask, NULL)) { 
        unix_error("eval: sigprocmask error"); 
        exit(1); 
       } 

       if (setpgid(0, 0)) { // Set group ID 
        unix_error("eval: setpgid error"); 
        exit(1); 
       } 
       oldfd = 0; 
       if (cmd_tokens.infile) { 
        fd = open(cmd_tokens.infile, O_RDONLY, 0); 
        dup2(fd, STDIN_FILENO); 
       } 

       if (cmd_tokens.outfile) { 
        oldfd = open(cmd_tokens.outfile, O_RDONLY, 0); 
        dup2(STDOUT_FILENO, oldfd); 
        fd = open(cmd_tokens.outfile, O_WRONLY, 0); 
        dup2(fd, STDOUT_FILENO); 
       } 

       if (execve(cmd_tokens.argv[0], cmd_tokens.argv, environ) < 0) { 
        if (oldfd) { 
         dup2(oldfd, STDOUT_FILENO); 
        } 
        printf("%s: Command not found\n", cmd_tokens.argv[0]); 
        exit(0); 
       } 
      } 

      if (nextjid == 1) { 
       pjob = getjobjid(jobs, MAXJOBS); 
      } else { 
       pjob = getjobjid(jobs, nextjid - 1); 
      } 

      pjob->pid = pid; 
      if (sigprocmask(SIG_UNBLOCK, &sigint_mask, NULL)) { 
       unix_error("eval: sigprocmask error"); 
       exit(1); 
      } 
      if (sigprocmask(SIG_UNBLOCK, &sigtstp_mask, NULL)) { 
       unix_error("eval: sigprocmask error"); 
       exit(1); 
      } 
      if (bg == FG) {  // Foreground job 
       waitfg(pid); 
      } else {   // Background job 
       printf("[%d] (%d) %s\n", pjob->jid, pjob->pid, pjob->cmdline); 
      } 
      if (sigprocmask(SIG_UNBLOCK, &sigchld_mask, NULL)) { 
       unix_error("eval: sigprocmask error"); 
       exit(1); 
      } 
     } else { 
      printf("eval: addjob error\n"); 
     } 
    } 
    return; 
} 

/* 
* parseline - Parse the command line and build the argv array. 
* 
* Parameters: 
* cmdline: The command line, in the form: 
* 
*    command [arguments...] [< infile] [> oufile] [&] 
* 
* cmd_tokens:  Pointer to a cmdline_tokens structure. The elements of this 
*    structure will be populated with the parsed tokens. Characters 
*    enclosed in single or double quotes are treated as a single 
*    argument. 
* Returns: 
* 1:  if the user has requested a BG job 
* 0:  if the user has requested a FG job 
* -1:  if cmdline is incorrectly formatted 
* 
* Note:  The string elements of cmd_tokens (e.g., argv[], infile, outfile) 
*    are statically allocated inside parseline() and will be 
*    overwritten the next time this function is invoked. 
*/ 
int parseline(const char *cmdline, struct cmdline_tokens *cmd_tokens) { 
    // TODO: find tutorial link and add here 
    /* Changing parseline to parse struct cmd_tokens */ 
    static char array[MAXLINE];   /* holds local copy of command line */ 
    const char delims[10] = " \t\r\n"; /* argument delimiters (white-space) */ 
    char *buf = array;     /* ptr that traverses command line */ 
    char *next;       /* ptr to the end of the current arg */ 
    char *endbuf;      /* ptr to end of cmdline string */ 
    int is_bg;       /* background job? */ 

    int parsing_state;     /* indicates if the next token is the 
              input or output file */ 

    if (cmdline == NULL) { 
     (void) fprintf(stderr, "Error: command line is NULL\n"); 
     return -1; 
    } 

    (void) strncpy(buf, cmdline, MAXLINE); 
    endbuf = buf + strlen(buf); 

    cmd_tokens->infile = NULL; 
    cmd_tokens->outfile = NULL; 

    /* Build the argv list */ 
    parsing_state = ST_NORMAL; 
    cmd_tokens->argc = 0; 

    while (buf < endbuf) { 
     /* Skip the white-spaces */ 
     buf += strspn (buf, delims); 
     if (buf >= endbuf) break; 

     /* Check for I/O redirection specifiers */ 
     if (*buf == '<') { 
      if (cmd_tokens->infile) { 
       (void) fprintf(stderr, "Error: Ambiguous I/O redirection\n"); 
       return -1; 
      } 
      parsing_state |= ST_INFILE; 
      buf++; 
      continue; 
     } 
     if (*buf == '>') { 
      if (cmd_tokens->outfile) { 
       (void) fprintf(stderr, "Error: Ambiguous I/O redirection\n"); 
       return -1; 
      } 
      parsing_state |= ST_OUTFILE; 
      buf ++; 
      continue; 
     } 

     if (*buf == '\'' || *buf == '\"') { 
      /* Detect quoted tokens */ 
      buf++; 
      next = strchr (buf, *(buf-1)); 
     } else { 
      /* Find next delimiter */ 
      next = buf + strcspn (buf, delims); 
     } 

     if (next == NULL) { 
      /* Returned by strchr(); this means that the closing 
       quote was not found. */ 
      (void) fprintf (stderr, "Error: unmatched %c.\n", *(buf-1)); 
      return -1; 
     } 

     /* Terminate the token */ 
     *next = '\0'; 

     /* Record the token as either the next argument or the i/o file */ 
     switch (parsing_state) { 
      case ST_NORMAL: 
       cmd_tokens->argv[cmd_tokens->argc++] = buf; 
       break; 
      case ST_INFILE: 
       cmd_tokens->infile = buf; 
       break; 
      case ST_OUTFILE: 
       cmd_tokens->outfile = buf; 
       break; 
      default: 
       (void) fprintf(stderr, "Error: Ambiguous I/O redirection\n"); 
       return -1; 
     } 
     parsing_state = ST_NORMAL; 

     /* Check if argv is full */ 
     if (cmd_tokens->argc >= MAXARGS-1) break; 

     buf = next + 1; 
    } 

    if (parsing_state != ST_NORMAL) { 
     (void) fprintf(stderr, 
       "Error: must provide file name for redirection\n"); 
     return -1; 
    } 

    /* The argument list must end with a NULL pointer */ 
    cmd_tokens->argv[cmd_tokens->argc] = NULL; 

    if (cmd_tokens->argc == 0) /* ignore blank line */ 
     return 1; 

    if (!strcmp(cmd_tokens->argv[0], "quit")) {     /* quit command */ 
     cmd_tokens->builtins = BUILTIN_QUIT; 
    } else if (!strcmp(cmd_tokens->argv[0], "jobs")) {   /* jobs command */ 
     cmd_tokens->builtins = BUILTIN_JOBS; 
    } else if (!strcmp(cmd_tokens->argv[0], "bg")) {   /* bg command */ 
     cmd_tokens->builtins = BUILTIN_BG; 
    } else if (!strcmp(cmd_tokens->argv[0], "fg")) {   /* fg command */ 
     cmd_tokens->builtins = BUILTIN_FG; 
    } else { 
     cmd_tokens->builtins = BUILTIN_NONE; 
    } 

    /* Should the job run in the background? */ 
    if ((is_bg = (*cmd_tokens->argv[cmd_tokens->argc-1] == '&')) != 0) 
     cmd_tokens->argv[--cmd_tokens->argc] = NULL; 

    return is_bg; 
} 

ben kod örneği gerçekten büyük, ama tüm fonksiyonlar birbirine bağlanır ve çözüm bulmaktan bir yardımcı olabileceğini biliyoruz. Ayrıca kodu azaltmak için yardımcı fonksiyonlardan oluşan bir kupon bıraktım. Şu ana kadar parseline ve eval işlevlerine bakıyorum. eval() işlevi, yönlendirmelerin yapıldığı yer olduğu için, buradaki boruyu uygulamaya çalışıyorum.

Bazı insanlar daha önce bu yüzden yaptım kod azaltmak için söylemişti ama bunu gerekirse, bu benim koduna ham dosyasıdır: https://cdn.rawgit.com/sukritchhabra/sc3349-cs283-winter2016/master/G4/tsh.c

+1

(yeniden) bir MCVE oluşturma hakkında okuyunuz ([MCVE]) . Kodunuzu derleyemiyoruz, örneğin, 'struct cmdline_tokens' tanımına sahip değiliz. Gösterdiğiniz hata mesajlarından söylemek zor, ama '' 'sembolünü' ls' olarak komut adı olarak geçiyormuşsunuz gibi görünüyor ve böyle bir dosyayı bulamıyor. “Echo” Merhaba ”> '| |' 'ı deneyebilir ve ardından kabuğunuzu yeniden çalıştırabilir ve farklı bir sonuç alıp alamayacağınızı görebilirsiniz. Ve belki de ls -l |/usr/bin/sort' de. –

+0

Bunu daha önce yaptım, ama insanlar kodun çok büyük olduğunu söylediler, bu yüzden onu düşürdüm ve yeniden yayınladım –

+0

Oluşturduğunuz yapıların ayrıntılarını dökmek için fonksiyonlara sahip olmalısınız. ve bir komut satırı ayrıştırma bitti ve belki de ayrıştırma yapıyorsunuz. Bu tür hata ayıklama işlevleri çok değerli olabilir. Unix_error() 'işlevinizi değiştirir, böylece çıkar veya unix_error()' i çağırıp yeni bir işlev oluşturur ve çıkar. Hata işleme için kod miktarını azaltırdı. Ancak, en çok test ettiğiniz (sisteminizin hepsini kontrol etmedim) sistem çağrıları iyidir. –

cevap

0

bir cevap içine benim yorum bölümlerini aktarılması.

tam kod erişilebilir Önce: Ben indirilen tam koduyla, Oluşturduğum etmişti dosyası | için 'doğru' çıktı got yaptım

It looks as if you're passing the | symbol to ls , and it is failing to find such a file. You could try echo "Hello" > '|' and then rerun your shell and see if you get a different result. And maybe experiment with ls -l | /usr/bin/sort , too.

. Tam kod erişilebilir sonra

You should have functions to dump the details of the structures you've created, so that you can show the information unambiguously when you've finished parsing a command line, and perhaps as you are doing the parsing. Such debugging functions can be very valuable.

I'd modify your unix_error() function so that it exits, or create a new function which calls unix_error() and then exits. It would cut down on the amount of code for error handling. You currently have 3 lines after each test where 1 could be sufficient (if you elect to do without braces around a single statement — 2 if you prefer to keep the braces). You could use _Noreturn (or #include <stdnoreturn.h> and noreturn) as a function specifier on unix_error() , too. But it is good that you are testing most (I haven't checked whether it is all) system calls.

:

As predicted, the problem is that you are not separating your commands properly, so you are running /bin/ls with arguments "|" and /usr/bin/sort in normal shell notations, and the ls command can't find the file called | . With that for information, you should be able to find your problem — you are not null terrminating the argument list when you find | .

You need to revise the code so your parser can create multiple commands to be executed. Your current struct cmdline_tokens structure is designed to store the information for one command, but with pipelines, you need to be able to handle multiple commands. When you come across a pipe, you'll complete the current cmdline_tokens structure and start on the next. You may need to dynamically allocate the structures in the long term, but for the time being you can use struct cmdline_tokens cmds[5]; to allow pipelines with up to 5 commands in it.

Belli ki, bileşen doğru komutları çalıştırabilmek için boru hattı kaç komutları takip etmek gerekir.

Bu arada, dikkatli kodlamada iyi bir şey yaptım - sigint_handler()'a assert(sig == SIGINT);'u eklemek zorunda kaldım ve tercih ettiğim seçeneklerimin altında kodun derlenmesini sağlamak için benzer bir sinyal işleyicisi gibi. Birkaç kişi temiz kodlara yakın olsun. Ben derlemek için (bir makefile yoluyla) Bu komutu kullanılır:

gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes \ 
    -Wold-style-definition -Werror tsh.c -o tsh 

ve hata mesajları ben gibi var idi:

sh.c:720:25: error: unused parameter ‘sig’ [-Werror=unused-parameter] 
void sigint_handler(int sig) {