使用 printf %q 使带引号的字符串可用作 shell 脚本输入
Posted
技术标签:
【中文标题】使用 printf %q 使带引号的字符串可用作 shell 脚本输入【英文标题】:Using printf %q to make a quoted string usable as shell script input 【发布时间】:2019-05-06 12:01:45 【问题描述】:在 bash 脚本中,我尝试将单引号和双引号的命令字符串附加到文件 (.profile
)。
我想使用echo
然后>>
命令到.profile
。当然,我愿意接受任何可行的解决方案。
我想使用的命令是 echo "curl -X POST -H "Content-Type: application/json" -d '"value1":"PHONENUMBER","value2":"MESSAGE"' https://maker.ifttt.com/trigger/TRIGGER/with/key/KEY &> /dev/null" >> .profile
,但显然这在我的 bash 脚本中不起作用。
我不清楚printf %q
的工作原理,也不明白如何将其应用于我的问题。
我试过了
`CMDSTRING='curl -X POST -H "Content-Type: application/json" -d '`
`CMDSTRING=$CMDSTRING"'"`
`CMDSTRING=$CMDSTRING'"value1":"+PHONENUMBER","value2":"MESSAGE"'`
`CMDSTRING=$CMDSTRING"'"`
`CMDSTRING=$CMDSTRING' https://maker.ifttt.com/trigger/TRIGGER/with/key/KEY &> /dev/null'`
`echo $CMDSTRING`
【问题讨论】:
很高兴看到光明并以“正确”的方式去做。我以为这会又快又脏,但现在它变得缓慢、丑陋和痛苦。您建议哪种方法可行? 顺便说一句,如果您的消息来自不受信任的来源,使用jq
或其他 JSON 语法感知工具对其进行格式化以包含在内会更安全一些;这样,包含文字 "
的消息将不会逃避其引用并能够添加额外的有效负载参数。 (我不知道 IFTTT 的 API,但如果它支持回调,这些参数甚至可能是恶意的)。
re:编辑,请参阅BashPitfalls #14,了解为什么echo $var
总是有问题(需要echo "$var"
)。我希望反引号只是一个格式错误;你永远不会希望它们出现在你的实际代码中。
顺便说一句,还请注意,全大写变量名称位于 shell 和 POSIX 指定实用程序本身使用的命名空间中,而小写名称保留供应用程序使用并保证不会发生冲突。 (我们遇到很多有问题的人,因为他们运行for PATH in ...
,而for path in ...
符合标准的建议并且不会无意中改变shell 的运行方式)。请参阅 pubs.opengroup.org/onlinepubs/9699919799/basedefs/…,记住 shell 和环境变量共享一个命名空间。
顺便说一句,还要注意printf '%q'
生成的代码对于bash 的评估是安全的,但对于所有符合POSIX 的shell来说不一定是安全的>。这使它成为在.bash_profile
中生成内容的正确工具(如果您是 实际上使用了以前未知的值),但是.profile
的错误工具(可以由bash 以外的shell 使用)。
【参考方案1】:
使用printf '%q'
生成.profile
内容如下所示:
printf '%q ' \
curl -X POST -H "Content-Type: application/json" \
-d '"value1":"PHONENUMBER","value2":"MESSAGE"' \
https://maker.ifttt.com/trigger/TRIGGER/with/key/KEY
printf '%s\n' "&>/dev/null"
>> .profile
请注意,如果您希望将&>/dev/null
解析为语法,则不能使用%q
格式字符串,因为就其本质而言,它会格式化所有传递以解析为数据的内容。
因此,我们将printf '%q ' "command name" "first argument" ...
用于实际命令本身,并格式化带外重定向。
也就是说,请注意,只有当您从不受信任的来源替换变量(而不是像示例中那样对它们进行硬编码)并且担心无效值被滥用时,上述内容才有 值用于命令注入。如果您真的只是在文件末尾附加一个常量字符串,引用的 heredoc 将让您手动构建更自然的 shell 引用(实际上,正如您已经完成的那样!),并逐字传递:
cat >>.profile <<'EOF'
curl -X POST -H "Content-Type: application/json" \
-d '"value1":"PHONENUMBER","value2":"MESSAGE"' \
https://maker.ifttt.com/trigger/TRIGGER/with/key/KEY &> /dev/null
EOF
这里,<<'EOF'
和 EOF
之间的所有内容都通过完全原样传递,包括引号和 shell 可能尝试解释的参数扩展。
【讨论】:
谢谢。让我试试看。 CAPS 中的值对于这篇文章来说是模糊的,但实际上是常量。这是我为研究生网络安全学生建立的实验室的一部分,因此它是一个封闭的、知名的小组。他们需要找到这种妥协。 如果一切都是不变的,那么根本没有理由使用printf %q
;其目的是获取仅动态已知的内容,并找到一种方法将它们格式化为不能被解释为代码的文字。如果您以熟悉语言语法的人的身份编写这些文字,您已经可以防止它们被手动解析为非文字。
考虑到这一点,是否有更简单的方法将命令字符串附加到 .profile 文件?
这是我在答案后半部分展示的heredoc方法。以上是关于使用 printf %q 使带引号的字符串可用作 shell 脚本输入的主要内容,如果未能解决你的问题,请参考以下文章