当前位置: 首页 > 工具软件 > Parse CLI > 使用案例 >

Linux c语言实现简单的CLI(command line interpreter)

林英锐
2023-12-01

这是标准答案.
在这个CLI中没有对信号进行任何处理。

#include "wish.h"
#include <ctype.h>  // isspace
#include <regex.h>  // regcomp, regexec, regfree
#include <stdio.h>  // fopen, fclose, fileno, getline, feof
#include <stdlib.h> // exit
#include <sys/types.h>
#include <sys/wait.h> // waitpid

FILE *in = NULL;
char *paths[BUFF_SIZE] = {"/bin", NULL};
char *line = NULL;

void clean(void) {
  free(line);
  fclose(in);
}

char *trim(char *s) {
  // trim leading spaces
  while (isspace(*s))
    s++;

  if (*s == '\0')
    return s; // empty string

  // trim trailing spaces
  char *end = s + strlen(s) - 1;
  while (end > s && isspace(*end))
    end--;

  end[1] = '\0';
  return s;
}

void *parseInput(void *arg) {
  char *args[BUFF_SIZE];
  int args_num = 0;
  FILE *output = stdout;
  struct function_args *fun_args = (struct function_args *)arg;
  char *commandLine = fun_args->command;

  char *command = strsep(&commandLine, ">");
  if (command == NULL || *command == '\0') {
    printError();
    return NULL;
  }

  command = trim(command);

  if (commandLine != NULL) {
    // contain white space in the middle or ">"
    regex_t reg;
    if (regcomp(&reg, "\\S\\s+\\S", REG_CFLAGS) != 0) {
      printError();
      regfree(&reg);
      return NULL;
    }
    if (regexec(&reg, commandLine, 0, NULL, 0) == 0 ||
        strstr(commandLine, ">") != NULL) {
      printError();
      regfree(&reg);
      return NULL;
    }

    regfree(&reg);

    if ((output = fopen(trim(commandLine), "w")) == NULL) {
      printError();
      return NULL;
    }
  }

  char **ap = args;
  while ((*ap = strsep(&command, " \t")) != NULL)
    if (**ap != '\0') {
      *ap = trim(*ap);
      ap++;
      if (++args_num >= BUFF_SIZE)
        break;
    }

  if (args_num > 0)
    executeCommands(args, args_num, output);

  return NULL;
}

int searchPath(char path[], char *firstArg) {
  // search executable file in path
  int i = 0;
  while (paths[i] != NULL) {
    snprintf(path, BUFF_SIZE, "%s/%s", paths[i], firstArg);
    if (access(path, X_OK) == 0)
      return 0;
    i++;
  }
  return -1;
}

void redirect(FILE *out) {
  int outFileno;
  if ((outFileno = fileno(out)) == -1) {
    printError();
    return;
  }

  if (outFileno != STDOUT_FILENO) {
    // redirect output
    if (dup2(outFileno, STDOUT_FILENO) == -1) {
      printError();
      return;
    }
    if (dup2(outFileno, STDERR_FILENO) == -1) {
      printError();
      return;
    }
    fclose(out);
  }
}

void executeCommands(char *args[], int args_num, FILE *out) {
  // check built-in commands first
  if (strcmp(args[0], "exit") == 0) {
    if (args_num > 1)
      printError();
    else {
      atexit(clean);
      exit(EXIT_SUCCESS);
    }
  } else if (strcmp(args[0], "cd") == 0) {
    if (args_num == 1 || args_num > 2)
      printError();
    else if (chdir(args[1]) == -1)
      printError();
  } else if (strcmp(args[0], "path") == 0) {
    size_t i = 0;
    paths[0] = NULL;
    for (; i < args_num - 1; i++)
      paths[i] = strdup(args[i + 1]);

    paths[i + 1] = NULL;
  } else {
    // not built-in commands
    char path[BUFF_SIZE];
    if (searchPath(path, args[0]) == 0) {
      pid_t pid = fork();
      if (pid == -1)
        printError();
      else if (pid == 0) {
        // child process
        redirect(out);

        if (execv(path, args) == -1)
          printError();
      } else
        waitpid(pid, NULL, 0); // parent process waits child
    } else
      printError(); // not fond in path
  }
}

int main(int argc, char *argv[]) {
  int mode = INTERACTIVE_MODE;
  in = stdin;
  size_t linecap = 0;
  ssize_t nread;

  if (argc > 1) {
    mode = BATCH_MODE;
    if (argc > 2 || (in = fopen(argv[1], "r")) == NULL) {
      printError();
      exit(EXIT_FAILURE);
    }
  }

  while (1) {
    if (mode == INTERACTIVE_MODE)
      printf("wish> ");

    if ((nread = getline(&line, &linecap, in)) > 0) {
      char *command;
      int commands_num = 0;
      struct function_args args[BUFF_SIZE];

      // remove newline character
      if (line[nread - 1] == '\n')
        line[nread - 1] = '\0';

      char *temp = line;

      while ((command = strsep(&temp, "&")) != NULL)
        if (command[0] != '\0') {
          args[commands_num++].command = strdup(command);
          if (commands_num >= BUFF_SIZE)
            break;
        }

      for (size_t i = 0; i < commands_num; i++)
        if (pthread_create(&args[i].thread, NULL, &parseInput, &args[i]) != 0)
          printError();

      for (size_t i = 0; i < commands_num; i++) {
        if (pthread_join(args[i].thread, NULL) != 0)
          printError();
        if (args[i].command != NULL)
          free(args[i].command);
      }
    } else if (feof(in) != 0) {
      atexit(clean);
      exit(EXIT_SUCCESS); // EOF
    }
  }

  return 0;
}

