【问题标题】:I'm trying to execute read in lines from file in C in a shell environment我正在尝试在 shell 环境中从 C 中的文件中执行读取行
【发布时间】:2020-02-05 04:24:28
【问题描述】:

我可以编译代码,将文件作为命令行参数执行,但没有任何反应。交互模式功能正常提示,batchMode功能不提示。

我正在尝试读取一行,然后执行该行。

示例文件

日期

ls -la

光盘

(行与行之间没有间距。我无法在此处获取格式。)

#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>


#define bSize 1000


void driveLoop();
char *userInput(void);
void removeExit(char *original, char *subString); // removes string with substring "exit"
void batchMode(char *c);


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

  char *fTemp;
  if (argc == 1)
    driveLoop(); // calls the loop function that accepts input and executes commands.

  else if (argc == 2)
    batchMode(&argv[1][0]);

  return 0;

}


void driveLoop(void){
  char *comTokens[100];
  char *tempTokens;
  char *command;
  char *cd;
  char *cdDir;
  char* cdTemp;
  char cdBuf[bSize];
  char checkExit[] = "exit";

  for (;;){


    printf("> ");
    command = userInput(); // reads input


    if (!*command) // allows for empty string error
      break;


    char *exitPtr = strstr(command, "exit"); // returns a value to a pointer if substring is found
    removeExit(command, "exit"); 
    puts(command); // updates the array after the function filter

    int i = 0;
    tempTokens = strtok(command, " \t\n"); // tokens are how the computer recognizes shell commands


    while (tempTokens && i < 99){ // geeksforgeeks.com
      comTokens[i++] = tempTokens;
      tempTokens = strtok(NULL, "\t\n");
    }


    if (strcmp(comTokens[0], "exit") == 0) // exit if input is "exit" only
      exit(0); 




    if(strcmp(comTokens[0], "cd") == 0){ // built in change directory command
      cd = getcwd(cdBuf, sizeof(cdBuf));
      cdDir = strcat(cd, "/");
      cdTemp = strcat(cdDir, comTokens[1]); // cplusplus.com reference
      chdir(cdTemp);
      continue;
    }

    comTokens[i] = NULL;


    pid_t cFork = fork(); // creates duplicate child process of parent


    if (cFork == (pid_t) - 1){ // error check
      perror("fork");
    }


    else if (cFork == 0) { // error codes found on cplusplus.com
      execvp(comTokens[0], comTokens);
      perror("exec");      
    }

    else { // children are returned. parent executes
      int status;
      waitpid(cFork, &status, 0);
      if (exitPtr != NULL){ // if substring exit was found, exit the program
    exit(0);
      }
    }


  }

}


char *userInput(void){  // referenced Linux man page - getline(3) (linux.die.net)
  char *input = NULL;
  size_t size = 0;
  getline(&input, &size, stdin); // updates the size as it goes along
  return input;
}


void removeExit(char *original, char *subString){ // removes exit from string
  char *ex;
  int len = strlen(subString); 
  while ((ex = strstr(original, subString))){ // Referenced from a Stack Overflow page.
    *ex = '\0';
    strcat(original, ex+len);
  }
}


void batchMode(char *c){


  char *tok[100];
  char *batchTokens;
  char *batchBuffer = NULL;
  size_t batchSize = 0;


  FILE *fp = fopen(c, "r");


  unsigned int line = 1;
  char buffer[bSize];


  while(fgets(buffer, sizeof(buffer), fp)){

      int i = 0;


      char *toks = strtok(buffer, "\t\n");


      while (toks && i < 99){
           tok[i] = malloc (strlen(toks) + 1);
           strcpy(tok[i++], toks);
           toks = strtok(NULL, " \t\n");
      }


      tok[i] = NULL;
          pid_t bFork = fork();


      if (bFork == (pid_t) - 1)
          perror("fork");


      else if (bFork == 0){
         execvp(tok[i], tok);
         perror("exec");
      }


      else {
        int status;
        waitpid(bFork, &status, 0);
      }

  }

}

旁注。这是对先前因信息不足而锁定的问题的重新尝试。我已经更新了我的代码并尝试尽可能详细。 我很乐意提供进一步的帮助来回答我的问题。

谢谢大家。

编辑 我放入了一个 fprintf 来验证它是否读取了文件,并且确实如此。

