서브 프로세스에 문자열을 어떻게 전달합니까? (stdin 인수 사용)


280

내가 다음을 수행하면 :

import subprocess
from cStringIO import StringIO
subprocess.Popen(['grep','f'],stdout=subprocess.PIPE,stdin=StringIO('one\ntwo\nthree\nfour\nfive\nsix\n')).communicate()[0]

나는 얻다:

Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "/build/toolchain/mac32/python-2.4.3/lib/python2.4/subprocess.py", line 533, in __init__
    (p2cread, p2cwrite,
  File "/build/toolchain/mac32/python-2.4.3/lib/python2.4/subprocess.py", line 830, in _get_handles
    p2cread = stdin.fileno()
AttributeError: 'cStringIO.StringI' object has no attribute 'fileno'

분명히 cStringIO.StringIO 객체는 하위 프로세스에 맞게 파일 덕에 충분히 근접하지 않습니다. 이 문제를 어떻게 해결합니까?


3
이것을 삭제하면서 내 대답을 논쟁하는 대신, 나는 그것을 주석으로 추가하고 있습니다 ... 권장 독서 : subprocess에 대한 Doug Hellmann의 Python 모듈 주간 블로그 게시물 .
— Daryl Spitzer

3
블로그 게시물에 여러 오류가 포함되어 있습니다 ( 예 : 첫 번째 코드 예 :call(['ls', '-1'], shell=True) 올바르지 않음). 대신 하위 프로세스의 태그 설명에서 일반적인 질문 을 읽는 것이 좋습니다 . 특히, 인수가 시퀀스 일 때 왜 subprocess.Popen이 작동하지 않습니까? 왜 call(['ls', '-1'], shell=True)잘못되었는지 설명합니다 . 블로그 게시물 아래에 댓글을 남기는 것을 기억하지만 어떤 이유로 든 댓글이 표시되지 않습니다.
— jfs

최신 subprocess.run내용은 stackoverflow.com/questions/48752152/…
— Boris

답변:


326

Popen.communicate() 선적 서류 비치:

프로세스의 stdin으로 데이터를 보내려면 stdin = PIPE를 사용하여 Popen 오브젝트를 작성해야합니다. 마찬가지로 결과 튜플에서 None 이외의 값을 얻으려면 stdout = PIPE 및 / 또는 stderr = PIPE도 제공해야합니다.

os.popen * 교체

    pipe = os.popen(cmd, 'w', bufsize)
    # ==>
    pipe = Popen(cmd, shell=True, bufsize=bufsize, stdin=PIPE).stdin

경고 자식 프로세스를 채우고 차단하는 다른 OS 파이프 버퍼로 인한 교착 상태를 피하려면 stdin.write (), stdout.read () 또는 stderr.read () 대신 communi ()을 사용하십시오.

따라서 다음과 같이 예제를 작성할 수 있습니다.

from subprocess import Popen, PIPE, STDOUT

p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)    
grep_stdout = p.communicate(input=b'one\ntwo\nthree\nfour\nfive\nsix\n')[0]
print(grep_stdout.decode())
# -> four
# -> five
# ->

현재 Python 3 버전에서는을 사용 subprocess.run하여 입력을 문자열로 외부 명령에 전달하고 종료 상태를 얻고 출력을 문자열로 다시 한 번의 호출로 얻을 수 있습니다.

#!/usr/bin/env python3
from subprocess import run, PIPE

p = run(['grep', 'f'], stdout=PIPE,
        input='one\ntwo\nthree\nfour\nfive\nsix\n', encoding='ascii')
print(p.returncode)
# -> 0
print(p.stdout)
# -> four
# -> five
# -> 

3
나는 그 경고를 놓쳤다. 나는 (나는 대답이 있다고 생각하더라도) 물어 봤다 니 기쁘다.
— Daryl Spitzer

11
이것은 좋은 해결책이 아닙니다. 특히 전체 stdout이 도착할 때까지 기다려야하므로 p.stdout.readline 출력을 비동기 적으로 처리 할 수 ​​없습니다. 또한 메모리 비효율적입니다.
— OTZ

7
@OTZ 더 나은 솔루션은 무엇입니까?
— Nick T

11
@Nick T : " 더 나은 "은 상황에 따라 다릅니다. 뉴턴의 법칙은 적용 가능한 영역에 적합하지만 GPS를 설계하려면 특별한 상대성이 필요합니다. python의 하위 프로세스에서 비 블로킹 읽기를 참조하십시오 .
— jfs

9
그러나 통신 참고 사항 : "데이터 크기가 크거나 무제한 인 경우이 방법을 사용하지 마십시오"
— Owen

44

이 해결 방법을 찾았습니다.

>>> p = subprocess.Popen(['grep','f'],stdout=subprocess.PIPE,stdin=subprocess.PIPE)
>>> p.stdin.write(b'one\ntwo\nthree\nfour\nfive\nsix\n') #expects a bytes type object
>>> p.communicate()[0]
'four\nfive\n'
>>> p.stdin.close()

더 좋은 것이 있습니까?


25
@Moe : stdin.write()사용을 권장하지 않습니다 p.communicate(). 내 대답을 참조하십시오.
— jfs

11
하위 프로세스 문서에 따라 : 경고-다른 OS 파이프 버퍼가 자식 프로세스를 채우고 차단하여 교착 상태를 피하려면 .stdin.write, .stdout.read 또는 .stderr.read 대신 communi ()을 사용하십시오.
— Jason Mock

1
stdout / err이 채워지지 않을 것이라고 확신하고 (예를 들어 파일로 이동하거나 다른 스레드가 그것을 먹는 중) 자신감이 있고 무제한의 데이터가있는 경우이 방법이 좋은 방법이라고 생각합니다 stdin으로 보내질 것입니다.
— Lucretiel

1
특히, 이렇게하면 stdin이 닫히므로 하위 프로세스가 입력을 영원히 소비하는 프로세스 인 경우 communicate파이프가 닫히고 프로세스가 정상적으로 종료됩니다.
— Lucretiel

프로세스가 소비 영원히 stdin을 경우 @Lucretiel, 다음 아마도 여전히 쓰기 표준 출력 영원히, 우리는 완전히 만능 다른 기술을 필요로 줄 수 있습니다 (수없는 read()것이, 같은로부터 communicate()도 인수없이 않습니다).
— Charles Duffy

25

아무도 파이프를 만들 것을 제안한 사람이 조금 놀랐습니다. 이것은 하위 프로세스의 stdin에 문자열을 전달하는 가장 간단한 방법입니다.

read, write = os.pipe()
os.write(write, "stdin input here")
os.close(write)

subprocess.check_call(['your-command'], stdin=read)

2
os과 subprocess문서 모두 당신이 전을 통해 후자를 선호한다는 것을 동의합니다. 이것은 (약간 간결한) 표준 대체가있는 레거시 솔루션입니다. 승인 된 답변은 관련 문서를 인용합니다.
— tripleee

1
그게 맞는지 확실하지 않습니다. 인용 된 문서에는 프로세스에서 만든 파이프를 사용하기 어려운 이유가 나와 있지만이 솔루션에서는 파이프를 만들어서 전달합니다. 프로세스가 이미 시작된 후 파이프를 관리하는 데 필요한 교착 상태 문제를 피할 수 있다고 생각합니다.
— 그레이엄 크리스텐슨

os.popen은 하위 프로세스를 위해 사용되지 않습니다
— hd1

2
-1 : 교착 상태가 발생하여 데이터가 손실 될 수 있습니다. 이 기능은 이미 서브 프로세스 모듈에서 제공합니다. 잘못 재 구현하는 대신 사용하십시오 (OS 파이프 버퍼보다 ​​큰 값을 쓰십시오)
— jfs

당신은 최고의 좋은 사람을받을 자격이 간단하고 영리한 솔루션을 주셔서 감사합니다
— 펠리페 Buccioni에게

21

Python 3.4 이상을 사용하는 경우 아름다운 해결책이 있습니다. bytes 인수를 허용하는 input인수 대신 인수를 사용하십시오 stdin.

output = subprocess.check_output(
    ["sed", "s/foo/bar/"],
    input=b"foo",
)

이것은 작동 check_output하고 run있지만, call또는 check_call어떤 이유.


5
@vidstige 네 말이 맞아, 이상해. 나는 이것을 파이썬 버그로 제출하는 것을 고려할 것인데, 왜 인수를 check_output가져야 하는지에 대한 정당한 이유는 input보이지 않지만 그렇지 않다 call.
— Flimm

2
이것은 Python 3.4+에 가장 적합한 답변입니다 (Python 3.6에서 사용). 실제로 작동하지 check_call않지만 작동합니다 run. 설명서에 따라 인코딩 인수를 전달하는 한 input = string에서도 작동합니다.
— Nikolaos Georgiou

13

나는 python3을 사용하고 있으며 stdin에 전달하기 전에 문자열을 인코딩해야한다는 것을 알았습니다.

p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=PIPE)
out, err = p.communicate(input='one\ntwo\nthree\nfour\nfive\nsix\n'.encode())
print(out)

