Subcommand auto-routing with argv0

We're in the process of unifying a bunch of small tools into a single binary and using a subcommand to launch the specific tool.

I have a potentially unknown number of scripts calling some of these tools, so it'd be handy if Swift Argument Parser would "auto-route" to a subcommand based on the contents of argv[0].

That is, if I had the subcommand "hello" and I had a symlink pointing to my executable ("tool") named "hello" (hello -> tool), the subcommand should trigger automatically when invoked from this symlink.

Is this doable?

1 Like

Yes, try this:

import Foundation
print(CommandLine.arguments[0].components(separatedBy: "/").last!)

and beware of edge cases (e.g. path without "/" or executable/link name containing "/", etc)

Please don't hardcode "/" as it is non-portable. Prefer something like this:

import Foundation
print(URL(fileURLWithPath: CommandLine.arguments[0]).components.last)
2 Likes

Right. And since we are using Foundation anyway there's more direct way to get that URL:

print(Bundle.main.executableURL!.lastPathComponent)

I would not rely on Bundle.main.executableURL to not resolve symlinks.

Using argv[0] is the right thing to do IMHO.

URL(fileURLWithPath: CommandLine.arguments[0]).lastPathComponent

But anyway, this is not what @jamesez is asking for. The subject is about using Swift Command Parser to automatically perform this.

1 Like

Bundle.main.executableURL works "correctly" with symlinks (returns the name of the link rather than the name of the link target), which is the behaviour OP is after in this case.

What I actually want is not how to read arg0, but to have argument-parser automatically route arg0 to a subcommand if the subcommand and arg0 match.

1 Like

What is "subcommand"? If it is a function in your code compiled into the same binary, then you can do this dispatch manually:

func main() {
    switch Bundle.main.executableURL!.lastPathComponent {
    case "hello": hello()
    case "world": world()
    default: help()
    }
}

main()

func hello() {}
func world() {}
func help() {}

By subcommand I mean argument-parser’s subcommand feature: Documentation

In the example docs, if I had “add” or “multiply” symlinked to “math”, I could either execute “math add 1 2” or “add 1 2”

Got you, scratch out what I wrote above, I didn't notice this question is about this specific framework.

Hi @jamesez

That’s an interesting question! This should be doable if you implement your own static func main() method on your root command. The default implementation drops the first argument from CommandLine.arguments before beginning parsing, so what you could do is check if the first argument is one of your recognized commands, and then send the full list to parseAsRoot(_:) instead.