【问题标题】:Escaping multiple layers of mixed quotes for a curl command executed inside a bash script为在 bash 脚本中执行的 curl 命令转义多层混合引号
【发布时间】:2020-06-06 01:56:47
【问题描述】:

我有以下 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

【问题讨论】:

标签: bash curl escaping


【解决方案1】:

首先,正如我在评论中所说,将命令存储在变量中是行不通的。变量用于数据,而不是可执行代码。其次,这里有两个级别的引用:作为 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 变量up 分别设置为shell 变量$username$password(并且shell 变量周围的双引号将保留shell从对值做任何愚蠢的事情),并创建一个嵌入它们的 JSON sn-p。 jq 将自动添加适当的引用/转义/需要的任何内容。

然后,要将它与curl 一起使用,请使用以下内容:

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

同样,$userinfo 周围的双引号可以防止 shell 做任何愚蠢的事情。您几乎应该总是在 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 不会在其输出末尾自动添加换行符。)

【讨论】:

  • 感谢@Gordon Davisson (+1),但是当我尝试您的示例时,我得到“jq: error: u/0 is not defined at <top-level>, line 1: {"username":u,"password":p} jq: error: p/0 is not defined at <top-level>, line 1: {"username":u,"password":p} ”任何想法?
  • @hotmeatballsoup 天哪! jq 变量是 $u$p,而不仅仅是 up。我在测试我的解决方案时解决了这个问题,但必须将修复前的版本复制并粘贴到我的答案中。我已经纠正了;试试上面的固定版本。
【解决方案2】:

尝试改变这个:

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

也是如此

【讨论】:

  • 谢谢@vgersh99 (+1) -- 我想你让我更接近,但还不够。请在上面查看我的更新^^^再次感谢!
【解决方案3】:

啊那些讨厌的“卷曲”东西...... 怎么样...

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

【讨论】:

  • 再次感谢@vgersh99 (+1) 以便为req 变量修复它(谢谢!!!)但是当我尝试对resp 变量进行相同的更改时,我得到了错误。您能否在答案中包含我需要将resp 更改为的内容?再次非常感谢!
【解决方案4】:

这需要更多的转义:
与:

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"}

(应该没问题)

【讨论】:

  • 感谢@lab9 (+1),但 curl 仍然无法执行。使用您的更改,我仍然将resp = HTTP/1.1 400 Bad Request... 作为回显输出。但是,如果我复制 req 打印的 curl,并在命令行上运行复制的 curl,它可以正常工作并且我得到 HTTP 200 OK。
  • 好的。如果您有机会进行故障排除,您可以在“resp=$(...)”行上方放置一行 set -x 并重新运行脚本。会有额外的调试输出可能会有所帮助。此外,您可以查看服务器日志(以防您控制它们)
猜你喜欢
  • 2013-04-08
  • 2020-04-06
  • 2023-03-09
  • 2014-11-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-14
相关资源
最近更新 更多