DEV Community

Cover image for Execute Comandos da Shell com Boost.Process
Marcos Oliveira
Marcos Oliveira

Posted on

1

Execute Comandos da Shell com Boost.Process

Mais segurança, portabilidade e controle do que a std::system.


Boost.Process é uma biblioteca C++ da Boost que facilita a criação e gerenciamento de processos externos, permitindo executar programas, capturar saídas e manipular entradas e erros de forma eficiente.

Ela oferece uma interface moderna e flexível para trabalhar com processos, encapsulando detalhes de implementação de sistemas operacionais e fornecendo funcionalidades como execução assíncrona, comunicação entre processos(IPC), controle de saída de streams e gerenciamento de ambiente de execução.

É ideal para aplicações que necessitam interagir com programas externos ou executar comandos do sistema de forma controlada e segura.

A Boost.Process foi criada para oferecer uma alternativa mais segura e flexível ao std::system(), que tem várias limitações.


Comparação com a std::system

1. Mais Controle sobre o Processo

Com boost::process, você pode:

  • Capturar saída e entrada (std::system não permite isso diretamente).
  • Definir variáveis de ambiente.
  • Trabalhar com processos assíncronos (std::system é sempre bloqueante).
  • Especificar diretórios de trabalho.

2. Segurança

O std::system() executa comandos via um shell, o que pode ser perigoso se você estiver passando strings dinâmicas (risco de injeção de comandos).

Exemplo perigoso com std::system():

std::string user_input = "ls && rm -rf /"; // Se for injetado, pode ser catastrófico!
std::system(user_input.c_str());  
Enter fullscreen mode Exit fullscreen mode

Com Boost.Process, você passa argumentos separadamente, evitando problemas de injeção:

boost::process::child c("ls", "-l");
Enter fullscreen mode Exit fullscreen mode

3. Portabilidade

  • std::system() depende do shell do sistema (cmd.exe no Windows, /bin/sh no Linux).
  • boost::process funciona de forma portável, sem depender do shell subjacente.

4. Execução Assíncrona

  • std::system() bloqueia a execução até que o processo termine.
  • Com Boost, você pode rodar processos em paralelo sem travar seu programa:
boost::process::child c("long_task");
c.detach(); // Continua a execução sem esperar
Enter fullscreen mode Exit fullscreen mode

5. Manipulação Direta de Entrada/Saída

Com boost::process, você pode redirecionar entrada/saída facilmente:

boost::process::ipstream out;
boost::process::child c("ls", boost::process::std_out > out);
std::string line;
while (std::getline(out, line)) {
    std::cout << line << std::endl;
}
Enter fullscreen mode Exit fullscreen mode

Com std::system(), você precisaria de pipes manuais, o que é mais trabalhoso.

Quando Usar Cada Um?

✅ Use Boost.Process se precisar de portabilidade, segurança, manipulação de I/O ou execução assíncrona.

⚠️ Use std::system() apenas para comandos simples e rápidos onde segurança e controle não são problemas.


Instalação e Exemplos de Uso

Para instalar a Boost.Process você pode seguir este artigo que nós fizemos que mostra tanto usando gerenciadores de pacotes, como também fazendo o download direto do endereço oficial da LibBoost: Conceito, Instalação e Exemplos de uso da biblioteca Boost

Exemplo básico

Nesse exemplo, mostra como rodar o comando ls usando a Boost.Process:

Exemplo: main.cpp

#include <boost/process.hpp>
#include <iostream>
#include <string>

namespace bp = boost::process;

