【问题标题】:How to execute a shell script from C in Linux?如何在 Linux 中从 C 执行 shell 脚本?
【发布时间】:2011-04-13 18:24:39
【问题描述】:

如何在 Linux 中从 C 语言执行 shell 脚本?

【问题讨论】:

    标签: c linux shell


    【解决方案1】:

    这取决于您要对脚本(或您要运行的任何其他程序)做什么。

    如果你只想运行脚本system 是最简单的事情,但它也做一些其他的事情,包括运行一个shell 并让它运行命令(大多数*nix 下的/bin/sh)。

    如果您想通过标准输入提供 shell 脚本或使用其标准输出,您可以使用popen(和pclose)来设置管道。这也使用 shell(大多数 *nix 下的 /bin/sh)来运行命令。

    这两个都是库函数,在底层做了很多工作,但如果它们不能满足您的需求(或者您只是想试验和学习),您也可以直接使用系统调用。这也可以让您避免让 shell (/bin/sh) 为您运行命令。

    感兴趣的系统调用是forkexecvewaitpid。您可能想要使用 execve 周围的库包装器之一(键入 man 3 exec 以获取它们的列表)。您可能还想使用其他等待功能之一(man 2 wait 拥有它们)。另外你可能对与fork相关的系统调用clonevfork感兴趣。

    fork 复制了当前程序,唯一的主要区别是新进程从 fork 调用中返回 0。父进程获取返回的新进程的进程 ID(或错误)。

    execve 用新程序替换当前程序(保持相同的进程 ID)。

    waitpid 被父进程用来等待特定子进程完成。

    将 fork 和 execve 步骤分开允许程序在创建新进程之前对其进行一些设置(不会弄乱自己)。其中包括将标准输入、输出和 stderr 更改为与使用的父进程不同的文件、更改进程的用户或组、关闭子进程不需要的文件、更改会话或更改环境变量。

    您可能还对pipedup2 系统调用感兴趣。 pipe 创建一个管道(具有输入和输出文件描述符)。 dup2 将文件描述符复制为特定的文件描述符(dup 类似,但将文件描述符复制到最低的可用文件描述符)。

    【讨论】:

    • 另外值得注意的是,fork() 非常便宜,因为内存是写时复制的,并且不会在程序分叉时立即复制。
    • 我是 fork/exec 的忠实粉丝——它可以让你避免基于环境的不确定性来决定运行什么。但是你可以直接在 shell 脚本上使用它吗?正如您所说,通过使用 exec,您“避免让 shell (/bin/sh) 为您运行命令”;因此,您不需要 exec /bin/sh 来运行 shell 脚本吗?
    • @Tom Anderson:如果 shell 脚本为有效用户设置了执行权限,并且有一个适当的 shabang 第一行列出了一个文件,有效用户也有权执行并且本身也不是脚本某种类型的内核将使用脚本文件调用 shabang 行中列出的文件。如果脚本的shabang 是#!/bin/csh,那么/bin/sh 将不会在fork/exec 场景中运行。如果是 #!/bin/sh,那么它会在 fork/exec 场景中,但对于 system 版本,/bin/sh 将为此脚本运行两次。
    【解决方案2】:

    你可以使用system:

    system("/usr/local/bin/foo.sh");
    

    这将在使用sh -c 执行时阻塞,然后返回状态码。

    【讨论】:

    • ... 假设它具有可执行位和 shebang 行。一般来说,system("/bin/sh /usr/local/bin/foo.sh");。是的,这实际上调用了两次 shell。
    • 嗨,马特。这很有用。但是,我看到它正在阻塞。如何使其异步?
    【解决方案3】:

    如果你对 POSIX 没问题,你也可以使用popen()/pclose()

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void) {
    /* ls -al | grep '^d' */
      FILE *pp;
      pp = popen("ls -al", "r");
      if (pp != NULL) {
        while (1) {
          char *line;
          char buf[1000];
          line = fgets(buf, sizeof buf, pp);
          if (line == NULL) break;
          if (line[0] == 'd') printf("%s", line); /* line includes '\n' */
        }
        pclose(pp);
      }
      return 0;
    }
    

    【讨论】:

      【解决方案4】:

      一个简单的方法是.....

      #include <stdio.h>
      #include <stdlib.h>
      
      
      #define SHELLSCRIPT "\
      #/bin/bash \n\
      echo \"hello\" \n\
      echo \"how are you\" \n\
      echo \"today\" \n\
      "
      /*Also you can write using char array without using MACRO*/
      /*You can do split it with many strings finally concatenate 
        and send to the system(concatenated_string); */
      
      int main()
      {
          puts("Will execute sh with the following script :");
          puts(SHELLSCRIPT);
          puts("Starting now:");
          system(SHELLSCRIPT);    //it will run the script inside the c code. 
          return 0;
      }
      

      说谢谢
      尤达@http://www.unix.com/programming/216190-putting-bash-script-c-program.html

      【讨论】:

        【解决方案5】:

        如果你需要更精细的控制,也可以走forkpipeexec路线。这将允许您的应用程序检索从 shell 脚本输出的数据。

        【讨论】:

          【解决方案6】:

          我更喜欢 fork + execlp 作为 doron 提到的“更精细”的控制。 示例代码如下所示。

          将您的命令存储在 char 数组参数中,并为结果存储 malloc 空间。

          int fd[2];
          pipe(fd);
          if ( (childpid = fork() ) == -1){
             fprintf(stderr, "FORK failed");
             return 1;
          } else if( childpid == 0) {
             close(1);
             dup2(fd[1], 1);
             close(fd[0]);
             execlp("/bin/sh","/bin/sh","-c",parameters,NULL);
          }
          wait(NULL);
          read(fd[0], result, RESULT_SIZE);
          printf("%s\n",result);
          

          【讨论】:

          • close(1); 不需要,因为 dup2() 在重新分配之前会自行关闭 newfd。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-03-26
          • 2013-05-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-01-05
          相关资源
          最近更新 更多