Python subprocess调用带参Bash脚本报错排查(对比硬编码成功场景)
排查REST API调用报错原因及解决方案
问题描述
我开发了一个通过Bash脚本调用curl从国家图书馆网站获取报纸信息的REST API,脚本支持两种运行方式:
- 直接执行
bash sof.sh,使用硬编码参数,运行正常,可生成包含预期信息的JSON文件; - 通过Python的subprocess模块传递参数执行
python file.py,运行报错,返回错误HTML页面。
Bash脚本内容(sof.sh)
#!/bin/bash : ' ################## 1) Running via Bash Script & hardcoding input arguments ################## myQUERY="Rusanen" myORDERBY="DATE_DESC" myFORMATS='["NEWSPAPER"]' myFUZZY="false" myPubPlace='["Iisalmi", "Kuopio"]' myLANG='["FIN"]' ################## 1) Running via Bash Script & hardcoding input arguments ################## # Result: OK! a json file with retreived expected information ' #: ' ################## 2) Running from python script with input arguments ################## for ARGUMENT in "$@" do #echo "$ARGUMENT" KEY=$(echo $ARGUMENT | cut -f1 -d=) KEY_LENGTH=${#KEY} VALUE="${ARGUMENT:$KEY_LENGTH+1}" export "$KEY"="$VALUE" done echo $# "ARGS:" $* ################## 2) Running from python script with input arguments ################## # Result: Error!! #' out_file_name="newspaper_info_query_${myQUERY// /_}.json" echo ">> Running $0 | Searching for QUERY: $myQUERY | Saving in $out_file_name" curl 'https://digi.kansalliskirjasto.fi/rest/binding-search/search/binding?offset=0&count=10000' \ -H 'Accept: application/json, text/plain, */*' \ -H 'Cache-Control: no-cache' \ -H 'Connection: keep-alive' \ -H 'Content-Type: application/json' \ -H 'Pragma: no-cache' \ --compressed \ --output $out_file_name \ -d @- <<EOF { "query":"$myQUERY", "languages":$myLANG, "formats":$myFORMATS, "orderBy":"$myORDERBY", "fuzzy":$myFUZZY, "publicationPlaces": $myPubPlace } EOF
Python调用代码(file.py)
def rest_api_sof(params={}): params = {'query': ["Rusanen"], 'publicationPlace': ["Iisalmi", "Kuopio"], 'lang': ["FIN"], 'orderBy': ["DATE_DESC"], 'formats': ["NEWSPAPER"], } print(f"REST API: {params}") subprocess.call(['bash', 'sof.sh', f'myFORMATS={params.get("formats", "")}', f'myQUERY={",".join(params.get("query"))}', f'myORDERBY={",".join(params.get("orderBy", ""))}', f'myLANG={params.get("lang", "")}', f'myPubPlace={params.get("publicationPlace", "")}', ]) if __name__ == '__main__': rest_api_sof()
报错表现
执行python file.py后,输出的JSON文件中返回国家图书馆的错误HTML页面(内容见问题原文),而非预期的报纸信息JSON数据。
原因分析
- 参数格式不匹配:Python传递数组类型参数时,直接拼接成了Python列表格式(如
['NEWSPAPER']),但API要求的是标准JSON数组格式(如["NEWSPAPER"]),导致请求体JSON语法错误。 - 参数缺失:Python代码未传递
myFUZZY参数,Bash脚本中该变量未定义,生成的请求体JSON会出现语法漏洞。 - Bash参数解析缺陷:原脚本用
cut -f1 -d=解析key=value参数,若参数值包含=会解析错误,且未对参数值的特殊字符做转义处理。
解决方案
1. 修改Python代码,生成标准JSON格式参数
使用json.dumps()将列表转为标准JSON数组字符串,同时补充缺失的myFUZZY参数:
import subprocess import json def rest_api_sof(params={}): params = { 'query': ["Rusanen"], 'publicationPlace': ["Iisalmi", "Kuopio"], 'lang': ["FIN"], 'orderBy': ["DATE_DESC"], 'formats': ["NEWSPAPER"], 'fuzzy': "false" } print(f"REST API: {params}") # 格式化参数值:列表转为JSON数组字符串,普通值直接返回 def format_param(value): if isinstance(value, list): return json.dumps(value) return value subprocess.call([ 'bash', 'sof.sh', f'myFORMATS={format_param(params.get("formats", ""))}', f'myQUERY={format_param(params.get("query", ""))}', f'myORDERBY={format_param(params.get("orderBy", ""))}', f'myLANG={format_param(params.get("lang", ""))}', f'myPubPlace={format_param(params.get("publicationPlace", ""))}', f'myFUZZY={format_param(params.get("fuzzy", ""))}' ]) if __name__ == '__main__': rest_api_sof()
2. 优化Bash脚本的参数解析逻辑
改用正则表达式解析key=value参数,支持值中包含=的情况,并对双引号做转义处理:
#!/bin/bash # 解析传入的key=value格式参数 for ARGUMENT in "$@" do # 正则匹配key=value,兼容值中包含=的场景 if [[ "$ARGUMENT" =~ ^([^=]+)=(.*)$ ]]; then KEY="${BASH_REMATCH[1]}" VALUE="${BASH_REMATCH[2]}" # 转义值中的双引号,避免Bash解析冲突 VALUE="${VALUE//\"/\\\"}" export "$KEY"="$VALUE" fi done out_file_name="newspaper_info_query_${myQUERY// /_}.json" echo ">> Running $0 | Searching for QUERY: $myQUERY | Saving in $out_file_name" curl 'https://digi.kansalliskirjasto.fi/rest/binding-search/search/binding?offset=0&count=10000' \ -H 'Accept: application/json, text/plain, */*' \ -H 'Cache-Control: no-cache' \ -H 'Connection: keep-alive' \ -H 'Content-Type: application/json' \ -H 'Pragma: no-cache' \ --compressed \ --output "$out_file_name" \ -d @- <<EOF { "query":"$myQUERY", "languages":$myLANG, "formats":$myFORMATS, "orderBy":"$myORDERBY", "fuzzy":$myFUZZY, "publicationPlaces": $myPubPlace } EOF
验证步骤
- 注释Bash脚本中的硬编码参数代码块,启用参数解析代码块;
- 执行
python file.py,此时生成的请求体JSON格式与硬编码场景完全一致,API将返回正常的报纸信息JSON数据。
内容的提问来源于stack exchange,提问作者farid
相关产品推荐
相关产品推荐

