diff --git a/README.md b/README.md
index dbcad36..dc62bfe 100644
--- a/README.md
+++ b/README.md
@@ -70,6 +70,7 @@ Option | Description
 -s  | select initial container sort field
 -scale-cpu	| show cpu as % of system total
 -v	| output version information and exit
+-shell | specify shell (default: sh)
 
 ### Keybindings
 
@@ -84,6 +85,7 @@ s | Select container sort field
 r | Reverse container sort order
 o | Open single view
 l | View container logs (`t` to toggle timestamp when open)
+e | Exec Shell
 S | Save current configuration to file
 q | Quit ctop
 
diff --git a/config/param.go b/config/param.go
index 23a7670..86f5828 100644
--- a/config/param.go
+++ b/config/param.go
@@ -12,6 +12,11 @@ var params = []*Param{
 		Val:   "state",
 		Label: "Container Sort Field",
 	},
+	&Param{
+		Key:   "shell",
+		Val:   "sh",
+		Label: "Shell",
+	},
 }
 
 type Param struct {
diff --git a/connector/manager/docker.go b/connector/manager/docker.go
index 77dc987..5b683fc 100644
--- a/connector/manager/docker.go
+++ b/connector/manager/docker.go
@@ -3,6 +3,9 @@ package manager
 import (
 	"fmt"
 	api "github.com/fsouza/go-dockerclient"
+	"github.com/pkg/errors"
+	"io"
+	"os"
 )
 
 type Docker struct {
@@ -17,6 +20,88 @@ func NewDocker(client *api.Client, id string) *Docker {
 	}
 }
 
+// Do not allow to close reader (i.e. /dev/stdin which docker client tries to close after command execution)
+type noClosableReader struct {
+	io.Reader
+}
+
+func (w *noClosableReader) Read(p []byte) (n int, err error) {
+	return w.Reader.Read(p)
+}
+
+const (
+	STDIN  = 0
+	STDOUT = 1
+	STDERR = 2
+)
+
+var wrongFrameFormat = errors.New("Wrong frame format")
+
+// A frame has a Header and a Payload
+// Header: [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}
+// STREAM_TYPE can be:
+//    0: stdin (is written on stdout)
+//    1: stdout
+//    2: stderr
+// SIZE1, SIZE2, SIZE3, SIZE4 are the four bytes of the uint32 size encoded as big endian.
+// But we don't use size, because we don't need to find the end of frame.
+type frameWriter struct {
+	stdout io.Writer
+	stderr io.Writer
+	stdin  io.Writer
+}
+
+func (w *frameWriter) Write(p []byte) (n int, err error) {
+	// drop initial empty frames
+	if len(p) == 0 {
+		return 0, nil
+	}
+
+	if len(p) > 8 {
+		var targetWriter io.Writer
+		switch p[0] {
+		case STDIN:
+			targetWriter = w.stdin
+			break
+		case STDOUT:
+			targetWriter = w.stdout
+			break
+		case STDERR:
+			targetWriter = w.stderr
+			break
+		default:
+			return 0, wrongFrameFormat
+		}
+
+		n, err := targetWriter.Write(p[8:])
+		return n + 8, err
+	}
+
+	return 0, wrongFrameFormat
+}
+
+func (dc *Docker) Exec(cmd []string) error {
+	execCmd, err := dc.client.CreateExec(api.CreateExecOptions{
+		AttachStdin:  true,
+		AttachStdout: true,
+		AttachStderr: true,
+		Cmd:          cmd,
+		Container:    dc.id,
+		Tty:          true,
+	})
+
+	if err != nil {
+		return err
+	}
+
+	return dc.client.StartExec(execCmd.ID, api.StartExecOptions{
+		InputStream:  &noClosableReader{os.Stdin},
+		OutputStream: &frameWriter{os.Stdout, os.Stderr, os.Stdin},
+		ErrorStream:  os.Stderr,
+		RawTerminal:  true,
+	})
+}
+
 func (dc *Docker) Start() error {
 	c, err := dc.client.InspectContainer(dc.id)
 	if err != nil {
diff --git a/connector/manager/main.go b/connector/manager/main.go
index b6debaa..f65aad3 100644
--- a/connector/manager/main.go
+++ b/connector/manager/main.go
@@ -7,4 +7,5 @@ type Manager interface {
 	Pause() error
 	Unpause() error
 	Restart() error
+	Exec(cmd []string) error
 }
diff --git a/connector/manager/mock.go b/connector/manager/mock.go
index f33fd77..f6fd62f 100644
--- a/connector/manager/mock.go
+++ b/connector/manager/mock.go
@@ -29,3 +29,7 @@ func (m *Mock) Unpause() error {
 func (m *Mock) Restart() error {
 	return nil
 }
+
+func (m *Mock) Exec(cmd []string) error {
+	return nil
+}
diff --git a/connector/manager/runc.go b/connector/manager/runc.go
index cf61f14..07a4b58 100644
--- a/connector/manager/runc.go
+++ b/connector/manager/runc.go
@@ -29,3 +29,7 @@ func (rc *Runc) Unpause() error {
 func (rc *Runc) Restart() error {
 	return nil
 }
+
+func (rc *Runc) Exec(cmd []string) error {
+	return nil
+}
diff --git a/container/main.go b/container/main.go
index 258f9d1..416b878 100644
--- a/container/main.go
+++ b/container/main.go
@@ -153,3 +153,7 @@ func (c *Container) Restart() {
 		}
 	}
 }
+
+func (c *Container) Exec(cmd []string) error {
+	return c.manager.Exec(cmd)
+}
diff --git a/grid.go b/grid.go
index 501c216..871d3cb 100644
--- a/grid.go
+++ b/grid.go
@@ -116,6 +116,10 @@ func Display() bool {
 		menu = LogMenu
 		ui.StopLoop()
 	})
+	ui.Handle("/sys/kbd/e", func(ui.Event) {
+		menu = ExecShell
+		ui.StopLoop()
+	})
 	ui.Handle("/sys/kbd/o", func(ui.Event) {
 		menu = SingleView
 		ui.StopLoop()
diff --git a/main.go b/main.go
index 3a93a4c..f2eac66 100644
--- a/main.go
+++ b/main.go
@@ -45,6 +45,7 @@ func main() {
 		invertFlag      = flag.Bool("i", false, "invert default colors")
 		scaleCpu        = flag.Bool("scale-cpu", false, "show cpu as % of system total")
 		connectorFlag   = flag.String("connector", "docker", "container connector to use")
+		defaultShell    = flag.String("shell", "", "default shell")
 	)
 	flag.Parse()
 
@@ -87,6 +88,10 @@ func main() {
 		config.Toggle("scaleCpu")
 	}
 
+	if *defaultShell != "" {
+		config.Update("shell", *defaultShell)
+	}
+
 	// init ui
 	if *invertFlag {
 		InvertColorMap()
diff --git a/menus.go b/menus.go
index 496c726..5c893a6 100644
--- a/menus.go
+++ b/menus.go
@@ -25,6 +25,7 @@ var helpDialog = []menu.Item{
 	{"[r] - reverse container sort order", ""},
 	{"[o] - open single view", ""},
 	{"[l] - view container logs ([t] to toggle timestamp when open)", ""},
+	{"[e] - exec shell", ""},
 	{"[S] - save current configuration to file", ""},
 	{"[q] - exit ctop", ""},
 }
@@ -134,6 +135,7 @@ func ContainerMenu() MenuFn {
 		items = append(items, menu.Item{Val: "stop", Label: "[s] stop"})
 		items = append(items, menu.Item{Val: "pause", Label: "[p] pause"})
 		items = append(items, menu.Item{Val: "restart", Label: "[r] restart"})
+		items = append(items, menu.Item{Val: "exec", Label: "[e] exec shell"})
 	}
 	if c.Meta["state"] == "exited" || c.Meta["state"] == "created" {
 		items = append(items, menu.Item{Val: "start", Label: "[s] start"})
@@ -182,6 +184,10 @@ func ContainerMenu() MenuFn {
 		})
 	}
 	if c.Meta["state"] == "running" {
+		ui.Handle("/sys/kbd/e", func(ui.Event) {
+			selected = "exec"
+			ui.StopLoop()
+		})
 		ui.Handle("/sys/kbd/r", func(ui.Event) {
 			selected = "restart"
 			ui.StopLoop()
@@ -210,6 +216,8 @@ func ContainerMenu() MenuFn {
 		nextMenu = SingleView
 	case "logs":
 		nextMenu = LogMenu
+	case "exec":
+		nextMenu = ExecShell
 	case "start":
 		nextMenu = Confirm(confirmTxt("start", c.GetMeta("name")), c.Start)
 	case "stop":
@@ -256,6 +264,24 @@ func LogMenu() MenuFn {
 	return nil
 }
 
+func ExecShell() MenuFn {
+	c := cursor.Selected()
+
+	if c == nil {
+		return nil
+	}
+
+	ui.DefaultEvtStream.ResetHandlers()
+	defer ui.DefaultEvtStream.ResetHandlers()
+
+	shell := config.Get("shell")
+	if err := c.Exec([]string{shell.Val, "-c", "echo '\033[0m' && clear && " + shell.Val}); err != nil {
+		log.Fatal(err)
+	}
+
+	return nil
+}
+
 // Create a confirmation dialog with a given description string and
 // func to perform if confirmed
 func Confirm(txt string, fn func()) MenuFn {