Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/shell/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
从shell脚本导入函数_Shell - Fatal编程技术网

从shell脚本导入函数

从shell脚本导入函数,shell,Shell,我有一个shell脚本,我想用它进行测试。脚本(和所有函数)都在一个文件中,因为它使安装更加容易 script.sh的示例 #!/bin/sh foo () { ... } bar () { ... } code #!/bin/sh . script.sh # Unit tests 我想编写第二个文件(不需要分发和安装)来测试script.sh #!/bin/sh foo () { ... } bar () { ... } code #!/bin/sh . script.sh

我有一个shell脚本,我想用它进行测试。脚本(和所有函数)都在一个文件中,因为它使安装更加容易

script.sh的示例

#!/bin/sh

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

code
#!/bin/sh

. script.sh

# Unit tests
我想编写第二个文件(不需要分发和安装)来测试
script.sh

#!/bin/sh

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

code
#!/bin/sh

. script.sh

# Unit tests
类似于
run\u tests.sh的内容

#!/bin/sh

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

code
#!/bin/sh

. script.sh

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

因为没有参数的脚本没有什么不好的,所以我可以

. script.sh > /dev/null 2>&1
但我在想是否有更好的方法来实现我的目标

编辑

#!/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
在源代码脚本调用
exit
的情况下,我建议的解决方案不起作用,因此我必须捕获退出

#!/bin/sh

trap run_tests ERR EXIT

run_tests() {
   ...
}

. script.sh
调用
run\u tests
函数,但一旦重定向源命令的输出,脚本中的函数就不会被解析,并且在陷阱处理程序中不可用

这是可行的,但我得到了
script.sh
的输出:

#!/bin/sh
trap run_tests ERR EXIT
run_tests() {
   function_defined_in_script_sh
}
. script.sh
这不会打印输出,但我得到一个错误,即未定义函数:

#!/bin/sh
trap run_tests ERR EXIT
run_tests() {
   function_defined_in_script_sh
}
. script.sh | grep OUTPUT_THAT_DOES_NOT_EXISTS
这不会打印输出,并且根本不会调用
run\u tests
trap处理程序:

#!/bin/sh
trap run_tests ERR EXIT
run_tests() {
   function_defined_in_script_sh
}
. script.sh > /dev/null

根据中的“Shell内置命令”部分,
aka
source
获取一个可选的参数列表,这些参数被传递给正在获取源代码的脚本。你可以用它来引入一个什么都不做的选项。例如,
script.sh
可以是:

#!/bin/sh

foo() {
    echo foo $1
}

main() {
    foo 1
    foo 2
}

if [ "${1}" != "--source-only" ]; then
    main "${@}"
fi
#!/bin/bash

. ./script.sh --source-only

foo 3
unit.sh
可以是:

#!/bin/sh

foo() {
    echo foo $1
}

main() {
    foo 1
    foo 2
}

if [ "${1}" != "--source-only" ]; then
    main "${@}"
fi
#!/bin/bash

. ./script.sh --source-only

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


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

的开头,从Python中获得了这项技术,但是这个概念在bash或任何其他shell中都可以正常工作

我们的想法是将脚本的主代码部分转换为函数。然后在脚本的最后,我们放置了一个“if”语句,该语句仅在执行脚本时调用该函数,而在源代码时不调用该函数。然后,我们显式地从“runtests”脚本调用script()函数,该脚本源于“script”脚本,因此包含其所有函数

这取决于这样一个事实:如果我们为脚本提供源代码,bash维护的环境变量
$0
,即正在执行的脚本的名称,将是调用的(父)脚本的名称(
runtests
,在本例中),而不是源代码脚本的名称

(我已将
script.sh
重命名为
script
,因为
.sh
是多余的,让我感到困惑。:-)

下面是两个脚本。一些注释

  • $@
    计算传递给函数的所有参数,或 脚本作为单个字符串。如果改为使用
    $*
    ,则所有 参数将被连接到一个字符串中
  • RUNNING=“$(basename$0)”
    是必需的,因为
    $0
    始终包括在 至少使用
    /script
    中的当前目录前缀
  • 测试
    如果[[“$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
运行测试

#!/bin/bash

source script 

# execute 'script' function in sourced file 'script'
script arg1 arg2 arg3

如果您使用的是Bash,则可以通过使用
Bash\u SOURCE
array来实现与@andrewdotn方法类似的解决方案(但不需要额外的标志或取决于脚本名称)

script.sh:

#!/bin/bash

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

main() {
    code
}

if [[ "${#BASH_SOURCE[@]}" -eq 1 ]]; then
    main "$@"
fi
运行_tests.sh:

#!/bin/bash

. script.sh

# Unit tests

如果您使用的是Bash,另一种解决方案可能是:

#!/bin/bash

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

[[ "${FUNCNAME[0]}" == "source" ]] && return
code

这是我设计的。假设我们的shell库文件是以下文件,名为aLib.sh:

funcs=("a" "b" "c")                   # File's functions' names
for((i=0;i<${#funcs[@]};i++));        # Avoid function collision with existing
do
        declare -f "${funcs[$i]}" >/dev/null
        [ $? -eq 0 ] && echo "!!ATTENTION!! ${funcs[$i]} is already sourced"
done

function a(){
        echo function a
}
function b(){
        echo function b
}
function c(){
        echo function c
}


if [ "$1" == "--source-specific" ];      # Source only specific given as arg
then    
        for((i=0;i<${#funcs[@]};i++));
        do      
                for((j=2;j<=$#;j++));
                do      
                        anArg=$(eval 'echo ${'$j'}')
                        test "${funcs[$i]}" == "$anArg" && continue 2
                done    
                        unset ${funcs[$i]}
        done
fi
unset i j funcs

~

这有两个缺点:无论何时重命名脚本,都必须更改其内容;而且它不适用于符号链接(不确定别名)。如果从命令行执行,它会抛出一个错误:
basename:非法选项--b
。您可能希望
移动参数列表,以便
main
不与
--仅源代码
@HubertGrzeskowiak接触良好点,已修复。谢谢你的建议!
shift
在这里没有意义,因为只有当
--source only
不是第一个参数时,
main
才会运行。@HelderPereira说得好!谢谢你的报道,真是太好了!我会亲自将测试条件交换到
(${BASH#u SOURCE[@]}==1))
;短条件
(${BASH_SOURCE[@]}==1))和&main“$@”
也应该可以工作,如果您不想让它做任何事情
或者
,它就足够了。