Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run embedded bash script as resource from c++ executable in Linux

Tags:

c++

linux

bash

The following example C++ project has two files: hello_world.sh and hello_world.cpp.

I want to embed the "hello_world.sh" as resource file when building the project with cmake, after building the project, an executable called "helloworld" is generated.

Here is src/hello_world.sh

#!/bin/bash
echo "Hello World in hello_world.sh"

Here is src/hello_world.cpp

#include <iostream>

int main()
{
    std::cout << "Hello World in hello_world.cpp" << std::endl;
    
    /** How to call hello_world.sh in the main() function? */
    
    /** Dummy Code **/
    
    // Use /bin/bash to run the hello_world.sh 
    // which is embedded into the "helloworld" executable when building with cmake
    
    /bin/bash /path/to/resource_inside_the_helloworld_executable/hello_world.sh
}

Build the project, and embed the "hello_world.sh" into the generated executable file "helloworld":

# ... I don't know how to build this project to include "hello_world.sh" as resource file ...

Here is the expected output when running "helloworld" from terminal:

# helloworld
Hello World in hello_world.cpp
Hello World in hello_world.sh

I know I can install the "hello_world.sh" into the /home/test/hello_world.sh and then run it with "/bin/bash /home/test/hello_world.sh". But I just want to embed the "hello_world.sh" into the executable "helloworld" as a resource and call "hello_world.sh" internally from "helloworld".

Is it possible? If yes, how to do it?

like image 563
stackbiz Avatar asked Aug 31 '25 04:08

stackbiz


1 Answers

Maybe stream your bash script to the bash command like this :

#include <cstdio>
#include <string>
#include <iostream>
#include <sstream>
#include <vector>

std::string shellEscape(const std::string &arg) {
    if (arg.empty())
        return "''";
    std::string out = "'";
    for (char c: arg) {
        if (c == '\'')
            out += "'\\''";
        else
            out += c;
    }
    out += "'";
    return out;
}

int main(int argc, char *argv[]) {
    const std::vector<std::string> args(argv + 1, argv + argc);

    const std::string bashScript = R"BASH(
#!/usr/bin/env bash

printArgs() {
    if [ "$#" -ne 0 ]; then
        printf 'Arguments: %d\n' "$#"
        i=1
        for arg; do
            printf 'Argument %d: %s\n' "$i" "$arg"
            i=$((i + 1))
        done
    fi
}

printArgs "$@"
printf 'Hello from embedded Bash script!\n'
printf 'Current directory: %s\n' "$PWD"
printf 'System info: %s\n' "$(uname -a)"
)BASH";

    // Build command with escaped arguments
    std::ostringstream command;
    command << "/usr/bin/env bash -s --";
    for (const auto &arg: args) {
        command << ' ' << shellEscape(arg);
    }

    FILE *pipe = popen(command.str().c_str(), "w");
    if (!pipe) {
        perror("popen failed");
        return 1;
    }

    std::istringstream iss(bashScript);
    std::string line;
    while (std::getline(iss, line)) {
        fwrite(line.c_str(), 1, line.size(), pipe);
        fputc('\n', pipe);
    }

    if (const int exitStatus = pclose(pipe); exitStatus != 0) {
        std::cerr << "Bash execution failed with code "
                << exitStatus << std::endl;
        return 1;
    }

    return 0;
}

Each line of the Bash script is handled with Unix LF line endings. It sends it to the pipe for the bash command.

Bash does not make a difference if the script comes from a file or from a pipe stream. In both cases, bash loads the entire script in memory then start executing the script.

To answer @stackbiz's question: This make absolutely 0 difference if the embedded script has functions in it.

It also supports passing arguments safely to the embedded bash script.

Example execution:

./bashEmbed foo\\ bar\" 'baz composed'

Output:

Arguments: 3
Argument 1: foo\
Argument 2: bar"
Argument 3: baz composed
Hello from embedded Bash script!
Current directory: /home/lea/CLionProjects/BashEmbed
System info: Linux marvin 6.11.0-26-generic #26~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Apr 17 19:20:47 UTC 2 x86_64 x86_64 x86_64 GNU/Linux
like image 99
Léa Gris Avatar answered Sep 02 '25 17:09

Léa Gris