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

如何在字符串内引用变量扩展以避免分词?
$ myvar="/path to/my directory"
$ sudo bash -c "cd $myvar"

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

答案1

没有分词(如在未加引号的扩展上分割变量的功能)在该代码中,as$myvar不是未加引号的。

然而,存在一个命令注入漏洞,$myvar在传递到 之前已展开bash。所以它的内容被解释为bash代码!

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

在这里,您想要:

sudo bash -c 'cd -P -- "$1"' bash "$myvar"

(您将 的内容$myvar作为该内联脚本的第一个参数传递;请注意如何为各自的 shell 引用$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

严格来说,对于$myvarlike的值(字面意思),当然,在 shell$(seq 10)扩展该命令替换时,会出现分词bashroot

答案2

新的(bash 4.4)引用魔法可以让你更直接地做你想做的事。根据更大的上下文,使用其他技术之一可能会更好,但它在有限的上下文中确实有效。

sudo bash -c "cd ${myvar@Q}; pwd"

答案3

如果您不能信任 的内容$myvar,唯一合理的方法是使用 GNUprintf创建它的转义版本:

#!/bin/bash

myvar=$(printf '%q' "$myvar")
bash -c "echo $myvar"

正如printf手册页所说:

%q

ARGUMENT 以可重复用作 shell 输入的格式打印,并使用建议的 POSIX$''语法转义不可打印的字符。

答案4

Stéphane Chazelas 建议的绝对是做到这一点的最佳方法。我只是提供一个关于如何安全地使用参数扩展语法引用而不是使用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

相关内容