簡體   English   中英

為什么subprocess.Popen不等待子進程終止?

[英]Why is subprocess.Popen not waiting until the child process terminates?

我遇到了Python的subprocess.Popen方法的問題。

這是一個演示問題的測試腳本。 它正在Linux機器上運行。

#!/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)

通常這個腳本的輸出是:

success: 100000

但有時它是

failure: 0
after sleeping the count is 100000

請注意,在失敗的情況下,插入后立即顯示0行,但在睡眠5秒后,第二次選擇正確顯示行數為100000.我的結論是以下之一為真:

  1. subprocess.Popen沒有等待子線程終止 - 這似乎與文檔相矛盾
  2. mysql插入不是原子的 - 我對mysql的理解似乎表明插入是原子的
  3. 選擇沒有立即看到正確的行數 - 根據一個比我更了解mysql的朋友,這也不應該發生

我錯過了什么?

僅供參考,我知道這是一種從Python與mysql交互的hacky方式,MySQLdb可能沒有這個問題,但我很好奇為什么這個方法不起作用。

subprocess.Popen在實例化時運行程序。 但是,它不會等待它 - 它會在后台觸發它,就像你在shell中鍵入cmd & 所以,在上面的代碼中,你基本上定義了一個競爭條件 - 如果插入可以及時完成,它將顯示正常,但如果沒有,你會得到意外的輸出。 你不是在等待你的第一次run() 'PID完成,你只是返回它的Popen實例並繼續。

我不確定這種行為是如何與文檔相矛盾的,因為在Popen上有一些非常明確的方法似乎表明它沒有等待,例如:

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

但我同意,可以改進該模塊的文檔。

要等待程序完成,我建議使用subprocess的便捷方法, subprocess.call ,或者在Popen對象上使用communicate (對於需要stdout的情況)。 您已經為第二次通話做了這個。

### 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

此外,在大多數情況下,您不需要在shell中運行該命令。 這是其中一種情況,但您必須像序列一樣重寫命令。 這樣做也可以避免傳統的shell注入,而不用擔心引用,如下所示:

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

這甚至會起作用,並且不會像你期望的那樣注入:

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

以交互方式嘗試。 您可以避免shell注入的可能性,尤其是在您接受用戶輸入的情況下。 我懷疑你正在使用與子進程通信的不那么棒的字符串方法,因為你在使序列工作時遇到了麻煩:^)

如果你不是絕對需要使用os.system和popen,那么使用os.system通常更簡單。 例如,對於快速腳本,我經常做這樣的事情:

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

與popen不同, os.system DOES會等待您的進程返回,然后再轉到腳本的下一個階段。

有關它的更多信息,請訪問以下文檔: http//docs.python.org/library/os.html#os.system

伙計,為什么你認為subprocess.Popen返回一個帶有wait方法的對象,除非是因為等待不是隱含的,內在的,立即的和不可避免的,因為你似乎猜測......?! 產生子進程的最常見原因不是立即等待它完成,而是讓它繼續(例如在另一個核心上,或者最壞的時候切片 - 這是操作系統的 - 和硬件 - 了望)在父進程繼續的同時; 當父進程需要等待子進程完成時,它顯然會調用原始subprocess.Process調用返回的對象的wait

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM