web-dev-qa-db-fra.com

Comment exécuter un script Shell à partir de C sous Linux?

Comment puis-je exécuter un script Shell à partir de C sous Linux?

49
Jan Deinhard

Cela dépend de ce que vous voulez faire avec le script (ou tout autre programme que vous souhaitez exécuter).

Si vous voulez simplement exécuter le script system est la chose la plus simple à faire, mais il fait aussi d'autres choses, y compris exécuter un Shell et le faire exécuter la commande (/ bin/sh sous la plupart des * nix).

Si vous souhaitez alimenter le script Shell via son entrée standard ou consommer sa sortie standard, vous pouvez utiliser popen (et pclose) pour configurer un canal. Cela utilise également le shell (/ bin/sh sous la plupart des * nix) pour exécuter la commande.

Les deux sont des fonctions de bibliothèque qui font beaucoup de choses sous le capot, mais si elles ne répondent pas à vos besoins (ou si vous voulez simplement expérimenter et apprendre), vous pouvez également utiliser directement les appels système. Cela vous permet également d'éviter que le shell (/ bin/sh) n'exécute votre commande pour vous.

Les appels système d'intérêt sont fork, execve et waitpid. Vous souhaiterez peut-être utiliser l'un des wrappers de bibliothèque autour de execve (tapez man 3 exec pour une liste). Vous pouvez également utiliser l'une des autres fonctions d'attente (man 2 wait les a tous). De plus, vous pouvez être intéressé par les appels système clone et vfork qui sont liés à fork.

fork duplique le programme actuel, où la seule différence principale est que le nouveau processus obtient 0 retourné de l'appel à fork. Le processus parent obtient l'ID de processus du nouveau processus (ou une erreur) retourné.

execve remplace le programme actuel par un nouveau programme (en gardant le même identifiant de processus).

waitpid est utilisé par un processus parent pour attendre la fin d'un processus enfant particulier.

La séparation des étapes fork et execve permet aux programmes d'effectuer une configuration pour le nouveau processus avant sa création (sans se gâcher). Il s'agit notamment de modifier l'entrée, la sortie et stderr standard pour qu'ils soient différents de ceux du processus parent utilisé, de changer l'utilisateur ou le groupe du processus, de fermer les fichiers dont l'enfant n'aura pas besoin, de changer la session ou de changer les variables d'environnement.

Vous pouvez également être intéressé par les pipe et dup2 appels système. pipe crée un tube (avec un descripteur de fichier d'entrée et de sortie). dup2 duplique un descripteur de fichier en tant que descripteur de fichier spécifique (dup est similaire mais duplique un descripteur de fichier au descripteur de fichier le plus bas disponible).

48
nategoose

Vous pouvez utiliser system:

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

Cela se bloquera lors de son exécution à l'aide de sh -c, puis renvoyez le code d'état.

25
Matthew Flaschen

Si vous êtes d'accord avec POSIX, vous pouvez également utiliser 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;
}
16
pmg

Un moyen simple est .....

#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;
}

Dites merci à
Yoda @ http://www.unix.com/programming/216190-putting-bash-script-c-program.html

3
Ganesh

Je préfère fork + execlp pour un contrôle "plus fin" comme Doron l'a mentionné. Exemple de code illustré ci-dessous.

Stockez votre commande dans un tableau de paramètres char et un espace malloc pour le résultat.

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);
1
GABIKA6

Si vous avez besoin d'un contrôle plus précis, vous pouvez également suivre la route forkpipeexec. Cela permettra à votre application de récupérer les données issues du script Shell.

1
doron