Post

(python) subprocess, Popen

Note )

subprocess로 실행하는 바이너리가 출력→입력대기 순서로 진행할 때, read() -> write()를 주었는데도 제대로 동작하지 않는 경우가 있다. 이는 실행하는 바이너리 자체에서 입력 대기 하기 전에 출력을 파이썬으로 보내주어야 하는데, 입력 대기가 끝난 이후에 파이썬으로 출력 내용을 보내기 때문에 발생하는 현상으로 보인다. 그래서, 파이썬 단에서는 파이프를 새로 만들든 무슨 짓을 하든 해결이 안되고, 바이너리에서 flush해주는 수 밖에 없는 듯.

Popen

세부적인 제어는 subprocess.Popen()을 사용해야 한다.

stdio 연결
1
2
3
4
5
6
7
>>> p = subprocess.Popen(argv, stdin=subprocess.PIPE, stdout=subprocess.PIPE, env={"ENV\_":"value"})
>>> p
<subprocess.Popen object at 0x7feb23e9a210>
>>> p.stdin.write("\x00\x0a\x00\xff")
>>> p.communicate("stdin input string")
("stdout output!", None)   #[0] == stdout, [1] == stderr

단점은 stderrrb로 열려서 여기에 쓸 수가 없다. 이미 열려있는 파일의 access mode를 변경하는건 OS 단에서 이미 안되기 때문에 따로 파이프를 생성해 지정해주어야 한다. File object, File descriptor 둘 다 지정 가능하다. Note ) 사실 그냥 파이프를 따로 생성해서 지정해주는게 더 직관적으로 동작하는 것 같은데, 이렇게 따로 지정하는 경우 그 파이프 변수로 접근해야만 한다. p.stdin이 설정되는 경우는 subprocess.PIPE를 지정하는 경우 말고는 없기 때문에 p.stdin 따위로 접근하면 None을 반환한다. 파이프 한 개(r, w 한 쌍) 당 한 쪽 방향으로만 사용할 수 있다는 점 유의.[IPC] pipe 즉, 두 프로세스를 양방향으로 각자 r,w할 수 있도록 연결하려면 파이프가 두 개 필요.

cat 예제
  • Popen("cat")일 때 stdin 지정 안하면 interactive에서는 stdin이 겹쳐서 입력이 잘 안된다.
  • stdout=subprocess.PIPE적고 ` p.stdout.read()`하면 왜인지 안된다.
1
2
3
4
5
6
>>> r, w = os.pipe()
>>> p = subprocess.Popen("cat", stdin=subprocess.PIPE, stdout=w)
>>> p.stdin.write("1234")
>>> os.read(r, 4)
'1234'

1
2
3
4
5
6
7
8
>>> stdin\_r, stdin\_w = os.pipe()
>>> stdout\_r, stdout\_w = os.pipe()
>>> p = subprocess.Popen("cat", stdin=stdin\_r, stdout=stdout\_w)
>>> os.write(stdin\_w, "1234")
4
>>> os.read(stdout\_r, 4)
'1234'

간단하게 사용할 때

1
2
3
4
5
6
import subprocess

  

subprocess.run(['ls', '-al'], shell=True)

* WindowsError: [Error 2] The system cannot find the file specified가 발생하는 경우, Windows에서 .exe말고 다른 확장자는 파일 이름만 적어 실행하는 것이 안되기 때문이다. 확장자까지 입력해주어야 한다.

리턴값을 이용하고 싶은 경우
1
2
3
4
5
6
7
8
9
10
11
12
13
14
>>> import subprocess
>>> result = subprocess.run(["ls"], stdout=subprocess.PIPE)
>>> result
CompletedProcess(args=['ls'], returncode=0, stdout=b'\_\_init\_\_.py\n\_\_pycache\_\_\nclean.p
y\ncopy.py\nlog.py\nrun.py\ntest.log\n')

  
## 3.7.0부터는 capture\_ouput을 사용하는게 좋음.
if (sys.version\_info >= (3, 7, 0)):
result = subprocess.run(args, capture\_output=True, \*\*kwargs)

## 이건 예전 버전이라 사용하지 않는 것이 좋다.
## result = subprocess.check\_output(["ls"])

구식 OS

구식 OS에서는 subprocess 모듈이 없어 os 모듈을 사용해야한다.

1
2
3
4
5
6
7
import os

  

os.system ('ls -al | grep "user")
os.popen("command")

This post is licensed under CC BY 4.0 by the author.