bash 中的 sudo echo 仅适用于双引号,但不适用于单引号

sudo echo in bash only work with double quote, but not single quote

我有 2 个变量 PASSWORD="123" 和 FILEPATH="/dir-1/dir2",它们都导出为对所有用户可见的环境变量。

我正在尝试回显到文件 /dir-1/dir2/file,该文件属于 root 作为 user1。 我知道这个问题是因为 shell 重定向不适用于 sudo,即 sudo echo <TEXT> >> <FILE>,所以我所做的是:

sudo bash -c 'echo "MY.Password $PASSWORD" >> ${FILEPATH}/file'

sudo bash -c 'echo MY.Password $PASSWORD >> ${FILEPATH}/file'

我原以为文件的最后一行是:
MY.Password 123
但是,尽管变量被正确替换,MY.Password 123 的预期内容并没有出现在 /dir-1/dir2/file 文件的最后一行。

然后我尝试了

sudo bash -c "echo My.Password '$PASSWORD' >> ${FILEPATH}/file"  

sudo bash -c "echo My.Password $PASSWORD >> ${FILEPATH}/file"

两者都有效。
然后我开始想是不是因为单引号的缘故,我尝试用双引号将单引号转义:

sudo bash -c 'echo "'"MY.Password $PASSWORD"'" >> "'"${FILEPATH}"'"/file'

这个看起来很奇怪的命令也有效...

谁能解释为什么我的命令不起作用,为什么最后一个看起来很奇怪的命令起作用了?

sudo 默认情况下不会通过您的环境。 sudo 有一个选项 -E--preserve-env 可以在 sudo 下保留命令 运行 中的现有环境。示例:

# let's set some variable `a`
a=Hello
# will print an empty line
sudo sh -c 'echo $a'
# will print Hello
sudo sh -c "echo $a"
# will print an empty line
sudo -E sh -c 'echo $a'
# because first we need to make `a` exported
export a
# this will print Hello
sudo -E sh -c 'echo $a'

当您使用双引号时,扩展发生在调用 sudo 之前。所以 expansion/execution 是这样的:

$ sudo sh -c "echo $a"
+ sudo sh -c 'echo Hello'
+ sh -c 'echo Hello'
+ echo Hello
Hello

当您使用单引号时,expansion/execution 如下所示:

$ sudo sh -c 'echo $a'
+ sh -c 'echo $a'
+ echo $a
# now it depends if `$a` exists here, after `sudo` inside `sh`
+ echo

如果变量不在环境中,它将展开为空。

sudo bash -c 'echo "MY.Password $PASSWORD" >> ${FILEPATH}/file'

PASSWORDFILEPATH 变量未在 sudo 中设置,因此它们扩展为空字符串。

sudo bash -c "echo My.Password '$PASSWORD' >> ${FILEPATH}/file"  

首先在您的 shell 中扩展了 PASSWORDFILEPATH 变量。然后执行sudo,然后执行bash。在 bash 中执行 echo My.Password 'PASSWORD' >> FILEPATH/file 所以它可以工作。

sudo bash -c 'echo "'"MY.Password $PASSWORD"'" >> "'"${FILEPATH}"'"/file'

让我们用换行符拆分它:

sudo bash -c 'echo "'\
"MY.Password $PASSWORD"\
'" >> "'\
"${FILEPATH}"\
'"/file'

因为您的变量在 " 中,所以它们在调用 sudo 之前被展开。然后在 sudo 内,它们已经展开并且值被正确引用,值在子 shell 的 " 内。这是 "most" 的正确形式,因为在 " 中应该正确引用扩展值,这样当 ex. FILEPATH里面有一个space。

我通常这样做:

 sudo bash -c 'echo MY.Password "" >> ""/file' -- "$PASSWORD" "$FILEPATH"

这让我不用担心在 shell 中使用 " 引号,并且仍然在不改变环境的情况下将正确引用的变量传递给 shell。

或者,您可以这样做:

 export PASSWORD FILEPATH
 sudo -E bash -c 'echo MY.Password "$PASSWORD" >> "$FILEPATH"/file'

或者去喝杯茶:

echo  MY.Password "$PASSWORD" | sudo tee -a "$FILEPATH"/file