python - 如何使用 popen 命令行参数包含单引号和双引号?
问题描述
我想在 python3中运行以下jq
命令。subprocess.Popen()
$ jq 'INDEX(.images[]; .id) as $imgs | {
"filename_with_label":[
.annotations[]
| select(.attributes.type=="letter" )
| $imgs[.image_id] + {label:.text}
| {id:.id} + {filename:.file_name} + {label:.label}
]
}' image_data_annotation.json > image_data_annotation_with_label.json
请注意,第一个命令行参数包含点、美元符号、单引号内的双引号。仅供参考,jq
是用于处理 json 文件的 JSON 处理器实用程序。
我编写了以下 python3 脚本,用于使用jq
实用程序自动处理 JSON 文件。
#!python3
# file name: letter_image_tool.py
import os, subprocess
"""
command line example to automate
$ jq 'INDEX(.images[]; .id) as $imgs | {
"filename_with_label":[
.annotations[]
| select(.attributes.type=="letter" )
| $imgs[.image_id] + {label:.text}
| {id:.id} + {filename:.file_name} + {label:.label}
]
}' image_data_annotation.json > image_data_annotation_with_label.json
"""
# define first command line argument
jq_filter='\'INDEX(.images[]; .id) as $imgs | { "filename_with_label" : [ .annotations[] | select(.attributes.type=="letter" ) | $imgs[.image_id] + {label:.text} | {id:.id} + {filename:.file_name} + {label:.label} ] }\''
input_json_files= [ "image_data_annotation.json"]
output_json_files= []
for input_json in input_json_files:
print("Processing %s" %(input_json))
filename, ext = os.path.splitext(input_json)
output_json = filename + "_with_label" + ext
output_json_files.append(output_json)
print("output file is : %s" %(output_json))
#jq_command ='jq' + " " + jq_filter, input_json + ' > ' + output_json
jq_command =['jq', jq_filter, input_json + ' > ' + output_json]
print(jq_command)
subprocess.Popen(jq_command, shell=True)
在 bash 上运行上述 python 脚本会导致以下结果:
$ ./letter_image_tool.py
Processing image_data_annotation.json
output file is : image_data_annotation_with_label.json
['jq', '\'INDEX(.images[]; .id) as $imgs | { "filename_with_label" : [ .annotations[] | select(.attributes.type=="letter" ) | $imgs[.image_id] + {label:.text} | {id:.id} + {filename:.file_name} + {label:.label} ] }\'', 'image_data_annotation.json > image_data_annotation_with_label.json']
jq - commandline JSON processor [version 1.6-124-gccc79e5-dirty]
Usage: jq [options] <jq filter> [file...]
jq [options] --args <jq filter> [strings...]
jq [options] --jsonargs <jq filter> [JSON_TEXTS...]
jq is a tool for processing JSON inputs, applying the given filter to
its JSON text inputs and producing the filter's results as JSON on
standard output.
The simplest filter is ., which copies jq's input to its output
unmodified (except for formatting, but note that IEEE754 is used
for number representation internally, with all that that implies).
For more advanced filters see the jq(1) manpage ("man jq")
and/or https://stedolan.github.io/jq
Example:
$ echo '{"foo": 0}' | jq .
{
"foo": 0
}
For a listing of options, use jq --help.
它不处理jq
实用程序的第一个参数:
'INDEX(.images[]; .id) as $imgs | {
"filename_with_label":[
.annotations[]
| select(.attributes.type=="letter" )
| $imgs[.image_id] + {label:.text}
| {id:.id} + {filename:.file_name} + {label:.label}
]
}'
第一个参数应该像上面的片段一样用单引号括起来,但我的脚本不处理它。
我认为主要问题与第一个命令行参数(jq_filter
在上面的 python 脚本中)中使用的点、美元符号、单引号和双引号有关。但是我不知道如何处理这种与bash相关的复杂元字符。
我应该怎么做才能解决以上问题?
感谢您的阅读。
更新我的解决方案
使用 jq_filter 定义的三引号和空格分隔连接,如下所示
#!python3
# file name: letter_image_tool.py
import os, subprocess
"""
command line example to automate
$ jq 'INDEX(.images[]; .id) as $imgs | {
"filename_with_label":[
.annotations[]
| select(.attributes.type=="letter" )
| $imgs[.image_id] + {label:.text}
| {id:.id} + {filename:.file_name} + {label:.label}
]
}' image_data_annotation.json > image_data_annotation_with_label.json
"""
# define first command line argument with triple quotes
jq_filter=""" 'INDEX(.images[]; .id) as $imgs | {
"filename_with_label" : [
.annotations[]
| select(.attributes.type=="letter" )
| $imgs[.image_id] + {label:.text}
| {id:.id} + {filename:.file_name} + {label:.label} ] } ' """
input_json_files= [ "image_data_annotation.json"]
output_json_files= []
for input_json in input_json_files:
print("Processing %s" %(input_json))
filename, ext = os.path.splitext(input_json)
output_json = filename + "_with_label" + ext
output_json_files.append(output_json)
print("output file is : %s" %(output_json))
#jq_command ='jq' + " " + jq_filter, input_json + ' > ' + output_json
# jq command composed with space separated join
jq_command =' '.join['jq', jq_filter, input_json, ' > ', output_json]
print(jq_command)
# shell keyword argument should be set True
subprocess.Popen(jq_command, shell=True)
使用三重双引号,jq_filter 可以使用多行定义而不是单行定义更具可读性。
解决方案
您需要单引号的原因是为了防止 shell 对您的参数进行任何扩展。这是一个问题,只有在使用shell=True
. 如果未设置,shell 将永远不会触及您的参数,也无需“保护”它们。
然而,shell 也负责stdout
重定向(即[... '>', output_json]
)。不使用 shell,需要在 Python 代码中处理重定向。然而,这就像将参数添加stdout=...
到Popen
.
总而言之,这意味着您的代码可以重写为
import os
import subprocess
# Still define first command line argument with triple quotes for readability
# Note that there are no single quotes though
jq_filter = """INDEX(.images[]; .id) as $imgs | {
"filename_with_label" : [
.annotations[]
| select(.attributes.type=="letter" )
| $imgs[.image_id] + {label:.text}
| {id:.id} + {filename:.file_name} + {label:.label} ] }"""
input_json_files = ["image_data_annotation.json"]
output_json_files = []
for input_json in input_json_files:
print("Processing %s" % (input_json))
filename, ext = os.path.splitext(input_json)
output_json = filename + "_with_label" + ext
output_json_files.append(output_json)
print("output file is : %s" % (output_json))
# Keep command as list, since this is what we need when NOT using shell=True
# Note also that the redirect and the output file are not parts of the argument list
jq_command = ['jq', jq_filter, input_json]
# shell keyword argument should NOT be set True
# Instead redirect stdout to an out_file
# (We must open the file for writing before redirecting)
with open(output_json, "w") as out_file:
subprocess.Popen(jq_command, stdout=out_file)
通常建议不要使用shell=True
,因为这会打开另一个针对代码的攻击向量,因为注入攻击可以完全访问 shell。此外,不使用 shell 的另一个小好处是,它将减少创建的子进程的数量,因为不需要额外的 shell 进程。
推荐阅读
- sqlalchemy - 使用冲突更新批量插入 sqlalchemy 核心
- typescript - Typesript 参数依赖于另一个具有默认值的参数
- gml - 事件的 GMS2 触发器无法正常工作
- reactjs - 如何在反应中使用时刻将我的字符串日期转换为日期时间类型以对它们进行日期时间加法和减法?
- python - 如何在 Oddsportal 抓取代码输出中添加额外的列?
- python - Append 连接字符串而不是将它们添加到列表中
- node.js - 当机器没有安装 nvm 并且具有与 .nvmrc 提到的不同的 Node 版本时会发生什么?
- express - 如何构建 express 使用 webpack?
- c++ - 连接两个数组并将新元素插入数组
- javascript - Blazor 服务器端问题分享 javascript 代码