PIPE #2/3

지난 글에서는 한줄의 입력을 처리하는 hello.py 를 만들어 보았다.

여러줄이 들어올 땐 어떻게 할까?

쉽다. 여러번 읽으면 된다.

name = input()
print("Hello "+ name)

조금 깔끔한 출력을 위해 질문을 출력하는 print(“name?”)을 제거했다.

여기까지는 다를게 없고,

while True:
    name = input()
    print("Hello "+ name)

while 문을 추가해보았다.
실행해보면 한도 끝도없이 계속 질문을 한다. ctrl+c 를 눌러서 그만두자.

$ python3 hello_multi.py
KHS
Hello KHS
ABC
Hello ABC
123
Hello 123
^CTraceback (most recent call last):
  File "hello_multi.py", line 2, in <module>
    name = input()
KeyboardInterrupt
$

이 상태에서 echo와 파이프로 연결해보자.

$ echo "KHS" | python3 hello_multi.py
Hello KHS
Traceback (most recent call last):
  File "hello_multi.py", line 2, in <module>
    name = input()
EOFError: EOF when reading a line
$

첫 줄에서 KHS에 대한 반응이 Hello KHS 로 잘 나왔다.
그런데 오류가 났다.
잘보니 ctrl+c 를 눌렀을때와 같은 위치에서(입력을 기다리는 부분) 에러가 조금 다르다.

EOFError이고 오류내용은 “줄을 읽으려는데 EOF예요”

EOF

EOF는 End Of File의 약자인데, 파일의 끝이란 이야기다.

“파일이 무슨 상관이지?” 싶겠지만…

파일을 열어서 한줄씩 읽다보면 결국 끝이 오게 되고, 거기까지가 그 파일의 전체내용이다.

한줄씩 읽는 입장에서는 파일을 읽다가 마지막까지 가던,
표준입력을 읽다가 더이상 입력이 없던,
둘다 EOF인 것이다.

언어에 따라다르지만 표준입출력을 제공하는 언어는 isEOF() 라던지, null이 리턴되던지 해서 EOF를 알리는 방법을 제공하고 있다. 파이썬에서 input()함수는 EOFError를 던짐으로써 그 방법을 제공한다.

자 이제 에러가 안나게 해보자. 정확히 말하자면 EOF를 처리하는 것이다.

while True:
    try:
        name = input()
    except EOFError as eof:
        break
print("Hello "+ name)

EOFError가 잡히면 그냥 루프를 탈출하게 했다.
다시 해보자.

$ echo "KHS" | python3 hello_multi.py
Hello KHS
$

에러가 나지 않는다.

이제 진짜 여러 입력을 넣어보자.

한줄에 이름이 하나씩 쓰여있는 파일을 만들고 cat names.txt 를 파이프로 연결하면 된다.

KHS
sng2c
홍길동

cat 하면 파일의 내용이 표준출력으로 나오고,

$ cat names.txt
KHS
sng2c
홍길동
$

파이프로 연결하면, 여러줄의 입력이 하나씩 처리가 되면서

$ cat names.txt | python3 hello_multi.py
Hello KHS
Hello sng2c
Hello 홍길동
$

아름다운 결과를 볼 수 있다.

뽀나스

홍길동만 뽑아보겠다.

$ cat names.txt | python3 hello_multi.py | grep 홍길동
Hello sng2c
$

뽀나스2

키보드로 EOF를 넣어보자. 그냥 실행하면 무한정 물어보는건 알고 있을 것이다.
입력을 기다릴때 ctrl+d 를 눌러보자.

$ python3 hello_multi.py
KHS
Hello KHS
sng2c
Hello sng2c
^d
$

ctrl+c 를 눌렀을 때와 비교해보자.

PIPE #1/3

C, 파이썬, 펄, 루비, 자바등의 범용 프로그래밍 언어들은 표준입출력을 제공한다.

“표준입출력은 많이 들어봤는데, 나는 그거 안써” 라고 하는 분들을 위한 글이다.

프로그램은 시작과 끝이 있기 마련이고, 작동하는 중에 “내가 뭘 하고 있습니다” 라는 표시를 해야 사람이 그걸 보고 판단을 할 수 있게 된다.

물론 기능이 단순한 copy 같은 프로그램은 굳이 안보여줘도 되지만, 복잡한 기능을 가진 프로그램일 수록 지금 뭘하는지 보여주지 않으면 곤란한 상황이 많이 생긴다.

뻔한 얘기는 여기까지 하고.

표준입력 + 표준출력 + 표준에러 = 표준입출력

다르게 말해서 STDIN, STDOUT, STDERR 이다. 코드상에서는 이렇게 접하게 된다.

자바에서는 System.in, System.out, System.err 로 제공하고,
파이썬에서는 sys.stdin, sys.stdout, sys.stderr 로 제공한다.

