🐳 Dockerfile.exe

tldr: self-executing Dockerfiles using an awk script as the interpreter.

What?

When the kernel runs your shebang, it only does one split.

This means you can’t pass multiple parameters because they all get smushed together as one. So this sort of thing will fail:

#!/bin/sh -c 'echo party pooper'

Because it’s passing -c 'echo party pooper' into the first parameter for sh. So it reads the -c part and sees that ' ' isn’t an option and fails.

You can’t cheat it by passing -c'echo hello' either; it needs a space after the -c because it’s expected.

Same deal with bash, zsh, dash and csh. Some other shells do work, but nobody uses them (except you).

$ ./party_pooper
/bin/sh: 0: Illegal option -

$ sh '-c "echo party pooper"'
/bin/sh: 0: Illegal option -

$ sh -c"echo hello"
/bin/sh: 0: Illegal option -h

$ bash -c"boooh"
bash: -c: option requires an argument

Why?

Because the \0 terminated argv list only has 3 args. The binary ($0), everything after it ($1) and the name of the file ($2).

You can use env with the -S flag to work around that; this will work:

#!/usr/bin/env -S sh -c 'echo hello!'

But it’s not POSIX. And might be in /bin. It’s usually in both so that’s not a problem, but technically correct is the best form of correct.

So I was messing about and found a way around it is using awk, which is POSIX:

$ cat meh
#!/bin/awk BEGIN {system("hexdump -C /proc/$PPID/cmdline")}

$ ./meh
00000000  2f 62 69 6e 2f 61 77 6b  00 42 45 47 49 4e 20 7b  |/bin/awk.BEGIN {|
00000010  73 79 73 74 65 6d 28 22  68 65 78 64 75 6d 70 20  |system("hexdump |
00000020  2d 43 20 2f 70 72 6f 63  2f 24 50 50 49 44 2f 63  |-C /proc/$PPID/c|
00000030  6d 64 6c 69 6e 65 22 29  7d 00 2e 2f 6d 65 68 00  |mdline")}../meh.|
00000040

YEAH BUT WHYYY?!

So I can make a self-executing Dockerfile of course! The following copies a Dockerfile to /tmp, builds and runs it in that place, then deletes the temp dir afterwards:

#!/bin/awk BEGIN { system("t=$(mktemp -d); cat " ARGV[1] " > $t/Dockerfile; cd $t; docker run --rm $(docker build -q .); rm -rf $t") }
FROM alpine

ENTRYPOINT ["sh", "-c", "echo hello world"]

Works for the files I’ve tried it with, so it should probably be more of a thing!

awk is bad and you should feel bad

Well, it’s one way to deal with the problems of passing the input as an arg and the file into stdin and std out back into itself or whatever the hell is going on.

Here’s some you could try though: