我想做以下事情:
#!/bin/bash
cmd="find . -name '*.sh'"
echo $($cmd)
我期望它会显示当前目录中的所有shell脚本文件,但什么也没发生。
我知道我可以根据此post
解决eval问题#!/bin/bash
cmd="find . -name '*.sh'"
eval $cmd
所以我的问题是为什么命令替换在这里不起作用,$(...)和eval在问题方面的区别是什么?
答案 0 :(得分:1)
由于您已经在双引号中包含了模式*.sh
,因此不需要单引号来保护模式,因此单引号是模式的 part 。
您可以尝试使用数组来保持*.sh
引用,直到它被传递给命令替换:
cmd=(find . -name '*.sh')
echo $("${cmd[@]}")
我不知道是否有一种简单的方法可以将原始字符串转换为数组而不扩展模式。
更新:这不是太坏,但如果可以的话,最好直接创建数组。
cmd="find . -name *.sh"
set -f
cmd=($cmd)
set +f
echo $("${cmd[@]}")
答案 1 :(得分:1)
命令替换在这里工作。只是你有错误的引用。您的脚本只找到一个文件名!这个带有单引号和星号:
'*.sh'
您可以通过此命令创建此类非常用文件并对其进行测试:
touch "'*.sh'"
bash中的引用与其他编程语言不同。查看本文中的详细信息: White Shell
你需要的是这个引用:
cmd="find . -name *.sh"
echo $($cmd)
答案 2 :(得分:0)
当您使用echo $($cmd)
语法时,它基本上等同于将$cmd
放在它自己的行上。问题是bash想要在命令运行之前插入通配符的方式。防止这种情况的方法是在脚本中取消引用时将包含*
字符的变量放在引号中。
但是如果你将整个命令find . -name "*.sh"
放在一个变量中,然后用`echo $(“$ cmd”)引用它,那么shell会将其解释为意味着整行是一个要执行的文件,并且你得到一个文件未找到错误。
所以它真的取决于你在变量中真正需要什么以及可以从中取出什么。如果您需要变量中的程序,这将起作用:
#!/bin/bash
cmd='/usr/bin/find'
$cmd . -name "*.sh" -maxdepth 1
这将找到当前工作目录中以.sh
结尾但没有shell插入通配符的所有文件。
如果您需要将模式放在变量中,可以使用:
#!/bin/bash
pattern="*.sh"
/usr/bin/find . -name "$pattern" -maxdepth 1
但如果你将整个事物放在一个变量中,你就不会得到你所期望的。希望这可以帮助。如果一个bash guru知道我错过了什么,我很乐意听到它。