通常包含脚本的方式是"source"

eg:

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脚本,它将无法找到包含,除非它在您的路径中。

确保脚本能够找到包含脚本的好方法是什么,特别是在脚本需要可移植的情况下?


当前回答

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

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

使用通配符解决方案的问题…

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

...你的文件列表可能太长了。 一种方法是……

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

…运行但不按预期改变环境。

其他回答

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

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

引用readlink的手册页:

Readlink -显示符号链接的值 ... - f,规范化 通过遵循给定组件中的每个符号链接进行规范化 递归地名称;除最后一个组件外,所有组件都必须存在

我从未遇到MY_DIR计算不正确的用例。如果你通过$PATH中的符号链接访问你的脚本,它就可以工作。

您需要指定其他脚本的位置,没有其他方法可以绕过它。我建议在你的脚本顶部设置一个可配置的变量:

#!/bin/bash
installpath=/where/your/scripts/are

. $installpath/incl.sh

echo "The main script"

或者,您可以坚持让用户维护一个环境变量来指示您的程序所在的位置,比如PROG_HOME或类似的变量。这可以通过在/etc/profile中创建带有该信息的脚本自动提供给用户D /,它将在用户每次登录时被引用。

这是一个很好的函数。它建立在@sacii所做的基础上。谢谢你!

它将允许您列出任意数量的空格分隔的脚本名称到source(相对于调用source_files的脚本)。

可选的是,你可以传递一个绝对路径或相对路径作为第一个参数,它将从那里来源。

您可以多次调用它(参见下面的示例)以从不同的dirs中获取脚本

#!/usr/bin/env bash

function source_files() {
  local scripts_dir
  scripts_dir="$1"

  if [ -d "$scripts_dir" ]; then
    shift
  else
    scripts_dir="${BASH_SOURCE%/*}"
    if [[ ! -d "$scripts_dir" ]]; then scripts_dir="$PWD"; fi
  fi

  for script_name in "$@"; do
    # shellcheck disable=SC1091 disable=SC1090
    . "$scripts_dir/$script_name.sh"
  done
}

下面是一个示例,您可以运行来展示如何使用它

#!/usr/bin/env bash

function source_files() {
  local scripts_dir
  scripts_dir="$1"

  if [ -d "$scripts_dir" ]; then
    shift
  else
    scripts_dir="${BASH_SOURCE%/*}"
    if [[ ! -d "$scripts_dir" ]]; then scripts_dir="$PWD"; fi
  fi

  for script_name in "$@"; do
    # shellcheck disable=SC1091 disable=SC1090
    . "$scripts_dir/$script_name.sh"
  done
}

## -- EXAMPLE -- ##
# assumes dir structure:
# /
#   source_files.sh
#   sibling.sh
#   scripts/
#     child.sh
#   nested/
#     scripts/
#       grandchild.sh

cd /tmp || exit 1

# sibling.sh
tee sibling.sh <<- EOF > /dev/null 
  #!/usr/bin/env bash
  
  export SIBLING_VAR='sibling var value'
EOF

# scripts/child.sh
mkdir -p scripts
tee scripts/child.sh <<- EOF > /dev/null
  #!/usr/bin/env bash

  export CHILD_VAR='child var value'
EOF

# nested/scripts/grandchild.sh
mkdir -p nested/scripts
tee nested/scripts/grandchild.sh <<- EOF > /dev/null
  #!/usr/bin/env bash

  export GRANDCHILD_VAR='grandchild var value'
EOF

source_files 'sibling'
source_files 'scripts' 'child'
source_files 'nested/scripts' 'grandchild'

echo "$SIBLING_VAR"
echo "$CHILD_VAR"
echo "$GRANDCHILD_VAR"

rm sibling.sh
rm -rf scripts nested

cd - || exit 1

打印:

sibling var value
child var value
grandchild var value

我们只需要找到include .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"

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

然后,所有其他脚本将源此脚本,以便使用setenv脚本的所有脚本的所有位置都是通用的。

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

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