int main() {
    std::string output;
    bp::ipstream pipe_stream; // Stream para capturar a saída

    // Executa o comando "ls" e redireciona a saída para pipe_stream
    bp::child c("ls", bp::std_out > pipe_stream);

    // Lê a saída do comando linha por linha
    std::string line;
    while (std::getline(pipe_stream, line)) {
        std::cout << line << std::endl;
    }

    c.wait(); // Aguarda o término do processo
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Para compilar não precisa de nenhuma flag adicional, basta compilar normalmente e rodar:

g++ main.cpp
./a.out
Enter fullscreen mode Exit fullscreen mode

Se quiser usar com argumentos, substitua a linha: bp::child por essa abaixo:

//bp::child c("ls", bp::std_out > pipe_stream);
bp::child c("ls", "-l", "*.md", bp::std_out > pipe_stream);
Enter fullscreen mode Exit fullscreen mode

Em Shells que faz o uso de glob pode ser que você tenha um problema ao utilizar curingas, então use a bp::child assim:

//bp::child c("ls", bp::std_out > pipe_stream);
bp::child c("/bin/sh", "-c", "ls -l *.md", bp::std_out > pipe_stream);
Enter fullscreen mode Exit fullscreen mode

Isso lista com detalhes todos os arquivos com extensão Markdown no diretório que você executar o binário.


Outro exemplo

Esse exemplo exibe a versão do GNU GCC, caso você possua instalado no seu sistema, mas com um tratamento de falha mais adequado para o uso.

#include <boost/process.hpp>
#include <iostream>
#include <stdexcept>

using namespace boost::process;

int main() {
    ipstream pipe_stream;
    std::string line;

    try {
        child c("/usr/bin/gcc", args = { "--version" }, std_out > pipe_stream);

        while (pipe_stream && std::getline(pipe_stream, line)) {
            std::cerr << line << std::endl;
        }

        c.wait();

        // Verifica se o processo foi bem sucedido
        if (c.exit_code() != 0) {
            throw std::runtime_error("Process failed with error code: " + std::to_string(c.exit_code()));
        }
    } catch (const std::exception& e) {
        std::cerr << "Failed to execute command: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}
Enter fullscreen mode Exit fullscreen mode

O ideal é informar o caminho completo do programa: /usr/bin/gcc em vez de somente: gcc, tente alterar e coloque só o comando do programa e veja que dará erro.


Limitações

Assim como qualquer outra lib de subprocesso, alguns poucos comandos não funcionarão como esperado, pois pode ser um comando EMBUTIDO da Shell, como por exemplo: history.

Nós já abordamos sobre isso nesse artigo: Usando C++ como Shell Script.

Apesar dele pode exibir o histórico, se você tentar limpar a sessão isso, com certeza, não vai funcionar. Isso acontece porque ele não é um executável separado. Quando você executa history -c em um subprocesso, ele está em um novo shell que não compartilha o histórico da sua sessão principal.

Esse código abaixo não resulta em uma ação esperada:

#include <boost/process.hpp>
#include <iostream>

namespace bp = boost::process;

int main() {
    std::string output;
    bp::ipstream pipe_stream;

    bp::child c("/bin/bash", "-c", "history -c && history -w", bp::std_out > pipe_stream);

    std::string line;
    while (std::getline(pipe_stream, line)) {
        std::cout << line << std::endl;
    }

    c.wait();
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Caso ainda queira executar o comando, o código está correto(pode melhorar com tratamento de falhas como vimos), mas a saída não mostrará nada relevante, pois history -c não gera saída e afeta apenas o subprocesso.

O comando history é um recurso embutido da Shell, e não um executável separado. Quando você executa history -c em um subprocesso, ele está em um novo shell que não compartilha o histórico da sua sessão principal.


Para mais informações consulte a documentação: https://www.boost.org/doc/libs/1_84_0/doc/html/process/reference.html.

Image of Datadog

How to Diagram Your Cloud Architecture

Cloud architecture diagrams provide critical visibility into the resources in your environment and how they’re connected. In our latest eBook, AWS Solution Architects Jason Mimick and James Wenzel walk through best practices on how to build effective and professional diagrams.

Download the Free eBook

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

If you found this post useful, please drop a ❤️ or leave a kind comment!

Okay