Bash:如何包含其他脚本?

通常包含脚本的方式是“源”

例如:

main.sh:

#!/bin/bash source incl.sh echo "The main script" 

incl.sh:

 echo "The included script" 

执行“./main.sh”的输出是:

 The included script The main script 

…现在,如果您尝试从另一个位置执行该shell脚本,除非它位于您的path中,否则无法find这个包含脚本的脚本。

什么是确保脚本可以find包含脚本的好方法,特别是如果脚本需要可移植?

我倾向于使我的脚本都是相对于彼此。 这样我可以使用dirname:

 #!/bin/sh my_dir="$(dirname "$0")" "$my_dir/other_script.sh" 

我知道我迟到了,但是无论你如何开始脚本并独占使用内build脚本,这都应该起作用:

 DIR="${BASH_SOURCE%/*}" if [[ ! -d "$DIR" ]]; then DIR="$PWD"; fi . "$DIR/incl.sh" . "$DIR/main.sh" 

. (dot)命令是源的别名, $PWD是工作目录的path, BASH_SOURCE是一个数组variables,其成员是源文件名, ${string%substring}从$ string后面剥离$ substring的最短匹配

替代:

 scriptPath=$(dirname $0) 

是:

 scriptPath=${0%/*} 

..好处是没有依赖dirname,这不是一个内置的命令(并不总是在模拟器中可用)

如果它在同一个目录下,你可以使用dirname $0

 #!/bin/bash source $(dirname $0)/incl.sh echo "The main script" 

我认为最好的办法是使用Chris Boran的方式,但是你应该这样计算MY_DIR:

 #!/bin/sh MY_DIR=$(dirname $(readlink -f $0)) $MY_DIR/other_script.sh 

引用readlink的手册页:

 readlink - display value of a symbolic link ... -f, --canonicalize canonicalize by following every symlink in every component of the given name recursively; all but the last component must exist 

我从来没有遇到一个用例, MY_DIR不正确的计算。 如果您通过$PATH的符号链接访问您的脚本,它将起作用。

 SRC=$(cd $(dirname "$0"); pwd) source "${SRC}/incl.sh" 

即使脚本来源:

 source "$( dirname "${BASH_SOURCE[0]}" )/incl.sh" 

您需要指定其他脚本的位置,没有其他方法。 我build议在脚本的顶部有一个可configuration的variables:

 #!/bin/bash installpath=/where/your/scripts/are . $installpath/incl.sh echo "The main script" 

另外,你可以坚持用户维护一个环境variables,指出你的程序所在的位置,如PROG_HOME或其他。 这可以通过在/etc/profile.d/中创build一个具有该信息的脚本自动提供给用户,每次用户login时都会提供该脚本。

所有这些解决scheme都不适合我。

请使用更健壮的方法:

  #!/斌/庆典

 #此脚本的完整path
 THIS =`readlink -f“$ {BASH_SOURCE [0]}”2> / dev / null || echo $ 0`

 #这个目录path
 DIR =`dirname“$ {THIS}”`

 #“点”的意思是“源”,即“包含”
 。  “$ DIR / compile.sh” 

它支持:

  • path中的空间
  • 链接 (通过readlink
  • ${BASH_SOURCE[0]}$0更健壮

我build议你创build一个setenv脚本,其唯一目的是为系统中的各种组件提供位置。

然后,所有其他脚本将获取此脚本,以便所有位置在使用setenv脚本的所有脚本中都是公共的。

这在运行cronjobs时非常有用。 在运行cron时,你会得到一个最小的环境,但是如果你让所有的cron脚本首先包含setenv脚本,那么你就可以控制和同步你想让cronjob执行的环境。

我们在我们的构build猴子上使用了这样的技术,用于在大约2000 kSLOC的项目中持续集成。

史蒂夫的答复绝对是正确的技术,但它应该重构,以便您的installpathvariables是在一个单独的环境脚本中所有这样的声明进行。

然后,所有脚本都会生成该脚本,并且应该安装path更改,您只需将其更改为一个位置即可。 使事情更多,呃,未来的保障。 上帝,我恨这个词! ( – :

顺便说一句,你应该使用$ {installpath}来真正引用variables,如下例所示:

 . ${installpath}/incl.sh 

如果大括号被遗漏了,一些shell会尝试扩展variables“installpath / incl.sh”!

我把所有的启动脚本放在.bashrc.d目录下。 这是在/etc/profile.d等地方常用的技术。

 while read file; do source "${file}"; done <<HERE $(find ${HOME}/.bashrc.d -type f) HERE 

使用globbing的解决scheme的问题…

 for file in ${HOME}/.bashrc.d/*.sh; do source ${file};done 

…你可能有一个“太长”的文件列表。 像…一样

 find ${HOME}/.bashrc.d -type f | while read file; do source ${file}; done 

…运行,但不会根据需要更改环境。

当然,对于他们自己,但我认为下面的块是相当稳固的。 我相信这涉及到find一个目录的“最佳”方法,以及调用另一个bash脚本的“最佳”方法:

 scriptdir=`dirname "$BASH_SOURCE"` source $scriptdir/incl.sh echo "The main script" 

所以这可能是包含其他脚本的“最佳”方式。 这是基于另一个“最好”的答案, 告诉bash脚本存储在哪里

这应该可靠地工作:

 source_relative() { local dir="${BASH_SOURCE%/*}" [[ -z "$dir" ]] && dir="$PWD" source "$dir/$1" } source_relative incl.sh 

使用源代码或$ 0不会给你脚本的真实path。 您可以使用脚本的进程ID来检索其真实path

 ls -l /proc/$$/fd | grep "255 ->" | sed -e 's/^.\+-> //' 

我正在使用这个脚本,它一直为我服务:)

我们只需要find我们的incl.sh和main.sh所在的文件夹; 只需改变你的main.sh:

main.sh

 #!/bin/bash SCRIPT_NAME=$(basename $0) SCRIPT_DIR="$(echo $0| sed "s/$SCRIPT_NAME//g")" source $SCRIPT_DIR/incl.sh echo "The main script" 

Shell脚本加载器是我的解决scheme。

它提供了一个名为include()的函数,可以在多个脚本中多次调用这个函数来引用单个脚本,但只会载入脚本一次。 该函数可以接受完整path或部分path(在searchpath中search脚本)。 还提供了一个名为load()的类似函数,它将无条件加载脚本。

它适用于bashkshpd kshzsh,并为其中的每一个优化脚本; 以及其他与原始sh一样兼容的其他shell,例如破折号传家宝sh等等,通过通用脚本,根据shell可以提供的function自动优化其function。

[Fooarded示例]

start.sh

这是一个可选的启动脚本。 在这里放置启动方法只是一种方便,可以放在主脚本中。 如果脚本被编译,这个脚本也不需要。

 #!/bin/sh # load loader.sh . loader.sh # include directories to search path loader_addpath /usr/lib/sh deps source # load main script load main.sh 

main.sh

 include a.sh include b.sh echo '---- main.sh ----' # remove loader from shellspace since # we no longer need it loader_finish # main procedures go from here # ... 

 include main.sh include a.sh include b.sh echo '---- a.sh ----' 

b.sh

 include main.sh include a.sh include b.sh echo '---- b.sh ----' 

输出:

 ---- b.sh ---- ---- a.sh ---- ---- main.sh ---- 

什么是最好的基于它的脚本也可以编译成一个单一的脚本与可用的编译器。

这是一个使用它的项目: http : //sourceforge.net/p/playshell/code/ci/master/tree/ 。 它可以在编译或不编译脚本的情况下运行。 编译生成一个单一的脚本也可以发生,并在安装过程中很有帮助。

我还为任何一个保守党派创build了一个更简单的原型,这个原型可能希望对实现脚本的工作原理有一个简要的了解: https : //sourceforge.net/p/loader/code/ci/base/tree/loader-include-prototype .bash 。 它很小,任何人都可以将代码包含在他们的主脚本中,如果他们的代码是用Bash 4.0或更新版本来运行的话,也不会使用eval

你也可以使用:

 PWD=$(pwd) source "$PWD/inc.sh"