如何使用 *.sh 执行不同目录中的多个 .sh 文件

如何使用 *.sh 执行不同目录中的多个 .sh 文件

我需要编写一个 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如果您想包含以下内容,请使用该选项:

显然,如果任何源脚本调用exitorexec或 有语法错误或任何在失败内调用的特殊内置函数,也将退出脚本。

答案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

相关内容