2010-04-16 16 views
5

Estoy escribiendo un programa en C++ que hace un tratamiento especial para todos los archivos en el directorio actual en el sistema operativo Linux.¿Cómo redirigir la salida de una llamada al sistema al interior del programa en C/C++?

Estaba pensando en utilizar llamadas al sistema como system("ls") para obtener la lista de todos los archivos.

pero cómo almacenarlo luego dentro de mi programa? (Cómo redirigir la salida de ls a digamos una cadena que he declarado en el programa)

Gracias

Respuesta

7

le sugiero que no llama a ls - hacer el trabajo correctamente, utilizando opendir/readdir/closedir para leer directamente El directorio.

código de ejemplo, las entradas de directorio de impresión:

// $ 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 - Gracias. –

+2

Tenga en cuenta que no debe usar 'readdir' en un programa multiproceso. En su lugar, debería usar 'readdir_r' –

10

no creo que el sistema se puede utilizar para leer la salida.

Intente utilizar popen.

#include <stdio.h> 

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

    int pclose(FILE *stream); 

embargo, es probable que no quieren hacer esto por ls. Dos razones:

  • Si desea obtener un listado de directorio utilizan la API del sistema de archivos directamente (readdir), en lugar de utilizar los comandos de shell y analizarlo.
  • Alguien me señaló un blog post explicando recientemente por qué el análisis ls de salida es una mala idea.
+1

. Creo que sería una buena adición explicar por qué eso no funcionaría para' ls'. –

+0

@RaphaelSP: He visto otra respuesta que describe no usar ls, pero quería responder a la pregunta del sistema frente a popen. Sin embargo, me tomó un punto, así que he agregado una mejor justificación. – Stephen

+0

Guau, lo has hecho bien :-). +2 si pudiera ... –

0

¿Está en UNIX o Windows? En UNIX crearía un proceso secundario con la llamada fork(), y luego crearía algunos pipes y asignaría el STDOUT del proceso secundario al STDIN del proceso principal. En Windows, seguramente habrá algo similar en la API de Win32.

+0

He hecho un ejemplo más elaborado para el entorno de Unix/Posix aquí: https://blog.uxul.de/e?e=mt-160 – schoppenhauer

0

veo 3 opciones:

  1. Haz system("ls > tempfile") y luego leer la salida de ls de tempfile
  2. Abrir una tubería usando pipe(), a continuación, utilizar fork() a generar un nuevo proceso. En el proceso hijo, cierre el descriptor de archivo 2 y sustituya el final de la escritura por él llamando al dup(). Luego, llame al exec("ls",...) en el proceso hijo. Finalmente lea la salida de ls de la tubería en el proceso principal
  3. No use ls para enumerar archivos en el directorio actual. Hay funciones para hacerlo bien en su programa, es decir, opendir(),readdir(),closedir.

recomiendo encarecidamente la opción 3.

0

utilizar el approiate C llama a leer los archivos en el directorio o generalizar el programa de modo que se necesita una lista de archivos como entrada y tomar esa lista de ls hilo a su programa

> ls | yoursoftware 
4

Una forma sencilla de hacer esto es utilizar el iterador boost filesystem directorio.O simplemente use opendir/readdir/closedir como otros han sugerido. No hay una ventaja real en la forma en que te diriges.

código de ejemplo, tamaños de impresión para los archivos normales en un directorio especificado:

// $ 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

Opción 4. Uso popen();

13

El consenso parece ser no usar "ls". Sin embargo, para cualquier persona que esté interesada en una función para hacer esto:

/** 
* 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

yo creo # 2 de pajton es la respuesta más adecuada a la pregunta.

Puede ser "ls" no es el mejor ejemplo de un comando para el que desea usar esto ya que hay otras funciones nativas de biblioteca C disponibles para hacer esto, pero hay otros programas que generan resultados que puede desea procesar inmediatamente sin tener que realizar una escritura/lectura en el archivo.

# 1 también es bueno en un sistema de tipo UNIX/Linux que implementa un eficiente sistema de archivos RAM que se puede utilizar para leer/escribir datos globales del sistema. En casi todos los sistemas, es una muy buena manera "rápida" y "sucia" de hacer el trabajo.

De nuevo, la mayoría de las respuestas ofrecen mejores formas de obtener los contenidos de un directorio utilizando bibliotecas C nativas, pero hay casos en que funciones como pipe(), fork(), dup(), exec(), sistema() y popen() son apropiados para comunicarse con los procesos del sistema.

0

El uso de las llamadas al sistema fork()/exec() y pipe() parece ser el mejor método general en Unix. Esto permite una separación más fácil de la salida estándar y las secuencias de error estándar del programa invocado. También se puede esperar en el proceso generado y cosecharlo, recopilando su estado de salida. Finalmente, uno puede usar E/S sin bloqueo para leer de la salida/error del programa invocado, si es necesario.

El mayor problema es que para lograr algo similar en Windows, posiblemente necesite escribir varios cientos de líneas de código. No encontré una mejor manera y no he estudiado el problema en el contexto de Windows.

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

+0

Y el La llamada al sistema dup() redirige la salida en el niño al extremo de escritura de las tuberías. – CppNoob

+0

Las diferencias en Windows son solo menores. – CppNoob

+2

En Windows, cree dos canalizaciones anónimas heredables usando _CreatePipe_ - una para leer el stdout y otra para stderr del comando que invocaría. Invoque el comando creando un proceso secundario usando _CreateProcess_ y pase los identificadores de extremo de escritura de las canalizaciones a este proceso secundario en la estructura STARTUPINFO. Use _ReadFile_ en los manejadores de extremo de lectura de las tuberías en su programa para leer desde los tubos stdout y stderr. Finalmente, use WaitForSingleObject para esperar que el proceso hijo salga y llame a CloseHandle en su identificador de proceso. – CppNoob

Cuestiones relacionadas