ShitalShah ShitalShah - 3 years ago 177
Linux Question

Running multi-line bash script as string from C++ code

I want to run following bash script from C++ code. I tries to use

system()
or
popen
to run commands and capture its output but they but I get errors because built-in sh tries to execute it, such as,

sh: 6: [[: not found
sh: 8: [[: not found
sh: 9: [[: not found


I tried
bash -c
as well but that also produced errors because I think it doesn't handle multiline string.

I can't put below script in to .sh file and run it because of several reasons. So this script needs to be stored as a string in C++ code and get executed. Any idea how this can be done?

#!/bin/bash
for sysdevpath in $(find /sys/bus/usb/devices/usb*/ -name dev); do
(
syspath="${sysdevpath%/dev}"
devname="$(udevadm info -q name -p $syspath)"
[[ "$devname" == "bus/"* ]] && continue
eval "$(udevadm info -q property --export -p $syspath)"
[[ -z "$ID_SERIAL" ]] && continue
[[ "${ID_SERIAL}" == *"PX4"* ]] && echo "/dev/$devname"
)
done


Sample code:

Note: You can use this tool to convert text to C++ escapped string.

int main() {
std::cout << system("#!/bin/bash\nfor sysdevpath in $(find /sys/bus/usb/devices/usb*/ -name dev); do\n (\n syspath=\"${sysdevpath%/dev}\"\n devname=\"$(udevadm info -q name -p $syspath)\"\n [[ \"$devname\" == \"bus/\"* ]] && continue\n eval \"$(udevadm info -q property --export -p $syspath)\"\n [[ -z \"$ID_SERIAL\" ]] && continue\n [[ \"${ID_SERIAL}\" == *\"PX4\"* ]] && echo \"/dev/$devname\"\n )\ndone");

return 0;
}

Answer Source

You can turn a multiline script to single-line. Let's assume you have the following script:

FOO=`uname`
if [ "$FOO" == "Linux" ]; then
    echo "You are using 'Linux'"
fi

The code above can be transformed into single-line by using semicolons:

FOO=`uname`; if [ "$FOO" == "Linux" ]; then echo "You are using 'Linux'"; fi

Now with proper escaping you can use system command to execute it from your program as follows:

#include <cstdlib>
#include <string>

int main() {
    std::string foo {
        "bash -c '"
        "FOO=`uname`; "
        "if [ \"$FOO\" == \"Linux\" ]; then "
        "echo \"You are using 'Linux'.\"; "
        "fi'"
    };
    system(foo.c_str());
}

Note that adjacent string literals are concatenated by the compiler, so you can still make it look like a multiline script for better readability.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download