转义在 bash 脚本中执行的 curl 命令的多层混合引号



我有以下bash脚本,它使用其参数来访问RESTful Web服务(通过curl(,并打印出发出的curl请求和响应:

#! /bin/bash
# arguments:
# $1 - username
# $2 - password
#
# outputs:
# if the script exits with a non-zero status then something went wrong
# verify that we have all 6 required arguments and fail otherwise
if [ "$#" -ne 2 ]; then
echo "Required arguments not provided"
exit 1
fi
# set the script arguments to meaningful variable names
username=$1
password=$2
# login and fetch a valid auth token
req='curl -k -i -H "Content-Type: application/json" -X POST -d ''{"username":"$username","password":"$password"}'' https://somerepo.example.com/flimflam'
resp=$(curl -k -i -H "Content-Type: application/json" -X POST -d ''{"username":"$username","password":"$password"}'' https://somerepo.example.com/flimflam)
# echo the request for troubleshooting
echo "req = $req"
if [ -z "$resp" ]; then
echo "Login failed; unable to parse response"
exit 1
fi
echo "resp = $resp"

当我运行这个时,我得到:

$ sh myscript.sh myUser 12345@45678
curl: (3) Port number ended with '"'
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0curl: (6) Could not resolve host: 12345@45678"
100  1107  100  1093  100    14   2849     36 --:--:-- --:--:-- --:--:--  2849
req = curl -k -i -H "Content-Type: application/json" -X POST -d {"username":"$username","password":"$password"} https://somerepo.example.com/flimflam
resp = HTTP/1.1 400 Bad Request...(rest omitted for brevity)

显然,我没有正确转义 curl 语句中单引号和双引号的各个层,如以下输出所示:

curl: (6) Could not resolve host: 12345@45678"

和:

req = curl -k -i -H "Content-Type: application/json" -X POST -d {"username":"$username","password":"$password"} https://somerepo.example.com/flimflam

用户名/密码变量未解析的地方。

实际上,我的脚本需要的参数远远超过 2 个,这就是为什么我要将它们更改为具有有意义的变量名称(例如$username而不是$1(,以便它更容易理解和可读。

谁能发现我哪里出了问题?提前感谢!

更新

我尝试了将req变成:

curl -k -i -H 'Content-Type: application/json' -X POST -d "{'username':'myUser','password':'12345@45678'}" https://somerepo.example.com/flimflam

但是,这仍然是一个非法的 curl 命令,而是需要:

curl -k -i -H 'Content-Type: application/json' -X POST -d '{"username":"myUser","password":"12345@45678"}' https://somerepo.example.com/flimflam

首先,正如我在评论中所说,将命令存储在变量中并不能正常工作。变量用于数据,而不是可执行代码。其次,这里有两个级别的引用:作为 shell 语法一部分的引号(在将参数传递给 'curl 之前由 shell 解析、应用和删除(,以及作为 JSON 语法一部分的引号。

但第二个问题实际上比这更糟糕,因为如果字符串包含属于 JSON 语法一部分的字符,则简单地将任意字符串嵌入到某些 JSON 中可能会导致 JSON 语法错误。哪些密码可能会起作用。要使密码(和用户名(正确嵌入到 JSON 中,请使用理解 JSON 语法的工具,例如jq

userinfo=$(jq -n -c --arg u "$username" --arg p "$password" '{"username":$u,"password":$p}')

说明:这使用--argjq变量分别设置为$username$password的 shell 变量up(shell 变量周围的双引号将防止 shell 对值做任何愚蠢的事情(,并创建一个嵌入它们的 JSON 代码段。jq将自动添加适当的引用/转义/所需的任何内容。

然后,要将其与curl一起使用,请使用如下所示的内容:

resp=$(curl -k -i -H "Content-Type: application/json" -X POST -d "$userinfo" https://somerepo.example.com/flimflam)

同样,$userinfo两边的双引号可以防止外壳做任何愚蠢的事情。在 shell 中,您几乎总是应该在变量引用周围加上双引号。

请注意,我从未使用req变量来存储命令。如果需要打印命令(或其等效项(,请使用如下所示的内容:

printf '%q ' curl -k -i -H "Content-Type: application/json" -X POST -d "$userinfo" https://somerepo.example.com/flimflam
echo

%q格式说明符告诉 shell 添加适当的引号/转义,以便您可以将结果作为 shell 命令运行,并且它可以正常工作。(echo之所以存在,是因为printf不会在其输出末尾自动添加换行符。

尝试更改它:

req='curl -k -i -H "Content-Type: application/json" -X POST -d ''{"username":"$username","password":"$password"}'' https://somerepo.example.com/flimflam'

对此

req="curl -k -i -H 'Content-Type: application/json' -X POST -d "{'username':'$username','password':'$password'}" https://somerepo.example.com/flimflam"

同样,对于RESP

啊那些讨厌的"卷曲"东西...... 怎么'回合...

req="curl -k -i -H 'Content-Type: application/json' -X POST -d '{"username":"$username","password":"$password"}' https://somerepo.example.com/flimflam"

这需要更多的转义:
使用:

resp=$(curl -k -i -H "Content-Type: application/json" -X POST -d "{"username":"$username","password":"$password"}" https://somerepo.example.com/flimflam)

在 bash 中,当变量位于双引号内的单引号内时,它们仍会扩展。
根据 JSON 定义,您将需要在有效负载中使用 \" 双引号。

编辑:我通过HTTP代理重新运行curl并更正了脚本行(见上文,删除了单引号(。结果(在原始 HTTP 中(现在为:

POST /flimflam HTTP/1.1
Host: somerepo.example.com
User-Agent: curl/7.68.0
Accept: */*
Content-Type: application/json
Content-Length: 44
Connection: close
{"username":"user","password":"12345@abcde"}

(应该没问题(

最新更新