Over a million developers have joined DZone.

Start an Interactive Shell from Within Go

DZone's Guide to

Start an Interactive Shell from Within Go

· DevOps Zone ·
Free Resource

The need for DevOps innovation has never been greater. Get the results from over 100 business value assessments in this whitepaper, Digital Darwinism: Driving Digital Transformation, to see the positive impact of DevOps first hand.

Looking around the web for information on creating a new shell from Go, I kept finding the same answer: "You can't do it." Actually, you can do it, and it's not hard.

My goal was to write a Go program that did some processing, set up a particular environment, and then opened an interactive UNIX shell for the user. I wanted the shell to have the following characteristics:

  • Act like the user's regular shell
  • Have certain extra environment variables set
  • Start in a particular directory
  • Return control to the Go program when the user types exit

All of this can be accomplished readily with just a few Go functions (all from the core os package) and a little UNIX knowledge.

This technique should work for most UNIX flavors, including OSX (my dev platform) and Linux.

package main

package main

import (

func main() {

    // Get the current user.
    me, err := user.Current()
    if err != nil {

    // Get the current working directory.
    cwd, err := os.Getwd()
    if err != nil {

    // Set an environment variable.
    os.Setenv("SOME_VAR", "1")

    // Transfer stdin, stdout, and stderr to the new process
    // and also set target directory for the shell to start in.
    pa := os.ProcAttr {
        Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
        Dir: cwd,

    // Start up a new shell.
    // Note that we supply "login" twice.
    // -fpl means "don't prompt for PW and pass through environment."
    fmt.Print(">> Starting a new interactive shell")
    proc, err := os.StartProcess("/usr/bin/login", []string{"login", "-fpl", me.Username}, &pa)
    if err != nil {

    // Wait until user exits the shell
    state, err := proc.Wait()
    if err != nil {

    // Keep on keepin' on.
    fmt.Printf("<< Exited shell: %s\n", state.String())

There are a few things to mention about the code above:

  • I use login instead of explicitly setting the shell. I do this because that ensures that all the usually profiles and scripts are executed. It's fine to use the user's existing shell, too. You can get it with os.Getenv("SHELL").
  • You really shouldn't panic on every error. I did that for convenience.
  • proc.Wait() (as the name implies) waits until the shell is done before continuing.
  • If we omit the proc.Wait() part, the Go process will quite... and also terminate the shell. There may be a way around this, but I don't know it.
  • Doing this sort of thing in a program that uses goroutines may cause... interesting... side... effects.
  • You can also use "os/exec".LookPath() to lookup the path to login instead of hard-coding the path as I did.

Interested in Kubernetes but unsure where to start? Check out this whitepaper, A Roundup of Managed Kubernetes Platforms from Codeship by Cloudbees, for an overview and comparison of Kubernetes platforms. 


Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}