2009-10-09 12 views
5

파이썬의 서브 프로세스에 문제가 있습니다. 팝업 방법.자식 프로세스가 종료 될 때까지 subprocess.Popen이 대기하지 않는 이유는 무엇입니까?

다음은 문제를 보여주는 테스트 스크립트입니다. 그것은 리눅스 박스에서 실행되고 있습니다.

success: 100000 

하지만 때로는 실패의 경우, 즉시 삽입 후 선택 0 행하지만 이후에 표시되는지

failure: 0 
after sleeping the count is 100000 

주의 :

#!/usr/bin/env python 
import subprocess 
import time 

def run(cmd): 
    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) 
    return p 

### START MAIN 
# copy some rows from a source table to a destination table 
# note that the destination table is empty when this script is run 
cmd = 'mysql -u ve --skip-column-names --batch --execute="insert into destination (select * from source limit 100000)" test' 
run(cmd) 

# check to see how many rows exist in the destination table 
cmd = 'mysql -u ve --skip-column-names --batch --execute="select count(*) from destination" test' 
process = run(cmd) 
count = (int(process.communicate()[0][:-1])) 

# if subprocess.Popen() waited for the child to terminate than count should be 
# greater than 0 
if count > 0: 
    print "success: " + str(count) 
else: 
    print "failure: " + str(count) 
    time.sleep(5) 

    # find out how many rows exists in the destination table after sleeping 
    process = run(cmd) 
    count = (int(process.communicate()[0][:-1])) 
    print "after sleeping the count is " + str(count) 

보통이 스크립트의 출력은 5 초 동안 잠을 자면 두 번째 선택에서 100000의 행 수가 올바르게 표시됩니다. 내 결론은 다음 중 하나가 참이라는 것입니다.

,
  1. subprocess.Popen가 종료 자식 스레드를 대기하지 않습니다 -이 문서를 모순되는 것처럼 보인다
  2. MySQL의 삽입은 원자 아니다 - MySQL의 나의 이해는
  3. 선택이 아닌 삽입 원자임을 표시하는 것 지금 당장 올바른 행 수를 보는 것 - 내가하는 것보다 MySQL을 잘 아는 친구에 따르면

무엇이 누락 되었습니까?

참고로, 저는 이것이 파이썬과 MySQLdb에서 mysql과 상호 작용하는 해킹 방법이라는 것을 알고 있습니다. MySQLdb에는이 문제가 없을 가능성이 있지만이 방법이 작동하지 않는 이유에 대해 궁금합니다.

+0

감사합니다 여러분. 하위 프로세스 문서를 다시 보면 Popen 메서드 섹션이 아닌 편의 메서드 섹션에 나타나는 "명령을 기다리는 중입니다"라는 주석이 표시됩니다. 나는 Jed의 답변에 고개를 끄덕였다. 왜냐하면 내 미래의 스크립팅 요구에 Paul의 솔루션을 사용할 것이라고 생각하기는하지만 원래의 질문에 가장 잘 답변했기 때문이다. –

+0

os.system은 (다른 것을 사용하지 않는 한) 프로세스의 반환 값 (보통 0 또는 1)을 반환합니다. 그것도 당신을 물게하지 마라. –

답변

20

subprocess.Popen은 인스턴스화 될 때 프로그램을 실행합니다. 그러나 그것은 기다리지 않습니다. 마치 쉘에 cmd &을 입력 한 것처럼 백그라운드에서 실행됩니다. 위의 코드에서 본질적으로 경쟁 조건을 정의했습니다. 삽입이 시간 내에 끝나면 정상적으로 표시되지만 예상치 못한 결과가 발생합니다. 처음 run() PID가 끝날 때까지 기다리지 않고 단순히 Popen 인스턴스를 반환하고 계속합니다. 이 같은 기다렸다되지 나타내는 것처럼 보인다는 popen에 대한 몇 가지 아주 명확한 방법이 있기 때문에

내가이 동작은 문서를 모순 방법을 잘 모르겠어요 : 나는 동의

Popen.wait() 
    Wait for child process to terminate. Set and return returncode attribute. 

을하지만, 그 이 모듈에 대한 문서를 개선 할 수 있습니다.

나는 subprocess의 편리한 메소드, subprocess.call를 사용하거나 (당신은 표준 출력을 필요로 할 때 경우에)를 Popen 객체에 communicate를 사용하는 것이 좋습니다 것, 완료 프로그램에 기다립니다. 두 번째 전화를 걸기 위해 이미이 작업을 수행하고 있습니다.

### START MAIN 
# copy some rows from a source table to a destination table 
# note that the destination table is empty when this script is run 
cmd = 'mysql -u ve --skip-column-names --batch --execute="insert into destination (select * from source limit 100000)" test' 
subprocess.call(cmd) 

# check to see how many rows exist in the destination table 
cmd = 'mysql -u ve --skip-column-names --batch --execute="select count(*) from destination" test' 
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE) 
try: count = (int(process.communicate()[0][:-1])) 
except: count = 0 

또한 대부분의 경우 쉘에서 명령을 실행할 필요가 없습니다. 이 경우 중 하나이지만 시퀀스와 같은 명령을 다시 작성해야합니다.예상대로이도 작동, 및 주입되지 않습니다

prog = ["mysql", "-u", "ve", "--execute", 'insert into foo values ("snargle", 2)'] 
subprocess.call(prog) 

: 그 일을 그런 식으로 또한 기존의 쉘 주입을 피하고과 같이, 인용에 대한 걱정을 덜 수 있습니다

prog = ["printf", "%s", "<", "/etc/passwd"] 
subprocess.call(prog) 

대화식으로 사용해보십시오. 쉘 입력의 가능성을 피합니다. 특히 사용자 입력을 받아들이는 경우 특히 그렇습니다. ^^

+1

subprocess.call을 사용하고 있는데 대기중인 것 같지 않습니다. 이후의 문장은 방금 실행 한 파일을 삭제하도록 코드에 지시하고 코드가 실행되기 전에 호출되어 프로그램을 중단합니다. – Elliot

4

야, subprocess.Popenwait 메서드를 사용하여 객체를 반환 한 이유는 무엇이라고 생각하십니까? 대기가 이 아니기 때문에 암시 적, 본질적, 즉각적이고 필연적 인 것으로 추측되는 것처럼 보였습니다 ...! 하위 프로세스를 생성하는 가장 보편적 인 이유는 프로세스가 완료 될 때까지 기다리지 말고 (예 : 다른 코어에서, 또는 시간 슬라이싱으로 최악의 경우) 운영 체제 및 하드웨어의 감시 장치) 부모 프로세스와 동시에; 부모 프로세스가 서브 프로세스가 끝날 때까지 기다릴 필요가있을 때 원래 subprocess.Process 호출에 의해 리턴 된 오브젝트에 대해 wait을 호출하게됩니다.

7

하위 프로세스와 popen을 반드시 사용해야하는 경우가 아니라면 보통 os.system을 사용하는 것이 더 간단합니다. 프로세스는 스크립트의 다음 단계로 이동하기 전에 반환 할

import os 
run = os.system #convenience alias 
result = run('mysql -u ve --execute="select * from wherever" test') 

는 popen과는 달리, os.system 기다릴 않습니다 예를 들어, 빠른 스크립트 나는 종종 이런 일을한다. 워드 프로세서에에

더 많은 정보 : 위대한 답변 http://docs.python.org/library/os.html#os.system

관련 문제