3

I'm looking for a way to have an executable binary hardwired into a script. Something like this:

#!/bin/bash
...some shell code
execute binary:
    >>>
        binary
        code
        ...
    <<<
...some more shell code possibly

I've found this solution, which uses uuencode and is good. But it depends on shrutils, which seem to be an extra, as they're not included by default on my Debian.

I've been thinking of having the binary encoded with base64 and then decoding it and somehow executing, possibly without creating any temp files. I remember there was a library that's responsible for executing things, but forgot what it was.

It might be the best to have a construct as simple as this execute:

$ <(base64 out | base64 -d)
bash: /dev/fd/63: Permission denied
  • Are you looking for no-tool-support-needed to run, or no-tool-support-needed to create? – ctrl-alt-delor Jun 19 '18 at 11:50
  • Consider also: putting the shell script inside of the binary-executable. – ctrl-alt-delor Jun 19 '18 at 11:52
  • @ctrl-alt-delor To run. Creation would be another question. And so I'd rather embed the executable in a script, not the other way round, to be able to modify the "packaging" easily. – Tomasz Jun 19 '18 at 11:56
  • 2
    Note that uudecode, contrary to base64 is a standard POSIX command. So while on Debian it may be more likely to find a base64 command than uudecode, it may be the contrary on many other systems. – Stéphane Chazelas Jun 19 '18 at 14:39
2

How about:

unpack() {
    tail +9 "$0" > /tmp/xxx.$$
    chmod +x /tmp/xxx.$$
}
unpack
/tmp/xxx.$$ <add args here>
rm /tmp/xxx.$$
exit
<add the binary here>

If you don't like to have binary data in the script, you may encode it and replace cat by the related decoder.

Note that you need to replace the +9 by the line number where the binary starts in case that you modify the script to be of different length.

If your tail implementation does not support the argument +9, try -n +9 instead.

If you are in fear of clobbering an existing /tmp file, try to use mktemp(1) to create the tmp filename.

Note that this method was used by the upgrade scripts for the SunPro compiler suite that included the compressed tar archive with the whole upgrade and some shell code to manage the handling around that.

  • I used base64 instead of cat, as the executable code doesn't look good in a script. – Tomasz Jun 19 '18 at 12:06
  • Use a longer marker, EOF is a bit short, and the binary code may trigger it. – ctrl-alt-delor Jun 19 '18 at 13:51
  • Well, the marker must appear directly after a newline. – schily Jun 19 '18 at 13:57
  • @Schily so that is 4 bytes \nEOF. You could make it longer and safer, with little waste of space. – ctrl-alt-delor Jun 19 '18 at 14:06
  • That approach would only work with zsh. Other shells would strip or choke on NUL bytes. Even with zsh, that means the binary file would have to end in a newline character. – Stéphane Chazelas Jun 19 '18 at 14:28
2

Starting with

aShellScript aBinaryExecutable

do

zip binary.zip aBinaryExecutable
cat aShellScript binary.zip > hybrid
chmod +x hybrid

I have missed out what to put in the script, to extract and run the binary, but note hybrid is a valid zip-file, and a valid shell script, the shell script can unzip itself and get the binary-executable (but not the shell script).

Advantage

Is robust: The file is a valid zip, and a valid shell script (so long as you exit before trying to interpret the garbage at the end).

Disadvantage

The end of script is ugly.

Why it works

  • shell starts interpreting from start of file. (All offsets are symbolic.)
  • zip starts interpreting from end of file. All offsets are relative.
  • I've not tested it and zip is not installed on Debian by default, but +1 for idea. I assume it works. – Tomasz Jun 19 '18 at 14:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.