英文:
Python call Powershell Invoke-RestMethod with Json as string into Body
问题
import requests
import subprocess
import base64
# 定义凭证
credentials = "login:password"
# 定义URL
url = f'urlXXXurl'
# 定义请求体
body = '{"id": 4986986, "key": "2df534ee-270b-4ab4-83fb-1b308febacce", ...}'
# 构建请求头
headers = f'@{{Authorization = \'Basic %s\'}}' % base64.b64encode(credentials.encode("ascii")).decode("ascii")
# 构建PowerShell命令
command = f'powershell.exe Invoke-RestMethod -Method Post -Uri %s -ContentType application/json -Body "{body.replace("\"", "\\\"")}" -Headers (Invoke-Expression {headers}) -UseBasicParsing' % (url)
# 执行命令
response = subprocess.check_call(command)
英文:
import requests
import subprocess
import base64
credentials = "login:password"
url = f'urlXXXurl'
body = '{"id": 4986986, "key": "2df534ee-270b-4ab4-83fb-1b308febacce", ...}'
headers = '"""@{Authorization = \'Basic %s\'}"""' % base64.b64encode(credentials.encode("ascii")).decode("ascii")
command = 'powershell.exe Invoke-RestMethod -Method Post -Uri %s -ContentType application/json -Body """%s""" -Headers ( Invoke-Expression %s ) -UseBasicParsing' % (url, body.replace('"', '\"'), headers)
response = subprocess.check_call(command)
Is there some kind of necessary conversion, so that JSON would be recognizable by PowerShell?
答案1
得分: 1
最后我使用了这种方法:
command = "powershell.exe ConvertTo-Json (Invoke-RestMethod -Method Post -Uri %s -ContentType application/json -Body '%s' -Headers (Invoke-Expression %s) -UseBasicParsing)" \
% (url, json.dumps(self.asset).replace('"', '""""'), headers)
英文:
Finally I used this approach
command = "powershell.exe ConvertTo-Json ( Invoke-RestMethod -Method Post -Uri %s -ContentType application/json -Body '%s' -Headers ( Invoke-Expression %s ) -UseBasicParsing )" \
% (url, json.dumps(self.asset).replace('"', '"""'), headers)
答案2
得分: 1
以下是您要翻译的内容:
-
对于您问题中的方法,唯一的即时问题是在
-Body
参数周围使用了"""..."""
(从PowerShell的角度看,这将导致-Command
参数中的"..."
字符串):-
这样做需要两种转义技巧:
\
(或"""
)以便保留任何"
字符作为传递给PowerShell CLI的(隐含的)-Command
参数的一部分,并且`
转义"
字符,嵌入在值中的...
。 -
因此,尽管您可以使用
body.replace('"', '`\"')
(请注意`
),但使用'...'
更简单,[1] 因为'
在命令行上不需要转义,并允许嵌入"
字符(尽管通过CLI作为\"
)。
-
-
无需使用
( Invoke-Expression %s )
,因为您可以直接构建表示-Header
参数的字符串作为PowerShell 散列表 文字。 -
正如Mathias所指出的,如果要使命令输出JSON字符串,请使用
Invoke-WebRequest
并访问输出对象的.Content
属性。- 相比之下,如果使用
Invoke-RestMethod
,PowerShell会自动且无条件地将JSON响应解析为对象图(这就是在 您自己的回答 中调用ConvertTo-Json
的原因,如果使用(Invoke-WebRequest).Content
则不需要)。
- 相比之下,如果使用
将上述内容应用于您的代码,使用Python v3.6+ f-strings:
import requests
import subprocess
import base64
credentials = 'login:password'
url = 'urlXXXurl'
body = '{"id": 4986986, "key": "2df534ee-270b-4ab4-83fb-1b308febacce", ...}'
# 构建表示PowerShell散列表文字的标头字符串。
# 请注意,在f字符串中使用{{和}}来使用*字面上*的{和}字符。
headers = f"@{{Authorization = 'Basic {base64.b64encode(credentials.encode('ascii')).decode('ascii')}'}}"
# 转义JSON中的"字符为\",
# 以便PowerShell CLI不会去掉它们。
bodyEscaped = body.replace('\"', '\"')
# 注意:
# 由于使用了'...' PowerShell字符串,假设
# 标头或bodyEscaped本身不包含'字符
# 如果包含,这些嵌入的'必须转义为''
command = f'''
powershell.exe -NoProfile -Command
(
Invoke-WebRequest -Uri '{url}' -Body '{bodyEscaped}' -Headers {headers} -Method Post -ContentType application/json -UseBasicParsing
).Content
'''
response = subprocess.check_call(command)
请注意,[Windows PowerShell CLI](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_powershell_exe)
的-NoProfile
参数和-Command
参数在这里并不是严格必需的:
-NoProfile
绕过了配置文件(初始化文件)的加载,因此可能更快,并且使执行环境更可预测。-Command
(-c
)如果没有使用,将被隐含使用,但出于概念上的清晰性考虑,它被包含在内(尤其是因为pwsh
,即_PowerShell (Core)_ CLI,现在要求使用-Command
,因为它默认为-File
)。
最后,请注意,从技术上讲,PowerShell命令作为多个参数传递,而不是作为一个字符串传递,不加(未转义的)"..."
。
- 如果(另一个)shell,尤其是
cmd.exe
,涉及到调用(在subprocess.call()
中没有涉及到),则建议使用"..."
,以防止该shell意外解释字符,如&
和|
。 - 另外,如果您的PowerShell命令中有嵌入的字符串,其中包含需要保留为多个空格的运行的情况(不使用
"..."
封闭,这些运行将折叠为单个空格)。
英文:
-
The only immediate problem with the approach in your question was the use of
"""..."""
around the-Body
argument (resulting in a"..."
string from PowerShell's perspective):-
Doing so requires two escaping techniques:
\
(or"""
) in order to preserve any"
characters as part of the PowerShell command passed to the the (implied)-Command
parameter of PowerShell's CLI and`
-escaping of the"
chars. embedded in the value (...
). -
While you could therefore have used
body.replace('"', '`\"')
(note the`
), it is simpler to use'...'
for strings,<sup>[1]</sup> given that'
does
not require escaping on the command line and allows embedding"
characters as-is (albeit as\"
via the CLI).
-
-
There is no need for
( Invoke-Expression %s )
, given that you can formulate the string representing the-Header
argument directly as a PowerShell hashtable literal. -
As Mathias points out, if you want the command to output a JSON string, use
Invoke-WebRequest
and access the output object's.Content
property.- By contrast, if you use
Invoke-RestMethod
, PowerShell automatically and invariably parses a JSON response into an object graph (which is what necessitated theConvertTo-Json
call in your own answer, which isn't needed if you use(Invoke-WebRequest).Content
).
- By contrast, if you use
Applying the above to your code, using Python v3.6+ f-strings:
import requests
import subprocess
import base64
credentials = 'login:password'
url = 'urlXXXurl'
body = '{"id": 4986986, "key": "2df534ee-270b-4ab4-83fb-1b308febacce", ...}'
# Construct the header as a string representing a PowerShell hashtable literal.
# Note the the use of {{ and }} to use *literal* { and } chars. in the f-string.
headers = f"@{{Authorization = 'Basic {base64.b64encode(credentials.encode('ascii')).decode('ascii')}'}}"
# Escape the " chars. in JSON as \",
# so that the PowerShell CLI doesn't strip them.
bodyEscaped = body.replace('"', '\"')
# Note:
# Due to use of '...' PowerShell strings, the assumption is that
# neither header nor bodyEscaped themselves contain '
# If they did, these embedded ' would have to be escaped as ''
command = f"""\
powershell.exe -NoProfile -Command
(
Invoke-WebRequest -Uri '{url}' -Body '{bodyEscaped}' -Headers {headers} -Method Post -ContentType application/json -UseBasicParsing
).Content
"""
response = subprocess.check_call(command)
Note that neither the -NoProfile
nor the -Command
parameter of the Windows PowerShell CLI are strictly needed here:
-NoProfile
bypasses profile (initialization-file) loading and is therefore both potentially faster and makes for a more predictable execution environment.-Command
(-c
) would be implied if not used, but is included for conceptual clarity (not least becausepwsh
, the PowerShell (Core) CLI, now requires-Command
, as it defaults to-File
).
Finally, note that - for simplicity - what constitutes the PowerShell command is technically passed as multiple arguments rather than as a single string enclosed in (unescaped) "..."
.
- If (another) shell, notably
cmd.exe
, were involved in the call (it isn't withsubprocess.call()
), use of"..."
would be advisable, to prevent accidental interpretation of characters such as&
and|
by that shell. - Separately,
"..."
would also be needed in the rare event that your PowerShell command has embedded strings with runs of multiple spaces that need to be preserved as such (without"..."
enclosure, such runs would be folded into a single space each).
<sup>[1] This assumes that you don't need PowerShell's string interpolation, which requires "..."
strings.</sup>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论