서브 프로세스에 문자열을 어떻게 전달합니까? (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
ossubprocess문서 모두 당신이 전을 통해 후자를 선호한다는 것을 동의합니다. 이것은 (약간 간결한) 표준 대체가있는 레거시 솔루션입니다. 승인 된 답변은 관련 문서를 인용합니다.
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=TruePopen있습니다. 그런 다음 입력을 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=Trueuniversal_newlines=True:

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