my solution

#include "wish.h"
#include <ctype.h>  // isspace
#include <regex.h>  // regcomp, regexec, regfree
#include <stdio.h>  // fopen, fclose, fileno, getline, feof
#include <stdlib.h> // exit
#include <sys/types.h>
#include <sys/wait.h> // waitpid

#define MAX_COMMAND 20
int parse(char *buf);
void parallel();

char *buf;
size_t size;
char *command[MAX_COMMAND], *path[MAX_COMMAND];
int num_command, num_path;
char *argv[20];
int flag = 0;
void printError()
{
  char error_message[30] = "An error has occurred\n";
  write(STDERR_FILENO, error_message, strlen(error_message));
}

int parse(char *buf)
{
  char *copy = strdup(buf);
  char *demi = strdup(" ");
  int cnt = 0;
  while (copy)
  {
    argv[cnt] = strsep(&copy, demi);
    if (argv[cnt] == NULL)
      break;
    ++cnt;
  }
  argv[cnt] = NULL;
  return cnt;
}
void call_cd(int argc, char *argv[])
{
  if (argc != 2)
  {
    printError();
    return;
  }
  int res = chdir(argv[1]);
  if (res)
    return;
  char *curpath = (char *)malloc(BUFF_SIZE * 4);
  char *tmp = getcwd(curpath, BUFF_SIZE);
  if (tmp == NULL)
  {
    printError();
    return;
  }
  strcat(curpath, argv[1]);
  res = chdir(curpath);
  if (res)
    return;
  printError();
  return;
}
void change_path(int argc, char *argv[])
{
  for (int i = 1; i < argc; i++)
    path[i - 1] = argv[i];
  return;
}
void call_exit(int argc, char *argv[])
{
  if (argc != 1)
  {
    printError();
    return;
  }
  exit(0);
  return;
}
int searchPath(char *path, char *firstArg)
{
  char *new_file = strdup(path);
  new_file = strcat(new_file, firstArg);
  if (access(new_file, X_OK) == 0)
    return 1;
  return 0;
}
void batch_mode(char *argv[])
{
  FILE *fin = fopen(argv[0], "r");
  while (getline(&buf, &size, fin) != EOF)
  {
    if (size == 0)
      exit(0);
    for (int i = 0;; i++)
      if (buf[i] == '\n')
      {
        buf[i] = '\0';
        break;
      }
    parallel();
    memset(buf, 0, sizeof(buf));
  }
}
void do_command(int argc, char *argv[])
{
  if (strcmp(argv[0], "cd") == 0)
  {
    call_cd(argc, argv);
    return;
  }
  if (strcmp(argv[0], "path") == 0)
  {
    change_path(argc, argv);
    return;
  }
  if (strcmp(argv[0], "exit") == 0)
  {
    call_exit(argc, argv);
    return;
  }
  if (access(argv[0], R_OK) == 0)
  {
    batch_mode(argv);
    exit(0);
  }

  for (int i = 0; i < num_path; i++)
  {
    if (searchPath(path[i], argv[0]) == 1)
    {
      pid_t pid;
      pid = fork();
      switch (pid)
      {
      case -1:
      {
        printError();
        break;
      }
      case 0:
      {
        char *new_path = strdup(path[i]);
        new_path = strcat(path[i], argv[0]);
        execv(new_path, argv);
        printError();
        exit(1);
      }

      default:
      {
        int status;
        waitpid(pid, &status, 0);      // 等待子进程返回
        int err = WEXITSTATUS(status); // 读取子进程的返回码
        if (err)
        {
          printf("Error: %s\n", strerror(err));
        }
        break;
      }
      }
      return;
    }
  }
  printError();
  return;
}
void parallel()
{
  num_command = 0;
  memset(command, '\0', sizeof(command));
  char *copy = strdup(buf);
  char *demi = strdup("&");
  while (1)
  {
    command[num_command] = strsep(&copy, demi);
    if (command[num_command] == NULL)
      break;
    ++num_command;
  }
  int argc = 0;
  for (int i = 0; i < num_command; i++)
  {
    argc = parse(command[i]);
    do_command(argc, argv);
  }
  wait(NULL);
  return;
}
char *getinput(char *buf, FILE *fin)
{
  size = 0;
  getline(&buf, &size, fin);
  return buf;
}

int main(int argc, char *argv[])
{
  // YOUR CODE HERE
  buf = (char *)malloc(BUFF_SIZE * sizeof(char));
  path[1] = strdup("/bin/");
  path[0] = strdup("usr/bin/");
  num_path = 2;
  while (1)
  {
    if (!flag)
      printf("wish> ");
    buf = getinput(buf, stdin);
    for (int i = 0;; i++)
      if (buf[i] == '\n')
      {
        buf[i] = '\0';
        break;
      }
    if (size == 0)
      continue;
    parallel();
    memset(buf, 0, sizeof(buf));
  }
  return 0;
}

 类似资料: