Shunsuke Suzuki
Shunsuke Suzuki

Reputation: 91

How can we execute a command changing the command name argv0 in Go?

When executing a command using os/exec or golang.org/x/sys/unix packages, How can we execute a command changing the command name argv0 like exec's -a option?

e.g.

exec -a assumego granted "$@" # Execute granted as assumego

I expected we could change the command name by changing exec.Cmd.Args[0] or unix.Exec's argv[0], but they didn't work.

A script foo:

#!/usr/bin/env bash

set -eu

echo "0: $0"
echo "@: $@"

echo foo

main.go using os/exec:

package main

import (
    "log"
    "os"
    "os/exec"
)

func main() {
    if err := core(); err != nil {
        log.Fatal(err)
    }
}

func core() error {
    cmd := exec.Command("./foo", "a", "b")
    cmd.Args[0] = "bar"
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    if err := cmd.Run(); err != nil {
        return err
    }
    return nil
}

Run main.go, but $0 isn't changed.

$ go run main.go
0: ./foo
@: a b
foo

main.go using golang.org/x/sys/unix:

package main

import (
    "log"
    "os"

    "golang.org/x/sys/unix"
)

func main() {
    if err := core(); err != nil {
        log.Fatal(err)
    }
}

func core() error {
    return unix.Exec("../foo", []string{"bar", "a", "b"}, os.Environ()) //nolint:wrapcheck
}

Run main.go, but $0 isn't changed.

$ go run main.go
0: ../foo
@: a b
foo

Reference:

Background:

Upvotes: 4

Views: 130

Answers (1)

booniepepper
booniepepper

Reputation: 61

I'm not sure exactly when it was introduced, but in the current versions of bash, you can set/export BASH_ARGV0. https://www.man7.org/linux/man-pages/man1/bash.1.html

If you need to support older versions of bash, though, like the 3.2 version on Mac, this is not available.

Zsh allows setting 0 directly. Posix shell does not have a method for doing this.


One "hack" that I've used (many times now) to get around this, which works with shell scripts as well as executables, is creating symlinks with the name I want as argv[0].

bash-3.2$ echo -e '#!/bin/sh\necho "$(basename "$0")" "$@"' >tmp.sh \
      && chmod +x tmp.sh \
      && ./tmp.sh hello world \
      && ln -s tmp.sh foo \
      && ./foo hello world
tmp.sh hello world
foo hello world

This allows for a lot of fun patterns like a "dispatch" executable that can redirect to other executables. Depending on the usecase, these symlinks can also be created on demand and cleaned up after execution.

Upvotes: 1

Related Questions