简体   繁体   English

Bash在执行前将引号插入字符串

[英]Bash inserting quotes into string before execution

I have managed to track done a weird problem in an init script I am working on.我设法在我正在处理的初始化脚本中跟踪完成了一个奇怪的问题。 I have simplified the problem down in the following example:我在以下示例中简化了问题:

> set -x                           # <--- Make Bash show the commands it runs
> cmd="echo \"hello this is a test\""
+ cmd='echo "hello this is a test"'
> $cmd
+ echo '"hello' this is a 'test"'  # <--- Where have the single quotes come from?
"hello this is a test"

Why is bash inserting those extra single quotes into the executed command?为什么 bash 将那些额外的单引号插入到执行的命令中?

The extra quotes don't cause any problems in the above example, but they are really giving me a headache.在上面的例子中,额外的引号不会引起任何问题,但它们确实让我头疼。

For the curious, the actual problem code is:对于好奇,实际问题代码是:

cmd="start-stop-daemon --start $DAEMON_OPTS \
    --quiet \
    --oknodo \
    --background \
    --make-pidfile \
    $* \
    --pidfile $CELERYD_PID_FILE
    --exec /bin/su -- -c \"$CELERYD $CELERYD_OPTS\" - $CELERYD_USER"

Which produces this:产生这个:

start-stop-daemon --start --chdir /home/continuous/ci --quiet --oknodo --make-pidfile --pidfile /var/run/celeryd.pid --exec /bin/su -- -c '"/home/continuous/ci/manage.py' celeryd -f /var/log/celeryd.log -l 'INFO"' - continuous

And therefore:因此:

/bin/su: invalid option -- 'f'

Note: I am using the su command here as I need to ensure the user's virtualenv is setup before celeryd is run.注意:我在这里使用su命令,因为我需要确保在运行 celeryd 之前设置用户的 virtualenv。 --chuid will not provide this --chuid不会提供这个

Because when you try to execute your command with因为当您尝试执行命令时

$cmd

only one layer of expansion happens.只有一层扩展发生。 $cmd contains echo "hello this is a test" , which is expanded into 6 whitespace-separated tokens: $cmd包含echo "hello this is a test" ,它被扩展为 6 个以空格分隔的标记:

  1. echo
  2. "hello
  3. this
  4. is
  5. a
  6. test"

and that's what the set -x output is showing you: it's putting single quotes around the tokens that contain double quotes, in order to be clear about what the individual tokens are.这就是set -x输出向您显示的内容:它在包含双引号的标记周围加上单引号,以便清楚各个标记是什么。

If you want $cmd to be expanded into a string which then has all the bash quoting rules applied again, try executing your command with:如果您希望将$cmd扩展为一个字符串,然后再次应用所有 bash 引用规则,请尝试使用以下命令执行您的命令:

bash -c "$cmd"

or (as @bitmask points out in the comments, and this is probably more efficient)或(正如@bitmask 在评论中指出的,这可能更有效)

eval "$cmd"

instead of just而不仅仅是

$cmd

Use Bash arrays to achieve the behavior you want, without resorting to the very dangerous (see below) eval and bash -c .使用 Bash 数组来实现您想要的行为,而无需求助于非常危险的(见下文) evalbash -c

Using arrays:使用数组:

declare -a CMD=(echo --test-arg \"Hello\ there\ friend\")
set -x
echo "${CMD[@]}"
"${CMD[@]}"

outputs:输出:

+ echo echo --test-arg '"Hello there friend"'
echo --test-arg "Hello there friend"
+ echo --test-arg '"Hello there friend"'
--test-arg "Hello there friend"

Be careful to ensure that your array invocation is wrapped by double-quotes;小心确保你的数组调用被双引号包裹; otherwise, Bash tries to perform the same "bare-minimum safety" escaping of special characters:否则,Bash 会尝试执行相同的“最低限度安全”转义特殊字符:

declare -a CMD=(echo --test-arg \"Hello\ there\ friend\")
set -x
echo "${CMD[@]}"
${CMD[@]}

outputs:输出:

+ echo echo --test-arg '"Hello there friend"'
echo --test-arg "Hello there friend"
+ echo --test-arg '"Hello' there 'friend"'
--test-arg "Hello there friend"

ASIDE: Why is eval dangerous?旁白:为什么eval是危险的?

eval is only safe if you can guarantee that every input passed to it will not unexpectedly change the way that the command under eval works. eval只有在您可以保证传递给它的每个输入都不会意外改变eval下命令的工作方式时才是安全的。

Example : As a totally contrived example, let's say we have a script that runs as part of our automated code deployment process.示例:作为一个完全人为设计的示例,假设我们有一个脚本作为我们自动化代码部署过程的一部分运行。 The script sorts some input (in this case, three lines of hardcoded text), and outputs the sorted text to a file whose named is based on the current directory name.该脚本对一些输入(在本例中为三行硬编码文本)进行排序,并将排序后的文本输出到一个基于当前目录名称命名的文件中。 Similar to the original SO question posed here, we want to dynamically construct the --output= parameter passed to sort, but we must (must? not really) rely on eval because of Bash's auto-quoting "safety" feature.与此处提出的原始 SO 问题类似,我们希望动态构造传递给 sort 的--output=参数,但由于 Bash 的自动引用“安全”功能,我们必须(必须?不是真的)依赖eval

echo $'3\n2\n1' | eval sort -n --output="$(pwd | sed 's:.*/::')".txt

Running this script in the directory /usr/local/deploy/project1/ results in a new file being created at /usr/local/deploy/project1/project1.txt .在目录/usr/local/deploy/project1/中运行此脚本会导致在/usr/local/deploy/project1/project1.txt中创建一个新文件。

So somehow, if a user were to create a project subdirectory named owned.txt; touch hahaha.txt; echo所以不知何故,如果用户要创建一个名为owned.txt; touch hahaha.txt; echo的项目子目录owned.txt; touch hahaha.txt; echo owned.txt; touch hahaha.txt; echo owned.txt; touch hahaha.txt; echo , the script would actually run the following series of commands: owned.txt; touch hahaha.txt; echo ,脚本实际上会运行以下一系列命令:

echo $'3\n2\n1'
sort -n --output=owned.txt; touch hahaha.txt; echo .txt

As you can see, that's totally not what we want.如您所见,这完全不是我们想要的。 But you may ask, in this contrived example, isn't it unlikely that the user could create a project directory owned.txt; touch hahaha.txt; echo但是你可能会问,在这个人为的例子中,用户创建项目目录owned.txt; touch hahaha.txt; echo是不是不太可能owned.txt; touch hahaha.txt; echo owned.txt; touch hahaha.txt; echo owned.txt; touch hahaha.txt; echo , and if they could, aren't we already in trouble already? owned.txt; touch hahaha.txt; echo ,如果他们可以的话,我们不是已经遇到麻烦了吗?

Maybe, but what about a scenario where the script is parsing not the current directory name, but instead the name of a remote git source code repository branch?也许吧,但是如果脚本不是解析当前目录名称,而是解析远程git源代码存储库分支的名称,那该怎么办? Unless you plan to be extremely diligent about restricting or sanitizing every user-controlled artifact whose name, identifier, or other data is used by your script, stay well clear of eval .除非您计划非常努力地限制或清理每个用户控制的工件,这些工件的名称、标识符或其他数据被您的脚本使用,否则请远离eval

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM