【问题标题】:How to use subprocess Popen?如何使用子进程 Popen?
【发布时间】:2014-02-28 09:07:05
【问题描述】:

我正在尝试使用 Popen 执行命令。

该命令使用一些 PostGIS/Postgresql 实用程序将光栅文件上传到数据库,并在从命令行执行时工作。它使用 unix 风格的管道来链接 2 个命令,如下所示:

"C:\\Program Files\\PostgreSQL\\9.2\\bin\\raster2pgsql.exe" -d -I -C -e -Y -F -t 128x128 "C:\\temp\\SampleDTM\\SampleDTM.tif" test | "C:\\Program Files\\PostgreSQL\\9.2\\bin\\psql.exe" -h localhost -p 5432 -d adr_hazard -U postgres

在 Python 中使用时,我将其设为带有 ' 代码的字符串:

command = '"C:\\Program Files\\PostgreSQL\\9.2\\bin\\raster2pgsql.exe" -d -I -C -e -Y -F -t 128x128 "C:\\temp\\SampleDTM\\SampleDTM.tif" test | "C:\\Program Files\\PostgreSQL\\9.2\\bin\\psql.exe" -h localhost -p 5432 -d adr_hazard -U postgres'

尝试执行它会导致错误:

p = subprocess.Popen(command)

ERROR: Unable to read raster file: test

错误似乎是命令未正确解析(它将错误的参数解释为光栅文件)

我用Popen 错了吗?

【问题讨论】:

  • 尝试subprocess.Popen(["C:\\Program Files\\PostgreSQL\\9.2\\bin\\raster2pgsql.exe", '-d', '-I', '-C', '-e', '-Y', '-F', '-t', '128x128', "C:\\temp\\SampleDTM\\SampleDTM.tif", 'test'']),如果它工作,然后在PIPE中输出。

标签: python postgresql subprocess popen raster


【解决方案1】:

您的command 使用管道|。它需要一个外壳:

p = subprocess.Popen(command, shell=True)

据我所知,command 本身看起来还不错。

【讨论】:

  • 这个效果不需要使用shell=True。鉴于danger of insecure input,最好使用stdoutstdin 关键字参数。
  • @Arthur:避免货物崇拜编程。这里没有安全问题,除非command 是使用用户输入构造的(它是问题中的硬编码字符串文字)。如果没有shell=True,使用管道a | b 的调用将在非Windows 系统上失败(我不了解Windows)。
【解决方案2】:

不必使用shell=True 来通过管道实现此目的。这可以通过管道以编程方式完成,甚至where concern about insecure input is an issue。这里,conn_params 是一个带有 PASSWORDNAME(数据库名称)、USERHOST 键的字典。

raster2pgsql_ps = subprocess.Popen([
    'raster2pgsql', '-d', '-I', '-C', '-e', '-Y', '-F', '-t', '128x128',
    'C:\\temp\\SampleDTM\\SampleDTM.tif',
    'test'
], stdout=subprocess.PIPE)

# Connection made using conninfo parameters
# http://www.postgresql.org/docs/9.0/static/libpq-connect.html
psql_ps = subprocess.check_output([
    'psql',
    'password={PASSWORD} dbname={NAME} user={USER} host={HOST}'.format(**conn_params),
], stdin=raster2pgsql_ps.stdout)

【讨论】:

  • 注意:raster2pgsql_ps.stdout 在父级中保持打开状态。在 POSIX 系统上,它会在写入时抑制 SIGPIPE 信号,该信号可能会通知 raster2pgsql_ps psql_ps 过早退出(关闭管道(其标准输入))。这是code example。不知道对windows有没有影响。如果您选择编写特定于 Windows 的命令,那么如果您已经将完整的命令作为字符串,那么使用列表参数是没有意义的(列表在 Windows 上使用 list2cmdline() 函数在 subprocess 模块中转换为字符串) .
  • 确实如此;我无法在 Windows 上尝试这种方法,但我知道它适用于 UNIX 系统。
【解决方案3】:

以下内容在 Windows 上对我有用,同时避免 shell=True

可以利用 Python 的 fstring formatting 来确保命令在 Windows 中有效。

请注意,我使用了shp2pgsql,但它应该与raster2pgsql 的过程非常相似。

shp2pgsql 的参数:srid 是形状文件的坐标系,filename 是要导入的形状文件的路径,tablename 是您要为表格命名的名称.

import os
import subprocess

shp2pgsql_binary = os.path.join(pgsql_dir, "bin", "shp2pgsql")
psql_binary = os.path.join(pgsql_dir, "bin", "psql")

command0 = f'\"{shp2pgsql_binary}\" -s {srid} \"{filename}\" {tablename}'
command1 = f'\"{psql_binary}\" \"dbname={databasename} user={username} password={password} host={hostname}\"'

try:
    shp2pgsql_ps = subprocess.Popen(command0, stdout=subprocess.PIPE)
    psql_ps = subprocess.check_output(command1, stdin=shp2pgsql_ps.stdout)
except:
    sys.stderr.write("An error occurred while importing data into the database, you might want to \
                        check the SQL command below:")
    sys.stderr.write(command)
    raise

要适应raster2pgsql,你只需要修改command0中的字符串,例如-s {srid} 变为 -d -I -C -e -Y -F -t 128x128command1 的字符串可以保持不变。

【讨论】:

    【解决方案4】:
      PIPE = subprocess.PIPE
      pd = subprocess.Popen(['"C:\\Program Files\\PostgreSQL\\9.2\\bin\\raster2pgsql.exe", '-d', '-I', '-C', '-e', '-Y', '-F', '-t', '128x128', "C:\\temp\\SampleDTM\\SampleDTM.tif", 'test'],
        stdout=PIPE, stderr=PIPE)
      stdout, stderr = pd.communicate()
    

    【讨论】:

    • 命令不需要转换成列表。 subprocess 在内部使用 CreateProcess() 无论如何都需要一个字符串。问题中的command 看起来还可以。 The error may be elsewhere.
    【解决方案5】:

    这样使用subprocess.Popen会更好:

    proc = subprocess.Popen(['"C:\\Program Files\\PostgreSQL\\9.2\\bin\\raster2pgsql.exe"', '-d', '-I', '-C', '-e', '-Y', '-F', '-t', '128x128', '"C:\\temp\\SampleDTM\\SampleDTM.tif"', 'test', '|', '"C:\\Program Files\\PostgreSQL\\9.2\\bin\\psql.exe"', '-h', 'localhost', '-p', '5432', '-d', 'adr_hazard', '-U', 'postgres'], shell = True, stdout = subprocess.pipe, stderr = subprocess.STDOUT)
    proc.wait()
    result = proc.stdout.readlines()#if you want to process the result of your command
    proc.kill()
    

    B.T.W,最好先格式化路径,使用:

    path = os.path.normalpath("C:\\Program Files\\PostgreSQL\\9.2\\bin\\raster2pgsql.exe")
    

    这将避免不同操作系统平台的一些路径问题。

    如果您想像在本地 shell 中执行命令一样执行命令,shell = True 很重要。

    希望能帮到你。

    【讨论】:

    • -1:有两个命令通过管道连接(|)。您正在尝试以管道的其余部分作为其参数来启动 raster2pgsql
    • 这样就可以了.评论前需要先尝试命令!!!
    • 它可能在 Windows 上工作,因为如果subprocess 接收到将其转换为字符串的列表参数,它会无条件地调用list2cmdline()。最后,它就像您将 'cmd1 | cmd2' shell 命令作为单个字符串传递一样。将其传递为 ['cmd1', '|', 'cmd2'] 具有误导性(这意味着 |cmd2cmd1 或 shell 的参数)并且这种方法在其他平台上会失效。
    • 该功能在windows平台下确实有效,对于linux,我检查了,只有一些特殊的命令会“|”作为一个选项可能有一些问题。但是这个问题是windows平台下的。
    • 我明白这一点。我说“可能”是因为你的代码有stdout=PIPE(在错误的情况下),但它在p.wait()之后读取输出,如果子进程生成足够的输出,可能会导致死锁。
    猜你喜欢
    • 1970-01-01
    • 2012-09-18
    • 1970-01-01
    • 1970-01-01
    • 2018-12-05
    • 2021-02-11
    • 2018-06-12
    相关资源
    最近更新 更多