簡體   English   中英

Python-在subprocess.Popen命令中使用變量

[英]Python - Using variables in subprocess.Popen command

我是編碼新手,需要一些幫助。 我正在編寫一個python腳本,它將遍歷目錄的內容,並在遍歷目錄時將每個文件發送到Bluetooth設備。

如果我指定文件名,它可以很好地工作,但是我不能通過使用文件名作為變量來使其工作。 這是下面的代碼

import os
import time
import subprocess

indir = '\\\\10.12.12.218\\myshare'
for  root, dirs, filenames in os.walk(indir):
   for file in filenames:
      print (file)
      subprocess.Popen('ussp-push /dev/rfcomm0 image1.jpg file.jpg', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print ('end') 

我試圖將命令中的“ image1.jpg”替換為變量“ file”,如下所示,但未成功。

subprocess.Popen('ussp-push /dev/rfcomm0', file, 'file.jpg', shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

會非常感謝您的幫助。

有幾個問題:

  • shell=True是不必要的。 刪除它並使用列表參數:

     import shlex args = shlex.split('ussp-push /dev/rfcomm0 image1.jpg file.jpg') 
  • 您試圖將命令行參數作為Popen單獨參數傳遞。 使用Popen(['echo', 'a'])代替Popen('echo', 'a') 后者是完全錯誤的。 請參閱文檔中的Popen()函數簽名

  • 除非您從p.stdout / p.stderr管道讀取,否則請勿使用stdout=PIPE和/或stderr=PIPE否則,如果子進程填充了任何OS管道緩沖區,則子進程可能會永遠阻塞

  • 保存對Popen()的引用,以稍后等待其狀態。 它是可選的,但有助於避免創建過多的僵屍

您可以將文件生成部分提取到單獨的函數中:

import os

def get_files(indir, extensions=('.jpg', '.png')):
    """Yield all files in `indir` with given `extensions` (case-insensitive)."""
    for root, dirs, files in os.walk(indir):
        for filename in files:
            if filename.casefold().endswith(extensions):
               yield os.path.join(root, filename)

然后並行執行每個文件的命令:

from subprocess import CalledProcessError, Popen

indir = r'\\10.12.12.218\myshare'
commands = [['ussp-push', '/dev/rfcomm0', path] for path in get_files(indir)]

# start all child processes
children = [Popen(cmd) for cmd in commands]

# wait for them to complete, raise an exception if any of subprocesses fail
for process, cmd in zip(children, commands):
    if process.wait() != 0:
       raise CalledProcessError(process.returncode, cmd)        

如果您不想並行運行命令,則只需使用subprocess.call而不是subprocess.Popen

import subprocess

indir = r'\\10.12.12.218\myshare'
statuses = [subprocess.call(['ussp-push', '/dev/rfcomm0', path])
            for path in get_files(indir)]
if any(statuses):
   print('some commands have failed')

它一次運行一個命令。

嘗試這個:

subprocess.Popen(
    ['ussp-push', '/dev/rfcomm0', file, 'file.jpg'],
     stdout=subprocess.PIPE, 
     stderr=subprocess.PIPE)

您想將一個字符串列表傳遞給Popen() 一種替代方法是構建一個空格分隔的命令,例如:

subprocess.Popen(
    'ussp-push /dev/rfcomm0 "{0}" file.jpg'.format(file) # replace {0} with file
     stdout=subprocess.PIPE, 
     stderr=subprocess.PIPE)

shell =真的不安全嗎?

關於shell=True的使用,我想提出幾點。

  1. 在這種情況下,正如m.wasowski在評論中指出的那樣,沒有必要。
  2. 如果您對該命令沒有任何控制權, shell=True是不安全的。 例如,如果您從用戶輸入中獲取命令,那么用戶可以向您傳遞諸如sudo rm -fr /
  3. 這是不安全的,因為一旦調用外殼程序, PATH可能會有所不同。 當發出ls的命令時,它可能不是來自通常的位置( /bin/ls ),而是來自一些惡意的位置,例如/home/evil/bin

話雖如此,如果您可以控制命令(在這種情況下為/dev/rfcomm0 ,則shell=True是安全的-您可以定義命令的/dev/rfcomm0 ,而不是從其他位置接收命令。 謝謝m.wasowski提出這一點。

更新

刪除shell=True 看評論。

因此,有一堆文件,您希望以每個文件為參數運行一次命令。 我認為以下方法應該可以正常工作。

import os
import time
import subprocess

indir = '\\\\10.12.12.218\\myshare'
for  root, dirs, filenames in os.walk(indir):
    for file in filenames:
        print 'Sending', os.path.join(root, file)
        subprocess.check_call(['ussp-push', '/dev/rfcomm0', os.path.join(root, file), 'file.jpg'])
print ('end')

這是我所做的更改:

  1. 我使用的check_call函數,而不是Popen如你想在並行順序,而不是運行命令。 check_call函數有相同的參數Popen ,而是等待進程完成,如果過程失敗,會引發異常。
  2. 我將數組(用方括號括起來)作為第一個參數傳遞給check_call命令。 這也意味着不需要外殼程序來解釋命令字符串,因此我刪除了shell=True 該數組中的第一項是命令,其余是傳遞給它的命令。
  3. 數組中的最后一項是文件的完整路徑。 file變量僅保存文件名。 但是我們需要它的路徑,因為它可能位於文件夾內的某個深處(當您遞歸walk )。 os.path.join在平台中使用\\/連接兩個字符串。
  4. 我還刪除了stdoutstderr參數。 這意味着命令的輸出和錯誤將在命令行中出現,這可能是您想要的。 當您想讀取命令的輸出並自己處理而不是在終端中顯示時, stdoutstderr參數才有意義。

暫無
暫無

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

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