Executable, single files with dependency specification should have first class support in all languages.
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 problem with these systems is third party dependencies and IDE support. You really want to be able to use third party dependencies, and have IDEs know about them.
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.
For the rustaceans in the crowd:
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.
For python
#!/usr/bin/env -S PIP_RUN_RETENTION_STRATEGY=persist pip-run
# Requirements:
# pendulum>=1.0.0
# requests
import requests
print('hello')
In my experience, writing small but critical shell-script-style mini-programs in rust can actually be a really good idea. The biggest drawback is, IMO, all the overhead of setting up an entire crate just to compile a tiny script, so I'm really happy to see projects like this (and other tools [1]) for this workflow.
[1] https://rust-lang.github.io/rfcs/3424-cargo-script.html#prio...
It's interesting being able to execute the source file like this, by passing in build flags and compilation instructions. But in Emacs I can do this by simply writing my code in scratch and then evaluating each piece of it with ctrl-j. I will immediately get output. The LISP read-eval-print loop enables this type of power.
not too dissimilar an idea from Tom Duff's com executable https://web.archive.org/web/20210204050841/http://www.iq0.co...
It looks like it invokes the compiler every time the script is run? That's nice if you've changed the file, but takes extra time if you haven't. It seems neat at first, but I guess I don't see the advantage over just compiling to an executable and then running that.
Via Simon Willison's excellent blog: https://simonwillison.net/2024/Feb/6/scriptisto/ , via lobste.rs: https://lobste.rs/s/ccssqq/scriptisto_writing_one_file_scrip...
Somewhat related: nix-shell supports an additional shebang, allowing summoning of any combination of packages, to be made available inside a script. An example from the docs[1]:
#!/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_shebangIn both Linux and Macos source files are usually not executable. So what's the point of this? Applicable only in Windows with ex-FAT/FAT32 (probably, also not a thing in NTFS, but not sure whether needs explicitly forbidding 'execute' flag or how it's called there) which is able to run any file.
I prefer to take advantage of the fact that without a proper shebang the file will be first executed by the shell. Then it is easy to do almost whatever you want.
For example I put this in front of my test C files:
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:
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:
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.