5
입력을 구체적으로 인코딩 할 필요는 없으며 바이트와 같은 객체 (예 :)를 원합니다 b'something'. 또한 바이트로 err 및 out을 리턴합니다. 이것을 피하려면으로 전달할 universal_newlines=True수 Popen있습니다. 그런 다음 입력을 str으로 받아들이고 err / out을 str으로 리턴합니다.
— 6

2
그러나 universal_newlines=True개행을 시스템에 맞게 변환 할 것입니다.
— Nacht-Reinstate Monica

1
Python 3을 사용 하는 경우 더 편리한 솔루션에 대한 내 대답 을 참조하십시오 .
— Flimm

12

분명히 cStringIO.StringIO 객체는 하위 프로세스에 적합하도록 파일 덕에 충분히 근접하지 않습니다.

난 두려워하지. 파이프는 저수준 OS 개념이므로 OS 수준 파일 설명 자로 표시되는 파일 개체가 절대적으로 필요합니다. 해결 방법이 맞습니다.


7
from subprocess import Popen, PIPE
from tempfile import SpooledTemporaryFile as tempfile
f = tempfile()
f.write('one\ntwo\nthree\nfour\nfive\nsix\n')
f.seek(0)
print Popen(['/bin/grep','f'],stdout=PIPE,stdin=f).stdout.read()
f.close()

3
fyi, tempfile.SpooledTemporaryFile .__ doc__의 말 : 임시 파일 래퍼로, 특정 크기를 초과하거나 fileno가 필요할 때 StringIO에서 실제 파일로 전환하는 데 특화되어 있습니다.
— Doug F

5

부모 프로세스가 버퍼를 버퍼링하기 때문에 너무 Popen.communicate(input=s)클 경우 문제 를 일으킬 수 있음을 주의하십시오.s 자식 하위 프로세스 를 forking 하기 전에 , 해당 시점에서 최소한 "두 번"사용 된 메모리가 필요하기 때문입니다. 링크 된 문서는 여기에 있습니다 ). 내 특별한 경우에는 s처음에 완전히 확장 된 다음 생성 된 생성기가 있었 stdin으므로 자식 프로세스가 생성되기 직전에 부모 프로세스가 엄청 났으며 포크 할 메모리가 없었습니다.

File "/opt/local/stow/python-2.7.2/lib/python2.7/subprocess.py", line 1130, in _execute_child self.pid = os.fork() OSError: [Errno 12] Cannot allocate memory


5
"""
Ex: Dialog (2-way) with a Popen()
"""

p = subprocess.Popen('Your Command Here',
                 stdout=subprocess.PIPE,
                 stderr=subprocess.STDOUT,
                 stdin=PIPE,
                 shell=True,
                 bufsize=0)
p.stdin.write('START\n')
out = p.stdout.readline()
while out:
  line = out
  line = line.rstrip("\n")

  if "WHATEVER1" in line:
      pr = 1
      p.stdin.write('DO 1\n')
      out = p.stdout.readline()
      continue

  if "WHATEVER2" in line:
      pr = 2
      p.stdin.write('DO 2\n')
      out = p.stdout.readline()
      continue
"""
..........
"""

out = p.stdout.readline()

p.wait()

4
shell=True타당한 이유없이 일반적으로 사용 되기 때문에 이것은 대중적인 질문입니다 . 쉘이 명령과 인수를 토큰으로 나누지 만 다른 방법으로 제공하지 않는 Popen(['cmd', 'with', 'args'])것보다 결정적으로 더 나은 상황이 많이 있음을 지적하겠습니다 Popen('cmd with args', shell=True). 상당한 양의 복잡성을 추가하면서 공격 표면을 공격하는 데 유용합니다.
— tripleee

2
p = Popen(['grep', 'f'], stdout=PIPE, stdin=PIPE, stderr=STDOUT)    
p.stdin.write('one\n')
time.sleep(0.5)
p.stdin.write('two\n')
time.sleep(0.5)
p.stdin.write('three\n')
time.sleep(0.5)
testresult = p.communicate()[0]
time.sleep(0.5)
print(testresult)

1

Python 3.7 이상에서는 다음을 수행하십시오.

my_data = "whatever you want\nshould match this f"
subprocess.run(["grep", "f"], text=True, input=my_data)

capture_output=True명령을 문자열로 실행 한 결과를 얻기 위해 추가하고 싶을 것입니다 .

파이썬의 이전 버전에서 교체 text=True로 universal_newlines=True:

subprocess.run(["grep", "f"], universal_newlines=True, input=my_data)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.