
subprocess 모듈에서 특히 Popen class 를 주로 다룬다.
https://docs.python.org/ko/3.9/library/subprocess.html#subprocess.Popen


proc=subprocess.Popen(CMD,shell=True,executable="/bin/bash")
예제1.
pstree -ps $$ 명령 수행subprocess.Popen("pstree -ps $$",shell=True,executable="bin/bash")으로 pstree실행
stdout=subprocess.PIPE: stdout 활용sys.stdout으로 설정했는데, 그럼 바로 내 쉘에 보이지만 이를 문자열로 잡아서 활용할 수는 없다.stdout = subprocess.PIPEtext=True(stdout을 str로 처리, 이 옵션이 False시 'b'(바이너리)로 처리됨)

| 유효한 값 | Description |
|---|---|
| PIPE | 자식에 대한 새 파이프를 만들어낸다. ex) stdout=subprocess.PIPE |
| DEVNULL | os.devnull이 사용될 것임 말그대로 stdout 등이 필요없을 때 혹은 출력하고 싶지 않을 때 사용 ./command >> /dev/null과 동일 |
bufsizebufsize: 버퍼링 할 사이즈stdin/stdout/stderr 파이프 파일 객체를 만들 때 open() 함수에 해당 인자로 제공된다.bufsize=0: 버퍼링 되지 않음bufsize=1: 줄 버퍼링 의미(universal_newlines=True 인 경우, 텍스트 모드에서만 사용할 수 있다.)bufsize=다른 양수 값: 대략 그 크기의 버퍼를bufsize=음수: 시스템 기본 값 io.DEFAULT_BUFFER_SIZE가 사용됨 의미executableexecutable: 실행할 대체 프로그램 지정shell=False일 때, executable은 args에 의해 지정된 프로그램을 대체shell=True이면, POSIX에서 executable 인자는 기본 /bin/sh의 대체 셸 지정
stdin,stdout,stderr의 파일 객체는 지정된 encoding과 errors 또는 io.TextIOWrapper 기본 값을 사용하여 텍스트 모드로 열린다.universal_newlines인자는 text와 동등하고 이전 버전과의 호환성을 위해 제공된다.python 3.6.ximport subprocess
subprocess.call('/bin/bash -c "$GREPDB"', shell=True, env={'GREPDB': 'echo 123'})

1. suprocess.Popen Class의 instance create
proc=subprocess.Popen(args...)
proc.poll()
Popen.poll()# Create Popen instance
proc=subprocess.Popen(...)
var=proc.poll()
print(var)
| return value | description |
|---|---|
None | instance 실행 중 |
return code | instance가 종료되었고 리턴코드를 가져옴 |
Popen.wait()# Create Popen instance
proc=subprocess.Popen(...)
try:
ret=proc.wait(timeout=None) # Wait...
except subprocess.TimeoutExpired:
Exception occured!
else:
print(ret)
| input args | description |
|---|---|
| timeout=None | 이 함수가 기다릴 time 지정 |
| return value | description |
|---|---|
return code | timeout내 종료된 Popen instance가 return 하는 값 |
| except | description |
|---|---|
| TimeoutExpired | stdout=PIPE나 stderr=PIPE를 사용하고 자식 프로세스가 파이프에 너무 많은 출력을 보내 OS 파이프 버퍼가 더 많은 데이터를 받아들일 때까지 기다리느라 블록하면 교착 상태에 빠진다. 파이프를 사용할 때 이를 피하려면 Popen.communicate()를 사용 해라. |
Popen.communicate()# Create Popen instance
proc=subprocess.Popen(...)
try:
ret, err = proc.commiunicate(input=None, timeout=10.0f)
except subprocess.TimeoutExpired:
Exception occured!
else:
...
stdin=PIPE를 사용해 Popen 객체를 만들어야함)| input args | description |
|---|---|
| input=None | 자식 프로세스로 전송될 데이터, 자식으로 데이터를 보내지 않으면 None 설정스트림이 텍스트 모드로 열렸으면, input 은 문자열이어야 함스트림이 텍스트 모드로 열리지 않았으면, input은 바이트열 |
| timeout=None | 이 함수가 기다릴 time 지정 |
| return value | description |
|---|---|
(stdout_data, stderr_data) | 스트림이 텍스트 모드로 열렸으면 데이터는 문자열 스트림이 비 텍스트 모드로 열렸으면 데이터는 바이트열 결과 튜플에서 None이외의 값을 얻으려면 Popen 객체 생성 시,stdout=PIPE, stderr=PIPE로 생성해야함 |
| exception | description |
|---|---|
| TimeoutExpired | timeout내에 Popen instance가 종료되지 않았을 때 발생하는 익셉션stdout=PIPE나 stderr=PIPE를 사용하고 자식 프로세스가 파이프에 너무 많은 출력을 보내 OS 파이프 버퍼가 더 많은 데이터를 받아들일 때까지 기다리느라 블록하면 교착 상태에 빠진다. 파이프를 사용할 때 이를 피하려면 Popen.communicate()를 사용 해라. 이 예외를 잡고 통신을 다시 시도해도 출력이 손실되지 않는다. 시간제한이 만료되면 자식 프로세스를 죽이지 않는다. 올바르게 하려면 프로세스를 죽이고 통신을 완료해야한다. ![]() |
cmd = "ps -al"
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
cmd_output, smd_stderr = p.communicate()
cmd_output = cmd_output.decode("utf-8")
cmd_stderr = cmd_stderr.decode("utf-8")
if p.returncode or cmd_stderr:
# Error Catch
print(cmd_output)
print(std_err)
Popen.send_signal(signal)Popen.terminate()TerminatedProcess()를 보낸다.Popen.kill()kill()은 terminate()의 별칭Popen.argsPopen.stdinstdin=PIPE면 this attribute는 open()이 반환한 쓰기 가능한 스트림 객체encoding, errors인자가 지정되었거나 universal_newlines 인자가 True이면, 스트림은 텍스트 스트림이고, 그렇지 않으면 바이트 스트림이다.stdin!=PIPE이면, this attribute는 NonePopen.stdoutstdout=PIPE 이면, this attribute는 open()이 반환한 읽기 가능한 스트림 객체encoding, errors 인자가 지정되었거나 universal_newlines 인자가 True 이면, 스트림은 텍스트 스트림이고, 그렇지 않으면 바이트 스트림이다.stdout!=PIPE면, this attribute는 NonePopen.stderrstderr=PIPE 이면, this attribute는 open()이 반환한 읽기 가능한 스트림 객체이다.encoing, errors 인자가 지정되었거나 universal_newlines 인자가 True이면, 스트림은 텍스트 스트림이고, 그렇지 않으면 바이트 스트림이다.stderr!=PIPE 이면, this attribute는 None다른 OS PIPE 버퍼가 차고 자식 프로세스를 blocking하는 것으로 인한 교착 상태를 피하려면 .stdin.write, stdout.read, stderr.read 대신 communicate()를 사용하라
Popen.pidTrue로 설정하면, 이것은 생성된 셸의 프로세스 IDPopen.returncodepoll()과 wait() 그리고 communicate()에 의해 설정된 자식 반환 코드None 값은 프로세스가 아직 종료되지 않았음을 나타냄-N은 자식이 시그널 N에 의해 종료되었음 나타냄(POSIX)


