我想要做如下的事情:
#!/bin/bash
cmd="find . -name '*.sh'"
echo $($cmd)
我希望它会显示当前目录中所有的Shell脚本文件,但什么也没发生。
我知道可以通过eval根据这个post来解决问题。
#!/bin/bash
cmd="find . -name '*.sh'"
eval $cmd
所以我的问题是为什么命令替换在这里不起作用,$(...)和eval在这个问题上有什么区别?
'*.sh'
touch "'*.sh'"
在bash中,引用的方式与其他编程语言不同。请查看答案了解详细信息。
你需要使用以下引用:
cmd="find . -name *.sh"
echo $($cmd)
既然您已经在双引号内包含了模式*.sh
,就没有必要使用单引号来保护该模式,因此单引号是模式的一部分。
您可以尝试使用数组来保持*.sh
在传递给命令替换之前被引用:
cmd=(find . -name '*.sh')
echo $("${cmd[@]}")
我不知道是否有一种简单的方法可以将原始字符串转换为数组,而无需扩展模式。
更新:这并不是太糟糕,但如果可能的话,最好直接创建数组。
cmd="find . -name *.sh"
set -f
cmd=($cmd)
set +f
echo $("${cmd[@]}")
find
命令后加上 -maxdepth 1
。 - platforms-maxdepth 1
运行它,结果它遍历了目录并在下面找到了更多的 shell 脚本。使用该选项后,它将搜索范围限制在当前工作目录中。 - platforms*.sh
也会匹配当前目录中的所有shell脚本,但不允许您根据文件类型、访问时间、文件大小或其他find
提供的属性进行过滤。 - chepner当你使用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大师知道我漏掉了什么,请告诉我。