【问题讨论】:

  • tok[i++] = toks; 将相同的指针分配给每个tok[i],因此在循环结束时,每个tok[i] 都指向最后一个toks。您需要分配存储空间。如果你有strdup,那么tok[i++] = strdup(toks);,如果没有,那么tok[i] = malloc (strlen (toks) + 1);(验证)然后strcpy (tok[i++], toks);
  • 没有。只有一个toks。是strtok每次返回的token。您只需要提供存储来保存每个令牌,因为下次您调用toks = strtok(NULL, " \t\n"); 时,toks 将指向一个新地址。所以只需分配存储空间,复制toks 当前指向的内容,然后调用strtok(重复)直到你用完令牌。
  • 好的,我更新了我的帖子和我的程序。既然它仍然什么都不做,那一定意味着我在其他地方也有问题,对吧?
  • 让我看看。
  • 我还添加了一个输入文件示例。它在帖子中并没有完美地显示出来,但它是每行一个命令。行与行之间没有间距。

标签: c shell fork


【解决方案1】:

输入文件中的第一个注释,最后一个命令 cd 不是系统命令,它是内置的 shell,因此您会认为它会失败(除非经过特殊处理)。

strdup(如果可用)或简单地malloc (strlen(toks) + 1); 讨论的每个令牌(tok[i])分配允许您将当前令牌复制到分配的内存块。现在每个tok[i] 都将引用保存的单个令牌(而不是全部指向最后一个令牌——因为都被分配了相同的指针)

batchMode 中最大的逻辑错误是您使用execvp (tok[i], tok); 调用execvp 而不是正确提供文件以作为tok[0] 执行。请注意,如果要执行的文件不在您的 PATH 中,您必须在输入文件中提供绝对路径。

进行更改后,您的batchMode 可以编写如下(并删除所有未使用的变量并在while 循环内移动char *tok[100];,以便在该范围内声明它):

#include <sys/types.h>
#include <sys/wait.h>
...
void batchMode(char *c)
{
    char buffer[bSize];
    FILE *fp = fopen (c, "r");

    if (!fp) {
        perror ("fopen-c");
        return;
    }

    while (fgets (buffer, sizeof(buffer), fp)) {

        int i = 0;
        char *tok[100];
        char *toks = strtok(buffer, " \t\n");

        while (toks && i < 99){
            tok[i] = malloc (strlen(toks) + 1);
            strcpy(tok[i++], toks);
            toks = strtok(NULL, " \t\n");
        }

        tok[i] = NULL;
            pid_t bFork = fork();

        if (bFork == (pid_t) - 1)
            perror("fork");
        else if (bFork == 0){
            execvp (tok[0], tok);
            perror("exec");
        }
        else {
            int status;
            waitpid(bFork, &status, 0);
        }
    }
}

输入文件示例

我测试了两个简单的输入文件:

$ cat dat/toksfile.txt
echo hello
echo goodbye

$ cat dat/toksfile2.txt
date
ls -al dat/toksfile.txt
cd

使用/输出示例

$ ./bin/shellorbatch dat/toksfile.txt
hello
goodbye

$ ./bin/shellorbatch dat/toksfile2.txt
Tue Feb  4 23:47:00 CST 2020
-rw-r--r-- 1 david david 24 Feb  4 23:24 dat/toksfile.txt
exec: No such file or directory

检查一下,如果您有任何问题,请告诉我:

【讨论】:

  • 一个问题,忽略 tok[i] 部分(这是一次未恢复的修复尝试),为什么我的 toks[i++] 部分会在交互模式下工作,而不是批量工作?这让我很困惑。
  • 我很确定您通过指出创建 getline 来分配存储并使输入更容易为我清除了它。 C太简陋了。编写代码肯定很有趣,但我将不得不回去重新学习指针的基础知识。
  • 请记住,指针只是一个普通变量,它保存其他东西的地址作为它的值。换句话说,一个指针指向可以找到其他东西的地址。您通常会想到一个保存立即值的变量,例如int a = 5;,而指针只会保存5 存储在内存中的地址。 (例如int *b; b = &amp;a;)所以现在我们说b points 指向a(因为ba 的地址作为其值)。
  • 至于为什么driveLoop(void) 有效——你是幸运多于好。它也应该更新为分配和复制。基本问题依然存在。用comTokens[i++] = tempTokens;comTokens 数组中的每个指针分配相同的指针。它“似乎可以工作”的事实是经典的 Undefined Behavior ——它也可能是段错误,因为tempTokens 持有的最后一个地址是NULL
猜你喜欢
  • 1970-01-01
  • 2023-03-24
  • 2019-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多