我有以下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}')
说明:这使用--arg
将jq
变量分别设置为$username
和$password
的 shell 变量u
和p
(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"}
(应该没问题(