2010-04-16 4 views
5

J'écris un programme en C++ qui font un traitement spécial pour tous les fichiers dans le répertoire courant sous Linux OS.Comment rediriger la sortie d'un appel système à l'intérieur du programme en C/C++?

Alors je pensais à l'aide du système tels que les appels system("ls") pour obtenir la liste de tous les fichiers.

mais comment le stocker puis à l'intérieur de mon programme? (Comment rediriger la sortie de ls disons une chaîne que je déclarais dans le programme)

Merci

Répondre

7

Je vous suggère de n'appelez pas à ls - faire le travail correctement, en utilisant opendir/readdir/closedir lire directement le répertoire.

exemple de code, impression des entrées du répertoire:

// $ gcc *.c && ./a.out 
#include <stdlib.h> // NULL 
#include <stdio.h> 
#include <dirent.h> 

int main(int argc, char* argv[]) { 
    const char* path = argc <= 1 ? "." : argv[1]; 

    DIR* d = opendir(path); 
    if (d == NULL) return EXIT_FAILURE; 

    for(struct dirent *de = NULL; (de = readdir(d)) != NULL;) 
    printf("%s/%s\n", path, de->d_name); 

    closedir(d); 
    return 0; 
} 
+0

@ J.F. Sebastian - Merci. –

+2

Sachez que vous ne devriez pas utiliser 'readdir' dans un programme multithread. Au lieu de cela, vous devriez utiliser 'readdir_r' –

10

Je ne pense pas que vous pouvez utiliser le système pour lire la sortie.

Essayez d'utiliser popen.

#include <stdio.h> 

    FILE *popen(const char *command, const char *type); 

    int pclose(FILE *stream); 

Mais, vous ne voulez probablement pas le faire pour ls. Deux raisons:

  • Si vous souhaitez obtenir une liste de répertoires utiliser le système de fichiers api directement (readdir), au lieu d'utiliser des commandes shell et l'analyser.
  • Quelqu'un m'a fait un blog post expliquant récemment pourquoi l'analyse syntaxique ls la sortie est une mauvaise idée.
+1

Je pense que ce serait un bon ajout pour expliquer pourquoi cela ne fonctionnerait pas pour' ls'. –

+0

@RaphaelSP: J'avais vu une autre réponse décrivant de ne pas utiliser ls, mais je voulais répondre à la question système vs popen. Point pris cependant, donc j'ai ajouté une meilleure justification. – Stephen

+0

Wow, vous l'avez bien fait :-). +2 si je pouvais ... –

0

Êtes-vous sous UNIX ou Windows? Sous UNIX, vous créez un processus enfant avec l'appel fork(), puis créez des canaux et affectez le STDOUT du processus enfant au STDIN du processus parent. Sous Windows, il y a forcément quelque chose de similaire dans l'API Win32.

+0

J'ai fait un exemple plus élaboré pour l'environnement unix/posix ici: https://blog.uxul.de/e?e=mt-160 – schoppenhauer

0

Je vois 3 options:

  1. Est-ce system("ls > tempfile") puis lisez la sortie de ls de tempfile
  2. Ouvrir un tuyau à l'aide pipe(), puis utilisez fork() pour frayer un nouveau processus. Dans le processus fils, fermez le descripteur de fichier 2 et remplacez-le par l'écriture de fin de canal en appelant le dup(). Ensuite, appelez exec("ls",...) dans le processus enfant. Enfin, lisez la sortie de ls à partir du canal dans le processus parent
  3. N'utilisez pas ls pour l'énumération de fichiers dans le répertoire en cours. Il y a des fonctions pour le faire correctement dans votre programme, c'est-à-dire opendir(),readdir(),closedir.

Je recommande fortement l'option 3.

0

Soit utiliser le approiate c appelle à lire les fichiers dans le répertoire ou vous généralisent votre programme donc il faut une liste de fichiers en entrée et prendre cette liste de ls canalisé à votre programme

> ls | yoursoftware 
4

Une façon simple de le faire est d'utiliser le répertoire iterator boost filesystem.Ou utilisez simplement opendir/readdir/closedir comme d'autres l'ont suggéré. Il n'y a pas de réel avantage à la façon dont vous vous dirigez.

Exemple de code

, formats d'impression pour les fichiers réguliers dans un répertoire spécifié:

// $ g++ listdir.cc -o listdir -lboost_filesystem && ./listdir 
#include <iostream> 
#include <boost/filesystem.hpp> 

int main(int argc, char* argv[]) { 
    using std::cout; 
    using namespace boost::filesystem; 

    std::string path(argc <= 1 ? "." : argv[1]); 

    if (is_directory(path)) { 
    for (directory_iterator itr(path); itr!=directory_iterator(); ++itr) { 
     cout << itr->path().filename() << ' '; // display filename only 
     if (is_regular_file(itr->status())) // display size for regular files 
     cout << " [" << file_size(itr->path()) << ']'; 
     cout << '\n'; 
    } 
    } 
    else cout << (exists(path) ? "Found: " : "Not found: ") << path << '\n'; 
} 
1

Option 4. Utilisez popen();

13

Le consensus semble être de ne pas utiliser "ls". Cependant, pour toute personne qui est intéressée par une fonction pour le faire:

/** 
* Execute a command and get the result. 
* 
* @param cmd - The system command to run. 
* @return The string command line output of the command. 
*/ 
string GetStdoutFromCommand(string cmd) { 

    string data; 
    FILE * stream; 
    const int max_buffer = 256; 
    char buffer[max_buffer]; 
    cmd.append(" 2>&1"); // Do we want STDERR? 

    stream = popen(cmd.c_str(), "r"); 
    if (stream) { 
     while (!feof(stream)) 
      if (fgets(buffer, max_buffer, stream) != NULL) data.append(buffer); 
     pclose(stream); 
    } 
    return data; 
} 
0

Je pense # 2 de pajton est la réponse la plus appropriée à la question. Il peut être "ls" n'est pas le meilleur exemple d'une commande pour laquelle vous voudriez l'utiliser puisqu'il existe d'autres fonctions de bibliothèque C natives disponibles pour ce faire, mais il existe d'autres programmes qui génèrent des sorties que vous pouvez vouloir traiter immédiatement sans avoir à écrire/lire dans un fichier.

# 1 est également bon sur un système de type UNIX/Linux qui implémente un système de fichiers RAM efficace qui peut être utilisé pour lire/écrire des données globales du système. Sur presque tous les systèmes, c'est un moyen très rapide de faire le travail. Encore une fois, la plupart des réponses offrent de meilleurs moyens d'obtenir le contenu d'un répertoire en utilisant des bibliothèques C natives, mais il existe des instances où des fonctions telles que pipe(), fork(), dup(), exec(), système() et popen() sont appropriés pour communiquer avec les processus du système.

0

L'utilisation des appels système fork()/exec() et pipe() semble être la meilleure méthode générale sous Unix. Cela permet la séparation la plus facile de la sortie standard et des flux d'erreurs standard du programme appelé. On peut aussi attendre le processus engendré et le récolter, en récupérant son statut de sortie. Enfin, on peut utiliser des E/S non bloquantes pour lire à partir de la sortie/erreur du programme invoqué, si besoin est.

Le plus gros problème est que pour obtenir quelque chose de similaire sur Windows, vous devrez peut-être écrire plusieurs centaines de lignes de code. Je n'ai pas trouvé de meilleur moyen et n'ai pas étudié le problème dans le contexte Windows.

http://support.microsoft.com/kb/190351

+0

Et le appel système dup() pour rediriger la sortie dans l'enfant à l'extrémité d'écriture des tuyaux. – CppNoob

+0

Les différences sur Windows sont mineures. – CppNoob

+2

Sous Windows, créez deux canaux anonymes héritables à l'aide de _CreatePipe_ - un pour lire la sortie standard et un pour stderr de la commande que vous appelez. Appelez la commande en créant un processus enfant à l'aide de _CreateProcess_ et transmettez les handles de fin d'écriture des canaux à ce processus fils dans la structure STARTUPINFO. Utilisez _ReadFile_ sur les poignées de lecture des canaux de votre programme pour lire les tubes stdout et stderr. Enfin, utilisez WaitForSingleObject pour attendre la fin du processus fils et appelez CloseHandle sur son handle de processus. – CppNoob

Questions connexes