Python 子进程循环运行两次

Posted

技术标签:

【中文标题】Python 子进程循环运行两次【英文标题】:Python Subprocess Loop runs Twice 【发布时间】:2021-12-30 12:16:42 【问题描述】:

所以,我创建了一个 Python 脚本来使用 Ghostscript 批量转换 PDF 文件。理想情况下它应该可以工作,但我不确定为什么它不工作。目前,它会检查输入的 PDF 文件两次,第二次运行时会覆盖输出文件。

这是脚本。

from __future__ import print_function
import os
import subprocess

try:
   os.mkdir('compressed')
except FileExistsError:
   pass   

for root, dirs, files in os.walk("."):
   for file in files:
      if file.endswith(".pdf"):
         filename = os.path.join(root, file)
         arg1= '-sOutputFile=' + './compressed/' + file
         print ("compressing:", file )
         p = subprocess.Popen(['gs', '-sDEVICE=pdfwrite', '-dCompatibilityLevel=1.4', '-dPDFSETTINGS=/screen', '-dNOPAUSE', '-dBATCH',  '-dQUIET', str(arg1), filename], stdout=subprocess.PIPE).wait()

这是输出。

我错过了我做错了什么。

【问题讨论】:

file 只是文件的名称。您在不同的目录中有多个名称相同的文件 @Jean-FrançoisFabre 我在那个文件夹中只有这 6 个文件。 但是compressed 也被扫描了,因为它在. 中!! 请don’t post images of code, error messages, or other textual data. 【参考方案1】:

file 只是文件名。您在不同的目录中有多个名称相同的文件。不要忘记os.walk 默认在子目录中递归。

因此您必须将转换后的文件保存在依赖于root 的目录或名称中。

并将输出目录放在当前目录之外,因为os.walk 会扫描它

例如,对于平面输出替换:

arg1= '-sOutputFile=' + './compressed/' + file

通过

arg1= '-sOutputFile=' + '/somewhere/else/compressed/' + root.strip(".").replace(os.sep,"_")+"_"+file

表达式

root.strip(".").replace(os.sep,"_")

应该创建一个“平面”版本的root 树,没有当前目录(无点)和转换为下划线的路径分隔符,加上最后一个下划线。这是一种可行的选择。

不会扫描./compressed 或任何其他子目录(可能更多您正在寻找的)的替代版本将使用os.listdir 代替(无递归)

root = "."
for file in os.listdir(root):
  if file.endswith(".pdf"):
     filename = os.path.join(root, file)
     arg1= '-sOutputFile=' + './compressed/' + file
     print ("compressing:", file )

os.scandir

root = "."
for entry in os.scandir(root):
  file = entry.name
  if file.endswith(".pdf"):
     filename = os.path.join(root, file)
     arg1= '-sOutputFile=' + './compressed/' + file
     print ("compressing:", file )

【讨论】:

感谢os.listdir(root),这正是我想要的。 如果您不关心顺序,os.scandir(root) 通常更快,但如果您只有几个文件,差异会很小。 scandir 在 Windows 上肯定要快得多,确实如此,但在上述情况下,它与没有其他操作(例如 os.getmtime()stat 调用)相同。跨度> 【参考方案2】:

您的问题是 os.walk 也会在“压缩”目录中检索该内容。这是因为文件将在该目录中的 os.walk 列表文件之前被压缩和创建。如果您将print(os.path.join(root, file)) 添加到您的 for 循环中,您会注意到这一点。

Bellow 是一个有效的 sn-p,因为检索到的文件只是当前目录中的文件。

import os

os.makedirs("compressed", exist_ok=True)

for file in os.listdir("."):
    if not os.path.isfile(file):
        continue
    if not file.endswith(".pdf"):
        continue
    print(file)

【讨论】:

【参考方案3】:

os.walk 将根据定义进入子目录,因此您将再次压缩compressed 子目录中的文件。

也许你只是想要

for file in os.scandir("."):
   ...

顺便说一句,您几乎肯定希望避免使用Popen,而使用subprocess.run() 或其旧版本之一。

【讨论】:

【参考方案4】:

在第一次迭代 for root, dirs, files in os.walk(".") 在当前目录中找到文件,然后将它们压缩到 ./compressed/*.pdf 路径。

之后外循环的第二次迭代将在子目录中找到已经压缩的文件。

最简单的解决方法是将输出目录移到输入目录之外(或在compressed 目录旁边创建一个input 目录,并从那里读取文件而不是.

【讨论】:

以上是关于Python 子进程循环运行两次的主要内容,如果未能解决你的问题,请参考以下文章

当脚本在多处理工作人员中运行异步事件循环时,通过子进程运行脚本会挂起

python多进程

使用 asyncio 等待子进程的结果

python多进程

进程创建

python 如何监测进程是不是关闭?