자바는 Stream 객체이고, 파이썬에서는 file 오브젝트이지만 그건 중요치 않다.

각각이 뭘 주고 받는지 역할을 이해하는 것이 중요하다.

표준이라고 이름 붙인 것은 약속이기에

  • STDIN 은 프로그램의 입력
  • STDOUT 은 프로그램의 출력
  • STDERR 은 프로그램 오류나 경고등을 출력하기로 약속이 되어있다.

강제는 아니나 이를 지켰을 때 다른 프로그램과의 연동이 손쉬워 진다.

연동?

요즘은 주로 웹서비스나 모바일앱같은 GUI를 가진 프로그램을 작성하는 것이 큰 유행이기 때문에, 연동이라 함은

웹서비스 -> RESTful API
모바일앱 -> Custom URI Schema

등으로 연동을 하는 것이 상식일 것이다.

그런데 웹이나 모바일앱이 나오기 훨씬 이전부터 앱간의 연동의 필요성은 있었고,
이를 위해 표준입출력을 만든 것이다.

자주 쓰게 되는 명령어를 보자면…

ps -ef | grep java

ps 명령의 출력을 grep명령의 입력으로 전달( | )하고 있다. 그리고 grep의 출력이 화면에 나타난다.

요런 것이 OS에서 제공하는 앱간의 연동이자, 표준입출력간의 연결이자, IPC의 일종인 파이프 통신이다.
세가지 용어를 확 묶어버리니까 좀 마음이 편해졌다.

물론 프로그램끼리 통신하는 방법에는 파이프통신도 있고,

  • HTTP통신도 있고,
  • RPC도 있고,
  • COM(ActiveX)도 있고,
  • DB를 서로 뒤져보는 무식한 연동도 있고,
  • 공유메모리를 이용하는 방법도 있고,
  • 직접 실행시 인자를 집어넣어서 자식프로세스를 생성하는 것도 있겠다.

파이프통신은 쉽고, 유연하고, 편리하다

파이프 통신의 처리

파이썬으로만 살펴보겠다.

print("name?")
name = input()
print("Hello "+ name)

input 은 표준입력으로 글자를 받아들이는 역할을 한다.
print 는 표준출력으로 글자를 내보내는 역할을 한다.

잠깐, 아까 파이썬에는 sys.stdin 과 sys.stdout 으로 제공된다고 하지 않았나?
그것도 가능하고 이것도 가능하지만, 일단 엄청나게 자주 쓰이다 보니 쓰기 편한게 하나 더 있다고 생각하면 된다.
내부적으로는 같은 작동을 한다.

import sys
sys.stdout.write("name?\n")
name = sys.stdin.readline().rstrip()
sys.stdout.write("Hello "+name+"\n")
$ python3 hello.py
name?
sng2c
Hello sng2c
$ python3 hello_sys.py
name?
sng2c
Hello sng2c
$

나는 어릴때 C언어를 배울 때, 저것부터 시작했다. 표준입출력을 다루는 중요한 단계임에도 이게 얼마나 유용한 것인지 알지못했고, 초라한 프로그램이라고 생각했다.

name? 을 출력하고 멈춰있는 것을 볼수 있는데, 이게 표준입력을 read() 하기 위해 기다리고 있다. 이때에는 키보드를 타이핑하는 것이 표준입력이 된다.

이름을 써주면 “Hello “와 조합한 결과를 다시 표준출력으로 내보내게 된다.

echo 명령과 파이프로 연결해보자

echo 는 단순히 입력한 값을 “표준출력으로 내보낸다”.

$ echo 'KHS'
KHS

그리고 우리가 작성한 hello.py는 표준입력(아까는 키보드)를 한줄 받아서 인사를 하게 되어있다.
echo 의 출력을 | 로 연결을 해주면 hello.py 의 입력이 된다!!

$ echo 'KHS' | python3 hello.py
name?
Hello KHS
$

키보드 입력을 받기 위해 기다리지 않고,
표준입력으로 들어온 echo 의 ‘KHS’ 를 받아서 “Hello KHS”를 출력해냈다

첫번째(?) 파이프 통신을 축하한다.

전원주택을 스마트싱스로 자동화하기

전원주택으로 이사하고 나니 생각지도 못했던 불편함들이 있다.

밤에 늦게 오면 집 주위가 너무 어두워서 대문 열쇠구멍이 안보이고, 겨울에는 얼음이 껴서 열쇠도 안들어간다.

대문에 전기개폐기가 있어서, 인터폰으로는 집안에서 열어줄 수 있는데 집에 들어가는 입장에서는 어찌할 수가 없다.

와이프에게 한때 로봇제어회사도 다닌 사람이, 이대로 불편하게 사는 건 한심한 상황이라는 일침을 받고 홈오토메이션을 우리 집에 구현하기로 마음을 먹게 되었다.

더 보기 “전원주택을 스마트싱스로 자동화하기”