HOWTO · Go
Go에서 셸 명령을 실행하는 방법
Go에서 os/exec로 외부 프로그램을 실행하고, 출력을 캡처하고, 오류를 처리하고, 인수를 전달하고, 명령의 작업 디렉터리를 설정하는 방법을 알아보세요. 셸 기능을 사용하려면 셸을 명시적으로 호출해야 합니다.
이 페이지의 내용
Go에서 외부 프로그램을 실행하려면 표준 라이브러리의 os/exec 패키지를 사용하세요. 이는 이름이 지정된 실행 파일을 직접 시작합니다. 셸을 암시적으로 실행하지는 않습니다. 따라서 파이프, 리디렉션, 변수 확장, 와일드카드와 같은 셸 기능은 적용되지 않습니다. 작업에 이러한 기능이 필요한 경우 셸을 명시적으로 호출하고 해당 스크립트를 인수로 전달하되, 신뢰할 수 없는 텍스트를 해당 스크립트에 넣지 않도록 주의하세요.
os/exec로 명령 실행
exec.Command로 명령을 만든 다음 Output과 같은 메서드를 호출하여 명령을 시작하고 표준 출력을 수집하세요. 첫 번째 인수는 실행 파일의 이름을 지정하고, 나머지 인수는 해당 프로그램에 전달됩니다. 이 완전한 프로그램은 인수 하나를 가진 자식 프로세스로 자기 자신을 시작합니다. 자식은 고정된 줄을 출력하고, 부모는 해당 표준 출력을 캡처하여 자신의 표준 출력에 씁니다. 프로그램은 부모 프로세스를 통해 자식의 줄을 출력합니다.
package main
import (
"fmt"
"os"
"os/exec"
)
func main() {
if len(os.Args) > 1 && os.Args[1] == "child" {
fmt.Println("hello from child")
return
}
cmd := exec.Command(os.Args[0], "child")
output, err := cmd.Output()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Print(string(output))
}
hello from child
여기서 os.Args[0]은 현재 실행 파일을 식별하므로, 이 예제는 별도의 유틸리티가 설치되어 있는지에 의존하지 않습니다. 애플리케이션에서는 이를 git나 go와 같이 필요한 실행 파일로 교체하고, 해당 프로그램의 인수를 별도로 제공하세요.
출력 캡처 및 오류 처리
결과를 사용하기 전에 Output 또는 CombinedOutput가 반환한 오류를 확인하세요. 찾을 수 없는 실행 파일과 같이 프로그램을 시작하지 못하는 것은, 프로그램이 시작되어 0이 아닌 상태로 종료되는 것과 다릅니다. 후자의 경우 오류는 대개 *exec.ExitError입니다. 종료 상태가 중요할 때 이를 검사하세요. Cmd 타입을 사용하면 스트림을 직접 연결할 수도 있습니다. 출력을 바이트 슬라이스에 수집하는 대신 그곳으로 보내려면 cmd.Stdout 또는 cmd.Stderr를 라이터(writer)로 설정하세요.
프로그램에 필요한 데이터가 표준 출력인 경우 Output을 사용하세요. 반환된 바이트에 표준 오류도 필요한 경우 CombinedOutput을 사용하세요. 두 스트림이 결합되므로, 그 상대적 순서는 별도의 스트림을 재구성하는 신뢰할 수 있는 방법이 아닙니다. 진단을 별도로 유지해야 하는 경우 cmd.Stdout와 cmd.Stderr에 별도의 라이터를 할당하세요. 오류는 호출자가 재시도할지, 반환할지, 계속할지를 결정할 수 있는 지점에서 처리하고 보고하세요. 오류를 출력한 다음 명령이 성공한 것처럼 취급하면 실패한 작업을 숨길 수 있습니다.
명령에 인수 전달
각 인수를 실행 파일 이름 뒤에 자체 문자열로 전달하세요. 예를 들어 exec.Command("grep", "-n", pattern, filename)와 같습니다. Go는 이러한 값을 셸 명령으로 결합하거나 공백, 따옴표, 파이프, 리디렉션 문자를 대신 해석하지 않습니다. 공백이 포함된 파일 이름은 하나의 문자열로 제공될 때 하나의 인수로 유지됩니다. 이는 셸 구문 분석을 피하지만, 호출된 프로그램에 대한 입력을 검증하지는 않습니다. 인수는 해당 프로그램에 여전히 특별한 의미를 가질 수 있으므로, 명령 자체의 인터페이스에 따라 값을 검증하세요.
입력 및 출력 스트림의 경우 cmd.Stdin에 io.Reader를, cmd.Stdout 또는 cmd.Stderr에 라이터를 할당하고, 그런 다음 Run을 호출하세요. 위의 예제 실행 파일은 짧은 결과만 캡처하면 되므로 Output을 사용합니다. 스트림을 연결하고 표준 출력을 반환값으로 수집할 필요가 없을 때 Run을 사용하세요. 실행 파일은 프로세스에서 사용할 수 있어야 하며, 그 옵션은 운영 체제마다 다를 수 있습니다.
다른 디렉터리에서 명령 실행
명령의 작업 디렉터리를 선택하려면 명령을 시작하기 전에 cmd.Dir를 설정하세요. 예를 들어, 명령을 구성한 후 cmd.Dir = projectDir를 설정한 다음 Output, CombinedOutput 또는 Run을 호출하세요. 디렉터리가 존재해야 하며 프로세스가 액세스할 수 있어야 합니다. 상대 cmd.Dir는 Go 프로세스의 현재 작업 디렉터리를 기준으로 해석됩니다. 호출자의 시작 디렉터리가 달라질 수 있는 경우 절대 경로를 사용하세요. 이 설정은 자식 프로세스의 디렉터리를 변경하며 Go 프로세스 자체의 작업 디렉터리는 변경하지 않습니다.