Pythonist - 2013년 5월 16일 1:20:02 오전
필요상 파이썬 스크립트를 두개로 쪼개고 파이프(pipe)로 연결해서 돌렸다. 이렇게 하면 좀 더 효율적으로 돌아갈거라 기대했다. 앞엣 스크립트가 끝나지 않아도 결과가 나오는대로 뒷 스크립트로 들어가서 좀 더 빠르게 돌겠지. 그런데 왠걸, 앞 스크립트가 끝날 때 까지 기다린다는. 이래서는 파이프로 쓰는 이유가 없지 하고 좀 자세히 알아봤다.
간단한 예제파일을 만들어봤다. a.py 가 다음과 같고,
import sys
import time
for line in sys.stdin:
time.sleep(2)
sys.stdout.write(line.upper())
sys.stdout.flush()
b.py 가 다음과 같을 때,
import sys
for line in sys.stdin:
sys.stdout.write(line.capitalize())
sys.stdout.flush()
다음처럼 실행하면,
$ python a.py < test.txt
HELLO
WORLD
PYTHON
윗 결과가 2초마다 한줄씩 출력된다. 근데, 이를 b.py 에 연결하면,
$ python a.py < test.txt | python b.py
Hello
World
Python
위 결과가 6초후에 한번에 출력된다. 이러면 안되잖아. 얘도 2초에 한줄씩 출력해야지.
이것저것 키워드를 만들어서 검색해봤는데 마땅한 결과를 찾지 못하고는 스택오버플로우에 질문 올리고는 트윗. 적당한 용어와 영작이 아닌 듯 하여 좀 걱정되긴 하지만.
쉘 파이프로 명령을 연결했을 때 앞 명령이 다 끝나야 뒤엣꺼가 시작되는건가? 파이프는 원래 물 흘러가듯이 돌아가는 거 아니였나? stackoverflow.com/questions/1656… 잘 아시는분 알려주세요.
— Hyungyong Kim (@yong27) 2013년 5월 15일
그랬더니, 홍민희님 바로 답변.
@yong27 파이프는 사실 그냥 프로세스 간 통신(IPC)이예요. 연관된 프로세스는 다 동시에 실행되죠.
— Hong Minhee (@hongminhee) 2013년 5월 15일
그러고는, 재야의 숨은 고수 falsetru님 이미 다 알고 있다는 듯,
@yong27 man python 해서 -u 부분을 참조하세요.
— Jeong-Min Lee (@falsetru) 2013년 5월 15일
파이썬 질문은 스택오버플로우보다 falsetru님한테 바로 물어보는게 더 빨라 보임.
"man python" 하면 다음처럼 나온다.
-u Force stdin, stdout and stderr to be totally unbuffered. On
systems where it matters, also put stdin, stdout and stderr in
binary mode. Note that there is internal buffering in xread‐
lines(), readlines() and file-object iterators ("for line in
sys.stdin") which is not influenced by this option. To work
around this, you will want to use "sys.stdin.readline()" inside
a "while 1:" loop.
"for line in sys.stdin" 구문은 생각처럼 그때그때 결과를 바로 뱉어내지 않고 파일의 끝(EOF, End Of File) 까지 기다리나보다. 따라서 기대한 것 처럼 결과를 얻으려면 for 문 대신 while 문과 sys.stdin.readline()을 써야 한다고.
즉 b.py 를
import sys
while True:
line = sys.stdin.readline()
if not line:
break
sys.stdout.write(line.capitalize())
sys.stdout.flush()
위처럼 for문 대신 while문으로 써야 한다. 내가 알고 있는 반복자 for 문은 이렇게 돌면 안되는데. 표준입력처리엔 다른 뭔가가 있나보다.
이윽고, 스택오버플로우에도 답변이 달렸다. while 문을 써야 한다고. 그래도 표준입력 반복자가 for 문에서 EOF까지 기다린다는 건 아무리 생각해도 불만이다. 이것 저것 찾다보니, 파이썬 3에서는 안그런다고. 오~ 실제 테스트해보니 파이썬 3에서는 for문도 바로바로 결과가 나온다.
"man python3" 하면,
-u Force the binary I/O layers of stdin, stdout and stderr to be unbuffered.
The text I/O layer will still be line-buffered.
이렇게만 나온다. stdin EOF 문제는 정리된 듯. 파이썬 3 을 써야하는 또 다른 이유!
몇몇분의 조언. 파이썬 2.x 의 "for line in stdin" 이 EOF까지 기다리는 건 아니라고 함. 버퍼링할뿐. text.txt 를 크게 만들고 sleep(0.1) 해서 돌려보니 내 맥북에서 약 800라인씩 버퍼링됨. 이정도면 그냥 무시하고 써도 될 수준인 것 같다. 내가 처음 이 문제를 발견했을 때는 버퍼링도 안하는 것 같았는데... 좀 더 면밀한 테스트 후 포스팅했어야.
Posted by Hyungyong Kim (yong27)
태그(들): EOF, python, python3, stdin