$ myvar="/path to/my directory"
$ sudo bash -c "cd $myvar"
在这种情况下,$myvar
由于值中的空格,我如何引用以避免单词分裂myvar
?
sudo bash -c
我的命令字符串中有一个变量扩展为可能包含空格的路径名。
$ myvar="/path to/my directory"
$ sudo bash -c "cd $myvar"
在这种情况下,$myvar
由于值中的空格,我如何引用以避免单词分裂myvar
?
sudo bash -c
我的命令字符串中有一个变量扩展为可能包含空格的路径名。
Answers:
有没有分词(如功能,不带引号时扩展分裂变量)在代码$myvar
不带引号。
但是,存在一个命令注入漏洞,该漏洞$myvar
会在传递给之前扩展bash
。因此其内容被解释为bash代码!
那里的空格将导致几个参数传递给cd
,不是因为单词拆分,而是因为它们将在shell语法中被解析为几个标记。值为bye;reboot
,将重新启动!¹
在这里,您需要:
sudo bash -c 'cd -P -- "$1"' bash "$myvar"
(在其中将内容$myvar
作为该内联脚本的第一个参数传递;请注意如何分别对$myvar
和$1
进行引用,以防止它们各自的外壳被使用,以防止IFS单词拆分(和遍历))。
要么:
sudo MYVAR="$myvar" bash -c 'cd -P -- "$MYVAR"'
(您在其中传递$myvar
环境变量的内容的位置)。
当然,仅 cd
在该内联脚本中运行(除了检查是否root
可以cd
插入其中),您将无法获得任何有用的东西。大概,您希望该脚本在cd
那里,然后在其中执行其他操作:
sudo bash -c 'cd -P -- "$1" && do-something' bash "$myvar"
如果要使用sudo
它来cd
进入您否则无法访问的目录,那么这实际上是行不通的。
sudo sh -c 'cd -P -- "$1" && exec bash' sh "$myvar"
将bash
以其当前目录开始互动$myvar
。但是该shell将以方式运行root
。
您可以这样做:
sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
要获得bash
当前目录为的非特权交互$myvar
,但是如果您首先没有权限cd
进入该目录,则即使该目录是您的当前工作目录,您也将无法在该目录中执行任何操作。
$ myvar=/var/spool/cron/crontabs
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ ls
ls: cannot open directory '.': Permission denied
如果您确实具有对目录本身的搜索权限,但对目录路径的目录组件之一没有搜索权限,则为例外:
$ myvar=1/2
$ mkdir -p "$myvar"
$ chmod 0 1
$ cd 1/2
cd: permission denied: 1/2
$ sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
bash-4.4$ pwd
/home/stephane/1/2
bash-4.4$ mkdir 3
bash-4.4$ ls
3
bash-4.4$ cd "$PWD"
bash: cd: /home/stephane/1/2: Permission denied
¹严格来说,对于$myvar
like $(seq 10)
(字面值)的值,当然会在扩展该命令时被bash
shell 替换为root
有一种新的(bash 4.4)引用魔术,使您可以更直接地执行所需的操作。根据更大的上下文,使用其他技术之一可能会更好,但是它确实可以在这种有限的上下文中工作。
sudo bash -c "cd ${myvar@Q}; pwd"
如果您不信任的内容$myvar
,则唯一合理的方法是使用GNU printf
创建它的转义版本:
#!/bin/bash
myvar=$(printf '%q' "$myvar")
bash -c "echo $myvar"
如printf
手册页所述:
%q
以可重用为shell输入的格式打印ARGUMENT,使用建议的POSIX
$''
语法转义不可打印的字符。
printf
只有在GNU系统上没有内置$(printf '%q' "$myvar")
的Shell中运行GNU 时,才会在该处调用GNU printf
。printf
如今,大多数炮弹都已内置。并非所有人都支持,%q
但是当它们支持时,它们会以对应的shell支持的格式引用事物,这不一定与所理解的相同bash
。事实上,bash
的printf
无法正确引用的东西甚至本身的一些值$myvar
在某些地区。
bash-4.3
至少,这仍然是一个命令注入漏洞与myvar=$'\xa3``reboot`\xa3`'
在zh_HK.big5hkscs区域设置实例。
首先,正如其他人指出的那样,您的cd
命令是无用的,因为它发生在立即退出的shell上下文中。但总的来说,这个问题是有道理的。您需要一种用shell引用任意字符串的方法,以便可以在将其解释为shell引用的字符串的上下文中使用它。我的sh技巧页面中有一个示例:
quote () { printf %s\\n "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/" ; }
使用此功能,您可以执行以下操作:
myvar="/path to/my directory"
sudo bash -c "cd $(quote "$myvar")"
斯特凡·查泽拉斯(StéphaneChazelas)提出的建议无疑是做到这一点的最佳方法。我将提供一个关于如何使用参数扩展语法安全引用的答案,而不是sed
像在R ..的答案中那样使用子流程。
myvar='foo \'\''"bar'
quote() {
printf "'%s'" "${1//\'/\'\\\'\'}"
}
printf "value: %s\n" "$myvar"
printf "quoted: %s\n" "$(quote "$myvar")"
bash -c "printf 'interpolated in subshell script: %s\n' $(quote "$myvar")"
该脚本的输出为:
value: foo \'"bar
quoted: 'foo \'\''"bar'
interpolated in subshell script: foo \'"bar
是的,有分词。
好吧,从技术上讲,bash在解析命令行时使用了命令行拆分。
首先让我们引用正确:
我相信您会希望使命令正常工作的最简单(且不安全)的解决方案是:
bash -c "cd \"$myvar\""
让我们确认发生了什么(假设myvar="/path to/my directory"
):
$ bash -c "printf '<%s>\n' $myvar"
<./path>
<to/my>
<directory>
如您所见,路径字符串在空格处分割。和:
$ bash -c "printf '<%s>\n' \"$myvar\""
<./path to/my directory>
不分裂。
但是,命令(如所写)不安全。更好地使用:
$ bash -c "cd -P -- \"$myvar\"; pwd"
/home/user/temp/path to/my directory
这样可以保护cd免受以破折号(-)开头或可能导致问题的链接开头的文件的侵害。
如果您可以相信其中的字符串$myvar
是路径,那将起作用。
但是,由于您正在“执行字符串”,因此“代码注入”仍然可能:
$ myvar='./path to/my directory";date;"true'
$ bash -c "printf '<%s> ' \"$myvar\"; echo"
<./path to/my directory> Tue May 8 12:25:51 UTC 2018
您需要对字符串进行更强的引用,例如:
$ bash -c 'printf "<%s> " "$1"; echo' _ "$myvar"
<./path to/my directory";date -u;"true>
正如您在上面看到的那样,该值未被解释,而只是用作"$1'
。
现在我们可以(安全地)使用sudo了:
$ sudo bash -c 'cd -P -- "$1"; pwd' _ "$myvar"
_: line 0: cd: ./path to/my directory";date -u;"true: No such file or directory
如果有多个参数,则可以使用:
$ sudo bash -c 'printf "<%s>" "$@"' _ "$val1" "$val2" "$val3"
单引号在这里不起作用,因为那样您的变量将不会被扩展。如果您确定路径的变量已被清除,最简单的解决方案是将扩展名放在新的bash shell中,然后在其周围添加引号:
sudo bash -c "cd \"$myvar\""
$myvar
等$(reboot)
或"; reboot; #
sudo bash -c "cd -P -- '$myvar'"
会将问题限制为仅$myvar
包含单引号字符的值,这样更易于清理。
cd
只具有内部效力bash -c
外壳。