如何在字符串中引用变量扩展以避免分词?

Tim*_*Tim 10 shell bash sudo quoting

$ myvar="/path to/my directory"
$ sudo bash -c "cd $myvar"
Run Code Online (Sandbox Code Playgroud)

在这种情况下,$myvar由于 的值中存在空格,我如何引用以避免分词myvar

Sté*_*las 13

在该代码中没有分(如在未加引号的扩展时拆分变量的功能),因为$myvar不是未加引号。

然而,存在一个命令注入漏洞,$myvar在传递给bash. 所以它的内容被解释为 bash 代码!

那里的空格会导致多个参数被传递给cd,不是因为分,而是因为它们将被解析为 shell 语法中的几个标记。值为bye;reboot,将重新启动!¹

在这里,你会想要:

sudo bash -c 'cd -P -- "$1"' bash "$myvar"
Run Code Online (Sandbox Code Playgroud)

(您将 的内容$myvar作为该内联脚本的第一个参数传递;请注意$myvar$1是如何为各自的外壳引用的,以防止 IFS 分词(和通配符))。

或者:

sudo MYVAR="$myvar" bash -c 'cd -P -- "$MYVAR"'
Run Code Online (Sandbox Code Playgroud)

(在其中传递$myvar环境变量中的内容)。

当然, cd在该内联脚本中运行(除了检查是否root可以cd进入那里)不会获得任何有用的东西。据推测,您希望该脚本在cd那里,然后在那里做其他事情,例如:

sudo bash -c 'cd -P -- "$1" && do-something' bash "$myvar"
Run Code Online (Sandbox Code Playgroud)

如果目的是为了sudo能够cd进入一个您无权访问的目录,那么这实际上是行不通的。

sudo sh -c 'cd -P -- "$1" && exec bash' sh "$myvar"
Run Code Online (Sandbox Code Playgroud)

将开始bash与其当前目录的交互$myvar。但是该 shell 将作为root.

你可以这样做:

sudo sh -c 'cd -P -- "$1" && exec sudo -u "$SUDO_USER" bash' sh "$myvar"
Run Code Online (Sandbox Code Playgroud)

要获得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
Run Code Online (Sandbox Code Playgroud)

一个例外是,如果您确实对目录本身具有搜索权限,但对其路径的目录组件之一没有搜索权限:

$ 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
Run Code Online (Sandbox Code Playgroud)

¹ 严格来说,对于$myvarlike $(seq 10)(字面意思)的值,当然会在bash shell 开始的命令替换扩展后进行分词root