UP | HOME

Shells
Processes
COP-3402

Command-line interpreter (shell)

  • Executes commands

    ls ./
    

Commands

  • String input
  • Program name and arguments, e.g., ls ./
  • Built-in commands, e.g., cd or exit

Command loop

  • Prompt for another command after execution

    do
      command = read(userinput)
      interpret(command)
    while command != exit
    

Processing commands

Algorithm

  • Break up command into its parts
  • Create new process
  • Execute new command
  • Wait for command to finish

Diagram

  • ls ./
    • Boxes around tokens
    • Fork a new process
    • execvp ls with ./ as args

Command and arguments

  • Tokenize whitespace-delimited inputs
  • (Diagram)

    ls ./ ../
    

In reality, bash and other command-line interpreters process a full programming language that has structured constructs. We will look at parsing when we talk about compilers.

Redirection example

Diagram

$ ls >out.txt

Identify the program and redirections.

Piping and redirection

Diagram

ls / | wc

Identify the program, redirections, and piping.

Using strtok

#include <stdio.h>
#include <inttypes.h>  // intmax_t
#include <string.h>    // strcspn(), strtok
#include <stdlib.h>    // exit()

int main() {
  const int BUFSIZE = 1024;
  char *prog;
  char *arg;
  char *buffer;
  while (1) {
    buffer = malloc(sizeof(char) * BUFSIZE);

    printf("$ ");
    // if you don't flush the buffer, it gets printed out in child as well apparently!

                // read in command with maximum size
                if (!fgets(buffer, BUFSIZE, stdin)) {
      // use ctrl-d to end the input
                        fprintf(stderr, "no more input\n");
                        exit(EXIT_SUCCESS);
                }

                fprintf(stderr, "length: %" PRIdMAX "\n", strlen(buffer));
                fprintf(stderr, "buffer: %s\n", buffer);

    // remove newline
    buffer[strcspn(buffer, "\n")] = '\0';

    if (strlen(buffer) == 0) {
      continue;
    }

    char *prog1;
                char *next_str;
                char *buffercopy = malloc(sizeof(char) * BUFSIZE);
                next_str = strtok(strncpy(buffercopy, buffer, BUFSIZE), " ");
    prog1 = next_str;
    fprintf(stderr, "%s\n", next_str);
                while ((next_str = strtok(NULL, " ")) != NULL) {
      fprintf(stderr, "%s\n", next_str);
                }

    fprintf(stderr, "first program name: %s\n", prog1);
  }
}

Executing programs

What syscalls can we use for this?

fork and exec

execvp.c

#include <stdio.h>
#include <unistd.h>    // fork(), execvp()
#include <stdlib.h>    // exit()
#include <inttypes.h>  // intmax_t

// man 2 execvp

int main(int argc, char **argv) {
  char *prog = "ls";
  const int MAXARGV = 32 + 1 + 1;  // including the NULL terminator and the progname
  char **newargv = malloc(sizeof(char *) * (MAXARGV));
  newargv[0] = prog;
  newargv[1] = "./";
  newargv[2] = "../";
  newargv[3] = NULL;  // NULL-terminate the argv list
  execvp(prog, newargv);
}

Processing pipes

Algorithm

  • Tokenize command by whitespace
  • Identify the following
    • Left and right (if present) pipes
    • Arguments (if present) for both programs
    • In/out redirections (if present) for both programs
  • For each pipe program component
    • Create new process
    • Redirect I/O (if present)
      • Redirect to file for redirect commands if present
      • Redirect to/from pipe if present
    • Execute new commands
  • Wait for all commands to finish

Diagram

  • ls ./ | wc -l
    • Boxes around tokens
    • Create pipe
    • Fork new processes
    • Redirect i/o
    • execvp ls with ./ as args for each
    • Wait

Author: Paul Gazzillo

Created: 2025-02-21 Fri 13:36

Validate