So I remember shelling out commands in Java, and it was quite a process.  It turns out it’s essentially the same for Go.

In interpreted scripting languages like python or ruby, it’s not really something you have to put any thought into.


So because bash is a shell you don’t have to do anything.

#!/bin/bash

ls -l

Ruby supports backticks.

#!/usr/bin/env ruby

`ls -l`

In Python, you can call os.system()

#!/usr/bin/env python
import os

if __name__ == "__main__":
    os.system("ls -l")

So what about real programming languages? Well, there’s a little more involved to shell something out if you’re expecting the output much like an actual shell.


Using Go, I wrote this to shell out a command.

package main

import (
    "os/exec"
    "fmt"
    "bufio"
    "os"
)

func main() {
    // create command and arguments
    cmd := "ls"
    args := []string { "-l" }
    command := exec.Command(cmd, args...)

    // connect to stdout and stderr
    stdOutReader, err := command.StdoutPipe()
    checkErr("Error creating stdout pipe", err)
    stdErrReader, err := command.StderrPipe()
    checkErr("Error creating stderr pipe", err)

    stdOutScanner := bufio.NewScanner(stdOutReader)
    stdErrScanner := bufio.NewScanner(stdErrReader)

    go func() {
        for stdOutScanner.Scan() {
            fmt.Printf("%s\n", stdOutScanner.Text())
        }
    }()
    go func() {
        for stdErrScanner.Scan() {
            fmt.Printf("%s\n", stdErrScanner.Text())
        }
    }()

    // run the command
    err = command.Start()
    checkErr("Error starting command", err)

    err = command.Wait()
    checkErr("Error waiting for command", err)
}

func checkErr(msg string, err error) {
    if err != nil {
        fmt.Fprintln(os.Stderr, msg, err)
        os.Exit(1)
    }
}

Here’s the output of that program compared to ls -l

 

 

 

 

 


In reality, when simply using ls, we could have probably ignored stderr.  If you want stderr messages though, the code above will provide that output as well.