func GetExitCode(err error) (int, error) {
	if exiterr, ok := errors.Unwrap(err).(errors.IErrorCode); ok {
		return exiterr.ExitStatus()
	}
	if exiterr, ok := errors.Unwrap(err).(*exec.ExitError); ok {
		status := exiterr.Sys().(syscall.WaitStatus)
		return status.ExitStatus(), nil
	}
	if exiterr, ok := errors.Unwrap(err).(errors.MultiError); ok {
		for _, err := range exiterr.Errors {
			exitCode, exitCodeErr := GetExitCode(err)
			if exitCodeErr == nil {
				return exitCode, nil
			}
		}
	}
	return 0, err
}
Return the exit code of a command. If the error does not implement errors.IErrorCode or is not an exec.ExitError
or errors.MultiError type, the error is returned.
package shell
import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"reflect"
	"strings"
	"syscall"
	"github.com/gruntwork-io/terragrunt/errors"
	"github.com/gruntwork-io/terragrunt/options"
	"github.com/gruntwork-io/terragrunt/util"
)
var terraformCommandsThatNeedPty = []string{
	"console",
}
func RunTerraformCommand(terragruntOptions *options.TerragruntOptions, args ...string) error {
	_, err := RunShellCommandWithOutput(terragruntOptions, "", false, isTerraformCommandThatNeedsPty(args[0]), terragruntOptions.TerraformPath, args...)
	return err
}
func RunShellCommand(terragruntOptions *options.TerragruntOptions, command string, args ...string) error {
	_, err := RunShellCommandWithOutput(terragruntOptions, "", false, false, command, args...)
	return err
}
func RunTerraformCommandWithOutput(terragruntOptions *options.TerragruntOptions, args ...string) (*CmdOutput, error) {
	return RunShellCommandWithOutput(terragruntOptions, "", false, isTerraformCommandThatNeedsPty(args[0]), terragruntOptions.TerraformPath, args...)
}
func RunShellCommandWithOutput(
	terragruntOptions *options.TerragruntOptions,
	workingDir string,
	suppressStdout bool,
	allocatePseudoTty bool,
	command string,
	args ...string,
) (*CmdOutput, error) {
	terragruntOptions.Logger.Printf("Running command: %s %s", command, strings.Join(args, " "))
	if suppressStdout {
		terragruntOptions.Logger.Printf("Command output will be suppressed.")
	}
	var stdoutBuf bytes.Buffer
	var stderrBuf bytes.Buffer
	cmd := exec.Command(command, args...)
	cmd.Env = toEnvVarsList(terragruntOptions.Env)
	var errWriter = terragruntOptions.ErrWriter
	var outWriter = terragruntOptions.Writer
	if !reflect.DeepEqual(terragruntOptions.TerraformCliArgs, args) {
		outWriter = terragruntOptions.ErrWriter
	}
	if workingDir == "" {
		cmd.Dir = terragruntOptions.WorkingDir
	} else {
		cmd.Dir = workingDir
	}
	cmdStderr := io.MultiWriter(errWriter, &stderrBuf)
	var cmdStdout io.Writer
	if !suppressStdout {
		cmdStdout = io.MultiWriter(outWriter, &stdoutBuf)
	} else {
		cmdStdout = io.MultiWriter(&stdoutBuf)
	}
	if allocatePseudoTty {
		if err := runCommandWithPTTY(terragruntOptions, cmd, cmdStdout, cmdStderr); err != nil {
			return nil, err
		}
	} else {
		cmd.Stdin = os.Stdin
		cmd.Stdout = cmdStdout
		cmd.Stderr = cmdStderr
		if err := cmd.Start(); err != nil {
			return nil, errors.WithStackTrace(err)
		}
	}
	cmdChannel := make(chan error)  
	signalChannel := NewSignalsForwarder(forwardSignals, cmd, terragruntOptions.Logger, cmdChannel)
	defer signalChannel.Close()
	err := cmd.Wait()
	cmdChannel <- err
	cmdOutput := CmdOutput{
		Stdout: stdoutBuf.String(),
		Stderr: stderrBuf.String(),
	}
	return &cmdOutput, errors.WithStackTrace(err)
}
func toEnvVarsList(envVarsAsMap map[string]string) []string {
	envVarsAsList := []string{}
	for key, value := range envVarsAsMap {
		envVarsAsList = append(envVarsAsList, fmt.Sprintf("%s=%s", key, value))
	}
	return envVarsAsList
}
func isTerraformCommandThatNeedsPty(command string) bool {
	return util.ListContainsElement(terraformCommandsThatNeedPty, command)
}