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?
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With