Here's one use case:
#!/usr/bin/env zig
const std = @import("std");
pub fn main() !void {
// If this program is run without stdout attached, exit with an error.
var stdout_file = try std.io.getStdOut();
// If this program encounters pipe failure when printing to stdout, exit
// with an error.
try stdout_file.write("Hello, world!\n");
}
Using env is the portable way to use an interpreter for a shebang line, and Linux passes everything after the space as a single argument, which means we can't do something like #!/usr/bin/env zig run.
As an example, on my home OS - NixOS - the /usr/bin directory is empty except for env:
andy@xps ~> ls /usr/bin/
env
The other use case is that Zig now enters the playing field, alongside commands like these:
python foo.py [args]node foo.js [args]ruby foo.rb [args]perl foo.pl [args]This command would be unambiguous: args would be parsed normally until the first positional argument. If the first positional argument does not have a / in it then it is treated as a subcommand. Otherwise it is treated as a zig run target, and the rest of the arguments are forwarded to the program. If the file does not exist, a helpful error message could suggest that perhaps the user meant to try one of the subcommands, or they meant to create the file and execute it.
Here is a demonstration that it would be unambiguous:
$ ./zig test.zig
Unrecognized command: test.zig
Usage: ./zig [command] [options]
...
/:andy@xps ~/tmp> cat build-obj
#!/usr/bin/env echo
andy@xps ~/tmp> chmod +x build-obj
andy@xps ~/tmp> build-obj
build-obj: command not found
andy@xps ~/tmp> ./build-obj
./build-obj
So as long as no zig subcommands have a / in them, this would be unambiguous. This seems like a reasonable restriction.
cc @tiehuis - I think we discussed this before and ended up on status quo, but I'd like to bring it up one more time.
Another option: symlink zig-run to zig, have zig check arg[0] to see if it was called as zig-run.
If the first positional argument does not have a
/in it then it is treated as a subcommand. Otherwise it is treated as a zig run target
This is unambiguous in most but not all cases.
Invoking shebang scripts on the PATH uses absolute paths, so this is good:
~/tmp$ echo '#!/usr/bin/env echo' > build-obj
~/tmp$ chmod +x build-obj
~/tmp$ export PATH=$PATH:$(pwd)
~/tmp$ build-obj
/home/josh/tmp/build-obj
~/tmp$ cd ..
~$ build-obj
/home/josh/tmp/build-obj
However, we can get ambiguity by calling execv directly:
~/tmp$ python -c 'import os; os.execv("build-obj", ["blah"])'
build-obj
(I think the ./ prefix to invoke a program in the current directory is just a convention that shell languages follow.)
#!/usr/bin/env zig
As of GNU coreutils v8.30, you can do:
#!/usr/bin/env -S zig run
This feature was added in a recent (2018-04-20) commit to env.c in the
GNU coreutils package, which added the -S or --split-string option. I'm
pretty sure this feature was adopted from FreeBSD's version of env.
From the env man page:
OPTIONS
-S/--split-string usage in scripts
The -S option allows specifing multiple parameters in a script.
Running a script named 1.pl containing the following first line:
#!/usr/bin/env -S perl -w -T
Will execute perl -w -T 1.pl .
Without the '-S' parameter the script will likely fail with:
/usr/bin/env: 'perl -w -T': No such file or directory
See the full documentation for more details.
More examples are available in the GNU coreutils manual.
Note: This only applies to shebangs that use /usr/bin/env, as
--split-string is a feature of env specifically.
I'm reconsidering this. It's common to have zig installed somewhere other than globally. Plus, there could be different zig versions. And what about packages? Relying on the shebang line is a weird way to distribute zig code.
Is the use case for shebang support written down somewhere? I can't find the original proposal and am struggling to think of when I would need or even want to call $ ./script.zig over running a precompiled executable.
No there is no documented use case. The original motivation was just trying to be POSIX friendly.
It's common to have zig installed somewhere other than globally. Plus, there could be different zig versions.
The whole point of using env is that it'll pick the zig in your path. If you have multiple versions of zig installed; or somewhere other than globally, then you can still add them to your PATH.
See reasoning here for why this proposal is being rejected: https://github.com/ziglang/zig/issues/2165#issuecomment-478813464
Most helpful comment
As of GNU coreutils v8.30, you can do:
#!/usr/bin/env -S zig runThis feature was added in a recent (2018-04-20) commit to
env.cin theGNU coreutils package, which added the
-Sor--split-stringoption. I'mpretty sure this feature was adopted from FreeBSD's version of
env.From the
envman page:More examples are available in the GNU coreutils manual.
Note: This only applies to shebangs that use
/usr/bin/env, as--split-stringis a feature ofenvspecifically.