Как выполнить команду и получить её вывод в C++ (POSIX)?
Использование popen() для захвата вывода команды
Самый простой и эффективный способ получить вывод внешней команды в C++ в среде POSIX — это использовать функцию popen(). Она создаёт канал (pipe), запускает указанную команду и позволяет читать её стандартный вывод (stdout) как обычный файловый поток. В отличие от system(), вы получаете полный контроль над выводом.
Функция popen() принимает два аргумента: команду (строку) и режим ("r" для чтения, "w" для записи). Она возвращает указатель на FILE*, который можно читать с помощью fgets(), fread() или C++-совместимых обёрток. После завершения работы нужно закрыть поток функцией pclose().
Пример: получение вывода команды в строку
Вот полный пример на C++17:
#include <iostream>
#include <memory>
#include <cstdio>
#include <string>
#include <array>
std::string exec(const char* cmd) {
std::array<char, 128> buffer;
std::string result;
// Запускаем команду и открываем поток для чтения
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
// Читаем вывод построчно
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
return result;
}
int main() {
try {
std::string output = exec("ls -la");
std::cout << "Вывод команды:\n" << output << std::endl;
} catch (const std::exception& e) {
std::cerr << "Ошибка: " << e.what() << std::endl;
return 1;
}
return 0;
}
Объяснение кода
- popen(cmd, "r") — запускает команду в подоболочке (/bin/sh -c cmd) и подключает её stdout к нашему потоку.
- std::unique_ptr — RAII-обёртка для автоматического закрытия потока через
pclose()при выходе из области видимости. Это защищает от утечек ресурсов. - fgets() — читает строку из потока. Мы используем буфер на 128 байт; если строка длиннее, она будет прочитана за несколько итераций.
- Обработка ошибок: если
popen()возвращает nullptr (например, не хватило памяти или команда не найдена), выбрасывается исключение.
Важные замечания
- Безопасность:
popen()использует оболочку /bin/sh. Если команда содержит пользовательский ввод, экранируйте её (например, черезexecvp()с аргументами, но это сложнее). - Код возврата:
pclose()возвращает статус завершения команды (аналогичноsystem()). Если нужно проверить успешность, сохраните результатpclose(). - Производительность: Для больших объёмов данных используйте чтение блоками (
fread()) вместо построчного. - Альтернативы: В C++17 можно использовать
std::filesystem::read_symlink()для простых случаев, но для произвольных командpopen()— стандарт.
Этот подход работает на всех POSIX-системах (Linux, macOS, BSD) и является наиболее простым для 90% задач по запуску команд из C++.
<<Более низкоуровневый подход: fork(), exec() и pipe()
Если вам нужен полный контроль над процессом (например, захват stderr или передача данных в stdin), используйте комбинацию pipe(), fork() и exec(). Это сложнее, но даёт гибкость.
Вот минимальный пример для захвата stdout:
#include <unistd.h>
#include <sys/wait.h>
#include <cstring>
#include <iostream>
std::string exec_fork(const char* cmd) {
int pipefd[2];
if (pipe(pipefd) == -1) return "";
pid_t pid = fork();
if (pid == 0) { // Дочерний процесс
close(pipefd[0]); // Закрываем чтение
dup2(pipefd[1], STDOUT_FILENO); // Перенаправляем stdout в pipe
close(pipefd[1]);
execl("/bin/sh", "sh", "-c", cmd, nullptr);
_exit(1); // Если exec() не удался
} else if (pid > 0) { // Родительский процесс
close(pipefd[1]); // Закрываем запись
char buffer[128];
std::string result;
ssize_t count;
while ((count = read(pipefd[0], buffer, sizeof(buffer)-1)) > 0) {
buffer[count] = '\0';
result += buffer;
}
close(pipefd[0]);
waitpid(pid, nullptr, 0); // Ждём завершения
return result;
}
return "";
}
Плюсы: можно захватывать stderr (через второй pipe), передавать данные в stdin, управлять сигналами. Минусы: больше кода, нужно обрабатывать ошибки системных вызовов, риск зомби-процессов при неправильной обработке. Для большинства задач popen() из первого ответа — лучший выбор.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги
Алгоритмы и структуры данных на JavaScript: полное руководство для Node.js