我想做如下的事情:
#!/bin/bash
cmd="find . -name '*.sh'"
echo $($cmd)
我期望的是它会显示当前目录中的所有 shell 脚本文件,但什么也没发生。
我知道我可以根据这篇文章用 eval 解决问题
#!/bin/bash
cmd="find . -name '*.sh'"
eval $cmd
所以我的问题是为什么命令替换在这里不起作用,就问题而言,$(...) 和 eval 有什么区别?
我想做如下的事情:
#!/bin/bash
cmd="find . -name '*.sh'"
echo $($cmd)
我期望的是它会显示当前目录中的所有 shell 脚本文件,但什么也没发生。
我知道我可以根据这篇文章用 eval 解决问题
#!/bin/bash
cmd="find . -name '*.sh'"
eval $cmd
所以我的问题是为什么命令替换在这里不起作用,就问题而言,$(...) 和 eval 有什么区别?
由于您已经将模式包含*.sh
在双引号中,因此不需要单引号来保护模式,因此单引号是模式的一部分。
您可以尝试使用数组来保持*.sh
引用,直到它被传递给命令替换:
cmd=(find . -name '*.sh')
echo $("${cmd[@]}")
我不知道是否有一种简单的方法可以在不扩展模式的情况下将原始字符串转换为数组。
更新:这还不错,但如果可以的话,直接创建数组可能会更好。
cmd="find . -name *.sh"
set -f
cmd=($cmd)
set +f
echo $("${cmd[@]}")
命令替换在这里有效。只是你引用错误。您的脚本只找到一个文件名!这个带有单引号和星号的:
'*.sh'
您可以通过此命令创建此类不常见的文件并对其进行测试:
touch "'*.sh'"
bash 中的引用不同于其他编程语言。查看本文中的详细信息: 白壳
你需要的是这个引用:
cmd="find . -name *.sh"
echo $($cmd)
当你使用echo $($cmd)
语法时,它基本上相当于只是放在$cmd
它自己的行上。问题是 bash 希望在命令运行之前插入通配符的方式。防止这种情况发生的方法是,*
当您在脚本中取消引用它们时,将包含 char 的变量再次放在引号中。
但是如果你把整个命令find . -name "*.sh"
放在一个变量中,然后用 `echo $("$cmd") 引用它,shell 会解释这意味着整行是一个要执行的文件,你会得到一个 file not found 错误.
因此,这实际上取决于您在变量中真正需要什么以及可以从中提取什么。如果您需要变量中的程序,这将起作用:
#!/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 大师知道我想念的东西,我很想听听。