Bash脚本执行带路径的参数化命令

时间:2015-03-06 15:23:12

标签: bash

我对在bash中使用路径作为参数调用命令非常困惑。来自bash变量。

我已将问题缩小到这个范围:

给定一个包含以下内容的目录:

KOSTUNRIX:i18n jand$ ls -al
total 40
drwxr-xr-x  6 jand  staff   204 Mar  6 15:53 .
drwxr-xr-x  3 jand  staff   102 Mar  6 10:27 ..
-rw-r--r--  1 jand  staff  3159 Mar  6 10:47 README.md
-rwxr--r--  1 jand  staff  4504 Mar  6 15:47 diff.sh
-rwxr--r--  1 jand  staff  4080 Mar  6 13:43 takeIn.sh
-rwxr--r--  1 jand  staff   558 Mar  6 15:51 test.sh

脚本test.sh

#!/bin/bash

while [[ $# -gt 1 ]]
do
  key="$1"

  case ${key} in
      --from)
      echo "--from" $2
      from="$2"
      shift
      ;;
      --to)
      echo "--to" $2
      to="$2"
      shift
      ;;
      -c|--command)
      echo "--command" $2
      command="$2"
      shift
      ;;
      *)
      # unknown option
      echo ${key} "- huh?"
      ;;
  esac
  shift
done

pwd
echo $command
iCommand=${command/"\$from"/\"$from\"}
echo $iCommand
iCommand=${iCommand/"\$to"/\"$to\"}
echo ${iCommand}
${iCommand}
cp "./README.md" "./README.mdFORCED"

执行test.sh给出:

KOSTUNRIX:i18n jand$ ./test.sh --command "cp \$from \$to" --from ./README.md --to ./README.md2
--command cp $from $to
--from ./README.md
--to ./README.md2
[...]/i18n
cp $from $to
cp "./README.md" $to
cp "./README.md" "./README.md2"
cp: "./README.md": No such file or directory
KOSTUNRIX:i18n jand$ ls -al
total 48
drwxr-xr-x  7 jand  staff   238 Mar  6 15:57 .
drwxr-xr-x  3 jand  staff   102 Mar  6 10:27 ..
-rw-r--r--  1 jand  staff  3159 Mar  6 10:47 README.md
-rw-r--r--  1 jand  staff  3159 Mar  6 15:57 README.mdFORCED
-rwxr--r--  1 jand  staff  4504 Mar  6 15:47 diff.sh
-rwxr--r--  1 jand  staff  4080 Mar  6 13:43 takeIn.sh
-rwxr--r--  1 jand  staff   558 Mar  6 15:51 test.sh

收集参数(--from--to--command)后,我输出进行调试:

  • pwd - > [...]/i18n
  • echo $command - > cp $from $to
  • echo $iCommand - > cp "./README.md" $to(来自已更换)
  • echo $iCommand - > cp "./README.md" "./README.md2"(替换)

接下来,我执行最后一次输出。让我烦恼的是,这不起作用。答复是:

cp: "./README.md": No such file or directory

然而,当我在终端中复制/粘贴前一个字符串时,它可以工作。作为一个仔细检查,我在脚本中也粘贴了该命令的变体,在最后一行,这也有效,正如您在ls之后可以看到的那样(README.mdFORCED存在)。< / p>

无论尝试使用反引号,${}$(),我都无法使其发挥作用。

(请注意,这是我提出的这个问题的最简单版本。实际问题是使用包含空格的路径执行此操作 - 但我认为,当这一点变得清晰时,也可以解决这个问题。)

使用绝对路径会得到相同的结果:

KOSTUNRIX:i18n jand$ ./test.sh --command "cp \$from \$to" --from `pwd`/README.md --to `pwd`/README.md2
--command cp $from $to
--from /[...]/i18n/README.md
--to /[...]/i18n/README.md2
/[...]/i18n
cp $from $to
cp "/[...]/i18n/README.md" $to
cp "/[...]/i18n/README.md" "/[...]/i18n/README.md2"
cp: "/[...]/i18n/README.md": No such file or directory

再次,手动执行复制/粘贴命令

cp "/[...]/i18n/README.md" "/[...]/i18n/README.md2"

确实有效。

我错过了什么?

2 个答案:

答案 0 :(得分:3)

在代码上进行字符串替换始终是坏主意。就像Bobby Tables can wreck havoc on SQL一样,他的表弟吉米/tmp/$(rm -rf /)/可以破坏贝壳。

现在,有一种更安全的方式来做你想做的事情:不要改变代码;相反,通过环境将参数传递给它。

也就是说:

from="$from" to="$to" eval "$command"

... 没有更改$command来自受信任的用户提供的内容。然后,一切都取决于$command安全写入:如果值为'cp "$from" "$to"',您将是安全的。 (但是,这就是如何在shell中正确编写该命令的方式 - 如果人们习惯在你的组织中编写cp $from $to,那么你们都会遇到更大的问题。


现在,如果你坚持在代码上进行字符串替换,你也可以这样做 - 但是你需要使用bash或其他带有printf %q扩展名的shell来清理变量是eval - 安全,并且您需要确保在command给出的 引用替换发生的地方。 (在这种情况下,'cp $from $to'实际上是可取的。)

printf -v from_str %q "$from"
printf -v to_str %q "$to"
iCommand=${command/'$from'/$from_str}
iCommand=${iCommand/'$to'/$to_str}
eval "$iCommand"

我强烈反对这种方法,因为你要求用户编写在任何其他环境中都有错误的代码 - 任何进行安全审计的人都会倾向于标记这个脚本的用户,除非他们挖得足够远了解正在采取哪些措施以确保使用安全。或者,您可以使用不同的替换变量 - 即。 @from@@to@ - 清楚地表明正在发挥作用的语义不是上游bash的语义。

答案 1 :(得分:-1)

将变量放在一行上通常不足以执行存储在该变量中的命令。

请尝试eval $iCommand