It's not a universal "for all compiled langs," but I published a two-liner [0] with zero (non-standard) dependencies or pre-requisites that can be copy-and-pasted to the top of any rust code to turn it into an executable script (cached! also correctly cache-invalidating!) back in 2020.
It even lets you optionally omit the `fn main() { ... }` surrounding your code as well, if you really want to feel like you're just shell scripting <insert appropriate emoji here>.
It's an example of "polyglot source code" that is valid under two languages at once (in this case, standard sh and rust), (ab)using the rust `#[allow(...)]` "macro" to do double-duty as a shell script comment opener.
The clean solution of cargo script is here: https://github.com/rust-lang/cargo/issues/12207
Following the links, the RFC contains a nice list of similar tools: https://rust-lang.github.io/rfcs/3424-cargo-script.html#prio...
> Would it have been possible back then to use cargo to pull in some dependencies?
Not at the time, at least not that I know of.
I wonder if it's possible to get a link to this solution added to that "prior art" section.
Tiny nitpick: the #![allow()] generates a 'warning: unused attribute' warning for me.
If you change it to #![allow(unused_attributes)] it disables the warning for itself.
For example I put this in front of my test C files:
#if 0
set -e; [ "$0" -nt "$0.bin" ] &&
gcc -Wall -Wextra -pedantic -std=c99 "$0" -o "$0.bin"
exec "$0.bin" "$@"
#endif
I call those: polyglot shell scripts. All my examples are valid files for the language while working as shell scripts at the same time.In the past when Python 3 was still not obviously there and under different names it was once useful for me to have this on top of a python script:
"set" "-e"
"exec" "$(for p in python3.7 python3 python; do which p && break; done)" "$0"
"$@"
It works, because Python doesn't care about stray strings.You can also use the trick with Makefiles, although I never needed to use it:
#\
set -e
#\
exec make -f "$0" "$@"
This is not a useful example, but it works. This works, because make will treat slash followed by a new-line as a comment continuation and bash will ignore it.I know that bash does this, and possibly other shells, but it doesn't work in general when you `execv` the file (or when using the subprocess API on python f.i). But cool trick anyway
At least with TFAs shebang at the top of the file, it kind of gives a clue. Upon reflection, I don't feel great about either way, haha. Even if it makes sense to me, it'll be a little too sneaky for most.
EDIT:
Actually, that was how the original version worked, but it had the limitation that once the script changed you could no longer determine the path to the previous executable output to delete, so you could end up accumulating a lot of defunct binaries. Now the hash is stored alongside the target.
But that's not going to happen.
So next best option is probably weirdly a markdown file executor which runs triple quoted blocks.
The upside is that you could mix different languages, which is interesting.
The value here is more in being able to read a script before you run it, then have it run fast, maybe tweaking something here and there. And a compiled script will run 10,000 times faster than LISP, which can be important.
Putting aside some hyperbole, this depends how long it takes to compile.
The key word above is "usually". There's no good reason to make source files executable. And also, there's no much sense in compiling any source file individually instead of relying on a proper build system.
The only systems I know of that do that properly are Deno and I believe F#, though I haven't tried the latter.
Unfortunately Deno has a stupid issue where `deno run` will check for updates and print a "new version of Deno is available" message which rather sours the otherwise great experience of using it for shell scripting.
#!/usr/bin/env -S PIP_RUN_RETENTION_STRATEGY=persist pip-run
# Requirements:
# pendulum>=1.0.0
# requests
import requests
print('hello')[1] https://rust-lang.github.io/rfcs/3424-cargo-script.html#prio...
#!/usr/bin/env nix-shell
#![allow()] /*
#!nix-shell -i bash -p rustc
rsfile="$(readlink -f $0)"
binfile="/tmp/$(basename "$rsfile").bin"
rustc "$rsfile" -o "$binfile" --edition=2021 && exec "$binfile" $@ || exit $?
*/
fn main() {
for argument in std::env::args().skip(1) {
println!("{}", argument);
};
println!("{}", std::env::var("HOME").expect(""));
}
and another: #! /usr/bin/env nix-shell
#! nix-shell -p "haskellPackages.ghcWithPackages (p: with p; [turtle])" -i runghc
{-# LANGUAGE OverloadedStrings #-}
import Turtle
main = echo "Hello world!"
1. https://nixos.wiki/wiki/Nix-shell_shebang