我需要编写一个 shell 脚本,它可以执行.sh
目录模式中存在的所有文件。就像是:
##!/bin/bash
sh /var/scripts/*/my_*.inc.sh
但是上面的脚本只执行一个文件;发生的第一个文件(似乎是按最新修改日期选择的)这不是我的观点。我需要执行与规则匹配的所有文件。
答案1
您的代码不起作用的原因是文件名通配模式扩展时会发生什么。
该模式将扩展到列表匹配的路径名。脚本中的代码将sh
使用此列表进行调用。这意味着它将执行第一个匹配的名称作为脚本,并将其他名称指定为论点到该脚本:
sh /var/scripts/dir/my_first.inc.sh /var/scripts/dir/my_second.inc.sh /var/scripts/dir/my_third.inc.sh
相反,只需迭代匹配的名称:
#!/bin/sh
for name in /var/scripts/*/my_*.inc.sh; do
sh "$name"
done
这假设每个匹配的名称都是应该由sh
(not bash
) 执行的脚本。如果各个文件是可执行的并且具有正确的-line,则从上面循环中的脚本调用中#!
删除。sh
如果文件是“点脚本”,即应获取源的脚本,则替换sh
为.
以使脚本在当前脚本的环境中执行。
请注意,上面的脚本可以是sh
脚本 ( #!/bin/sh
),因为它不使用任何bash
功能。如果其他脚本是“点脚本”,那么您显然可能必须将其更改为#!/bin/bash
或需要任何其他解释器来获取脚本。
答案2
扩展名.inc.sh
表明这些.inc.sh
文件应该是包括,或者换句话说,它们的内容应该被评估为 shell 代码一shell 解释器,以便同一个 shell 解释器可以使用功能,变量,别名...在这些可用文件中定义。
bash
的语法主要与sh
语法向后兼容,特别是当启用其 POSIX 模式时(bash
实际上是一个sh
解释器,当调用为 时sh
),因此即使这些函数大概是用sh
语言编写的,您仍然应该能够让bash
解释器解释它们。
bash
(当不在 POSIX 模式下时) 和之间的一个显着区别sh
是它bash
不支持alias
脚本中的扩展。不过,可以通过设置expand_aliases
选项或posix
选项来解决这个问题,因此您可以执行以下操作:
#! /bin/bash -
set -o posix # increase POSIX sh compatibility, some but not all bash-specific
# extensions are still available.
for file in /var/scripts/*/my_*.inc.sh; do
. "$file"
done
# rest of the code that uses the functions/variables, etc defined
# in the files sourced above with the "." special builtin
请注意,如果没有匹配的文件,则.
在尝试获取字面意义上不存在的文件/var/scripts/*/my_*.inc.sh
并退出 shell 时,该命令将失败。
如果在这种情况下你不想做任何你可以做的事情:
shopt -s nullglob
files=(/var/scripts/*/my_*.inc.sh)
shopt -u nullglob
for file in "${files[@]}"; do
command . "$file"
done
使用 时nullglob
,不匹配的 glob 会扩展为空列表,而不是未扩展的模式。使用command
前缀,特殊内置函数的失败.
(如$file
不可读时)不会导致 shell 退出(并且仍应报告错误)。
要递归查找.inc.sh
文件而不仅仅是 的子目录/var/scripts
,您可以使用该globstar
选项来启用**
运算符:
shopt -s nullglob globstar
files=(/var/scripts/**/my_*.inc.sh)
shopt -u nullglob globstar
.
在任何情况下,名称以(隐藏的)开头的目录和文件都会被省略。dotglob
如果您想包含以下内容,请使用该选项:
显然,如果任何源脚本调用exit
orexec
或 有语法错误或任何在失败内调用的特殊内置函数,也将退出脚本。
答案3
在 shell 脚本(包括 bash)中迭代文件的最安全方法是在 for 循环中使用路径名扩展(通配符)。正确的代码是:
for file in /var/scripts/*/my_*.inc.sh
do
echo "Executing $file"
"$file"
done
这将处理带有特殊字符(例如空格、星号、换行符等)的路径和文件名。
如果你想进行递归搜索,或者使用 globbing 无法提供的选项,find 可以与该-exec
选项一起使用(下面假设最近的 GNU find):
find a/ -executable -type f -name '*.sh' -exec {} \;
如果 find 仍然是必要的,但您要在每个文件上执行多个命令,则read
可以使用,但您应该小心处理所有文件名的选项(下面假设 bash 和 GNU find):
#!/bin/bash
find a/ -executable -type f -name '*.sh' -print0 |
while IFS= read -r -d $'\0' file
do
echo "Executing $file"
"$file"
done
答案4
您可以使用for
循环(递归地)迭代通过 找到的路径中的所有 shell 文件find
,如下所示:
for i in $(find /var/scripts/ -type f -name '*.sh')
do sh "$i"
done