我对在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"
确实有效。
我错过了什么?
答案 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
。