#!/usr/bin/env python3
import subprocess
import signal
import errno
def subprocess_setup():
# Python installs a SIGPIPE handler by default. This is usually not what
# non-Python subprocesses expect.
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
class CmdError(RuntimeError):
def __init__(self, command, msg=None):
self.command = command
self.msg = msg
def __str__(self):
if not isinstance(self.command, str):
cmd = subprocess.list2cmdline(self.command)
else:
cmd = self.command
msg = "Execution of '%s' failed" % cmd
if self.msg:
msg += ': %s' % self.msg
return msg
class NotFoundError(CmdError):
def __str__(self):
return CmdError.__str__(self) + ": command not found"
class ExecutionError(CmdError):
def __init__(self, command, exitcode, stdout = None, stderr = None):
CmdError.__init__(self, command)
self.exitcode = exitcode
self.stdout = stdout
self.stderr = stderr
self.extra_message = None
def __str__(self):
message = ""
if self.stderr:
message += self.stderr
if self.stdout:
message += self.stdout
if message:
message = ":\n" + message
return (CmdError.__str__(self) +
" with exit code %s" % self.exitcode + message + (self.extra_message or ""))
class Popen(subprocess.Popen):
defaults = {
"close_fds": True,
"preexec_fn": subprocess_setup,
"stdout": subprocess.PIPE,
"stderr": subprocess.PIPE,
"stdin": subprocess.PIPE,
"shell": False,
}
def __init__(self, *args, **kwargs):
options = dict(self.defaults)
options.update(kwargs)
subprocess.Popen.__init__(self, *args, **options)
def run(cmd, input=None, **options):
if isinstance(cmd, str) and not "shell" in options:
options["shell"] = True
try:
pipe = Popen(cmd, **options)
except OSError as exc:
if exc.errno == 2:
raise NotFoundError(cmd)
else:
raise CmdError(cmd, exc)
stdout, stderr = pipe.communicate(input)
if not stdout is None:
stdout = stdout.decode("utf-8")
if not stderr is None:
stderr = stderr.decode("utf-8")
if pipe.returncode != 0:
raise ExecutionError(cmd, pipe.returncode, stdout, stderr)
return stdout, stderr
if __name__ == '__main__':
stdout, stderr = run('echo hello')
print(stdout)
print(stderr)
try:
stdout, stderr = run('dummy')
print(stdout)
print(stderr)
except Exception as exc:
print(exc)

import os
print(os.environ)
# os.environ에서 Dictionary 형태로 'Key': Value, 'Key': [Value1, Value2...] 로 되어있음
export PATH=/update/dir:$PATH 와 똑같은 짓을 하려면import os
existing_PATH=os.getenv("PATH") # 기존의 $PATH 값을 가져온다.
new_path="/update/dir:"+existing_path # /update/dir:$PATH
os.environ["PATH"] = new_path
print(os.getenv("PATH")) # 확인