从shell脚本导入函数

Mat*_*teo 48 shell

我有一个shell脚本,我想用shUnit测试.脚本(和所有功能)都在一个文件中,因为它使安装更容易.

示例 script.sh

#!/bin/sh

foo () { ... }
bar () { ... }

code
Run Code Online (Sandbox Code Playgroud)

我想编写第二个文件(不需要分发和安装)来测试中定义的函数 script.sh

就像是 run_tests.sh

#!/bin/sh

. script.sh

# Unit tests
Run Code Online (Sandbox Code Playgroud)

现在的问题在于.(或source在Bash中).它不仅解析函数定义,还执行脚本中的代码.

由于没有参数的脚本没有任何坏处,我可以

. script.sh > /dev/null 2>&1
Run Code Online (Sandbox Code Playgroud)

但如果有更好的方法来实现我的目标,我就会徘徊.

编辑

我建议的解决方法在源脚本调用的情况下不起作用,exit所以我必须捕获退出

#!/bin/sh

trap run_tests ERR EXIT

run_tests() {
   ...
}

. script.sh
Run Code Online (Sandbox Code Playgroud)

run_tests调用该函数但是只要我重定向source命令的输出,脚本中的函数就不会被解析,并且在陷阱处理程序中不可用

这有效,但我得到的输出script.sh:

#!/bin/sh
trap run_tests ERR EXIT
run_tests() {
   function_defined_in_script_sh
}
. script.sh
Run Code Online (Sandbox Code Playgroud)

这不打印输出但是我得到一个错误,该函数未定义:

#!/bin/sh
trap run_tests ERR EXIT
run_tests() {
   function_defined_in_script_sh
}
. script.sh | grep OUTPUT_THAT_DOES_NOT_EXISTS
Run Code Online (Sandbox Code Playgroud)

这不会打印输出,并且run_tests根本不会调用陷阱处理程序:

#!/bin/sh
trap run_tests ERR EXIT
run_tests() {
   function_defined_in_script_sh
}
. script.sh > /dev/null
Run Code Online (Sandbox Code Playgroud)

and*_*otn 68

根据bash联机帮助页面的"Shell Builtin命令"部分,.aka source也会获取一个可选的参数列表,这些参数将传递给源代码.你可以使用它来引入一个do-nothing选项.例如,script.sh可能是:

#!/bin/sh

foo() {
    echo foo $1
}

main() {
    foo 1
    foo 2
}

if [ "${1}" != "--source-only" ]; then
    main "${@}"
fi
Run Code Online (Sandbox Code Playgroud)

并且unit.sh可能是:

#!/bin/bash

. ./script.sh --source-only

foo 3
Run Code Online (Sandbox Code Playgroud)

然后script.sh将正常运行,并将unit.sh访问所有函数,script.sh但不会调用main()代码.

请注意,额外的参数source不在POSIX中,所以/bin/sh可能无法处理它 - 因此#!/bin/bash在开头unit.sh.

  • “shift”在那里没有意义,因为“main”仅在“--source-only”不是第一个参数时运行。 (4认同)
  • 您可能想要“移动”参数列表,以便“main”不会与“--source-only”联系 (2认同)

Doc*_*ger 19

从Python中学到了这个技术,但这个概念在bash或任何其他shell中运行得很好......

我们的想法是将脚本的主代码部分转换为函数.然后在脚本的最后,我们放置一个'if'语句,只有在我们执行脚本时才调用该函数,但如果我们来源它则不会.然后我们从'runtests'脚本中显式调用script()函数,该脚本已经获取了'script'脚本,因此包含了它的所有功能.

这依赖于以下事实:如果我们获取脚本,bash维护的环境变量$0(正在执行的脚本的名称)将是调用(父)脚本的名称(runtests在本例中),而不是源脚本.

(我已经重命名script.sh,只是script因为这.sh是多余的,让我困惑.:-)

以下是两个脚本.一些笔记......

  • $@计算作为单个字符串传递给函数或脚本的所有参数.相反,如果我们使用$*,所有参数将被连接成一个字符串.
  • RUNNING="$(basename $0)"是必需的,因为$0始终至少包括当前目录前缀,如./script.
  • 测试if [[ "$RUNNING" == "script" ]]....script只有script直接从命令行运行才能调用script()函数的魔力 .

脚本

#!/bin/bash

foo ()    { echo "foo()"; }

bar ()    { echo "bar()"; }

script () {
  ARG1=$1
  ARG2=$2
  #
  echo "Running '$RUNNING'..."
  echo "script() - all args:  $@"
  echo "script() -     ARG1:  $ARG1"
  echo "script() -     ARG2:  $ARG2"
  #
  foo
  bar
}

RUNNING="$(basename $0)"

if [[ "$RUNNING" == "script" ]]
then
  script "$@"
fi
Run Code Online (Sandbox Code Playgroud)

runtests

#!/bin/bash

source script 

# execute 'script' function in sourced file 'script'
script arg1 arg2 arg3
Run Code Online (Sandbox Code Playgroud)

  • 这有两个缺点:重命名脚本时,必须更改其内容;而且它不适用于符号链接(不确定别名)。 (2认同)

Hel*_*ira 7

如果您正在使用Bash,则可以使用BASH_SOURCE数组来完成@andrewdotn方法的类似解决方案(但不需要额外的标志或取决于脚本名称).

script.sh:

#!/bin/bash

foo () { ... }
bar () { ... }

main() {
    code
}

if [[ "${#BASH_SOURCE[@]}" -eq 1 ]]; then
    main "$@"
fi
Run Code Online (Sandbox Code Playgroud)

run_tests.sh:

#!/bin/bash

. script.sh

# Unit tests
Run Code Online (Sandbox Code Playgroud)

  • 如果从命令行获取,它不起作用. (2认同)