// +build windows // We both need to // * use the registry editor directly to avoid possible PATH truncation // ( https://stackoverflow.com/questions/9546324/adding-directory-to-path-environment-variable-in-windows ) // ( https://superuser.com/questions/387619/overcoming-the-1024-character-limit-with-setx ) // * explicitly send WM_SETTINGCHANGE // ( https://github.com/golang/go/issues/18680#issuecomment-275582179 ) // * also install as a service // ( https://github.com/golang/sys/blob/master/windows/svc/example/install.go ) package main import ( "fmt" "os" "path/filepath" "strings" "golang.org/x/sys/windows/registry" ) var sendmsg func() func usage() { fmt.Fprintf(os.Stderr, "Usage: winpath show|append|prepend|remove \n") } func main() { fmt.Println("PATH:", os.Getenv("PATH")) //fpath, err := exec.LookPath("reg") //fmt.Println("LookPath(\"reg\"):", fpath, err) shell := os.Getenv("SHELL") if "" == shell { if strings.HasSuffix(os.Getenv("COMSPEC"), "/cmd.exe") { shell = "cmd" } } fmt.Println("SHELL?", shell) fmt.Println("os.PathListSeparator:", string(os.PathListSeparator)) // WM_SETTING_CHANGE // https://gist.github.com/microo8/c1b9525efab9bb462adf9d123e855c52 // os.Setenv("PATH") // TODO --system to add to the system PATH rather than the user PATH if len(os.Args) < 2 { usage() os.Exit(1) } action := os.Args[1] paths, err := Paths() if nil != err { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(2) } if "show" == action { if len(os.Args) > 2 { usage() } fmt.Println() for i := range paths { fmt.Println("\t" + paths[i]) } fmt.Println() return } if len(os.Args) < 3 { usage() os.Exit(1) } pathname := os.Args[2] abspath, err := filepath.Abs(pathname) if nil != err { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(2) } if "remove" != action { stat, err := os.Stat(pathname) if nil != err { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(2) } if !stat.IsDir() { fmt.Fprintf(os.Stderr, "%q is not a directory (folder)\n", pathname) os.Exit(2) } } index := -1 for i := range paths { if pathname == paths[i] { index = i break } if abspath == paths[i] { index = i break } } switch action { default: usage() os.Exit(1) case "append": if index >= 0 { fmt.Fprintf(os.Stderr, "%q is already in PATH at position %d. Remove it first to re-order.\n", pathname, index) os.Exit(3) } paths = append(paths, pathname) fmt.Println("Run this to cause settings to take affect immediately:") fmt.Println("\tPATH %PATH%;" + pathname) case "prepend": if index >= 0 { fmt.Fprintf(os.Stderr, "%q is already in PATH at position %d. Remove it first to re-order.\n", pathname, index) os.Exit(3) } paths = append([]string{pathname}, paths...) fmt.Println("Run this to cause settings to take affect immediately:") fmt.Println("\tPATH " + pathname + ";%PATH%") case "remove": if index < 0 { fmt.Fprintf(os.Stderr, "%q is NOT in PATH.\n", pathname) os.Exit(3) } oldpaths := paths paths = []string{} for i := range oldpaths { if i != index { paths = append(paths, oldpaths[i]) } } } k, err := registry.OpenKey(registry.CURRENT_USER, `Environment`, registry.SET_VALUE) if err != nil { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(4) } defer k.Close() // ";" on Windows err = k.SetStringValue(`Path`, strings.Join(paths, string(os.PathListSeparator))) if nil != err { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(4) } err = k.Close() if nil != err { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(4) } err = os.Setenv(`PATH`, strings.Join(paths, string(os.PathListSeparator))) if nil != err { fmt.Fprintf(os.Stderr, "%s\n", err) os.Exit(4) } if nil != sendmsg { fmt.Println("Open a new Terminal for the updated PATH") sendmsg() } else { fmt.Println("You'll need to reboot for setting to take affect") } } func Paths() ([]string, error) { k, err := registry.OpenKey(registry.CURRENT_USER, `Environment`, registry.QUERY_VALUE) if err != nil { return nil, err } defer k.Close() // This is case insensitive (PATH, Path, path) s, _, err := k.GetStringValue("Path") if err != nil { return nil, err } // ";" on Windows return strings.Split(s, string(os.PathListSeparator)), nil /* shstr := loader() if "" == shstr { shstr = "NO_SHELL_DETECTED" } if "" == s { s = "NO_WINREG_PATH" } return fmt.Sprintf("%s\n%s", s, shstr) */ }