我能否从另一个shell脚本中调用一个脚本函数?

154

我有两个shell脚本。

第二个shell脚本包含以下函数 second.sh

func1 
func2

第一个脚本将使用一些参数调用第二个 shell 脚本,并使用特定于该函数的其他参数调用 func1 和 func2。

这是我所说的内容的示例。

second.sh

val1=`echo $1`
val2=`echo $2`

function func1 {

fun=`echo $1`
book=`echo $2`

}

function func2 {

fun2=`echo $1`
book2=`echo $2`


}

first.sh

second.sh cricket football

func1 love horror
func2 ball mystery

我该怎么做才能实现它?


4
v=$(echo $1) 这个命令是完全冗余的。只需要写成 fun2=$1 即可。唯一的区别在于 $()(或者反引号)会移除结尾的换行符。 - William Pursell
1
由于从shell脚本调用函数与从命令行调用函数没有什么特别之处,因此这个问题可以简化为“如何调用bash函数?” - Tom Russell
7个回答

222

请将您的second.sh脚本重构为以下方式:

func1 {
   fun="$1"
   book="$2"
   printf "func=%s,book=%s\n" "$fun" "$book"
}

func2 {
   fun2="$1"
   book2="$2"
   printf "func2=%s,book2=%s\n" "$fun2" "$book2"
}

然后像这样从脚本 first.sh 中调用这些函数:

source ./second.sh
func1 love horror
func2 ball mystery

输出:

func=love,book=horror
func2=ball,book2=mystery

28
为避免意外副作用,请注意 source 命令实际上会运行传递的脚本。 - dvlcube
3
@dvlcube 如果您能提供一个替代方案会更好。 - Alpha2k
1
这个答案很清晰、不错。重要的是,https://dev59.com/FGgv5IYBdhLWcg3wI9ag#42101141 描述了一些人可能会遇到的问题,并提供了一个优雅的通用解决方案。 - Aaron Johnson
2
@Alpha2k 我不确定是否有替代方案。这个想法是“小心你要放入源文件的内容”。 - CoffeeTableEspresso
这个不起作用:源文件未找到 - Richard Barraclough
你在用什么手机? - anubhava

92

你不能直接调用另一个shell脚本中的函数。

你可以将函数定义放到一个单独的文件中,然后使用.命令将其加载到脚本中,像这样:

. /path/to/functions.sh

这将解释 functions.sh 仿佛它的内容实际上就存在于你的文件中。这是一种实现shell函数共享库的常见机制。


5
既然 .source 的别名,那这不就和被接受的答案一样了吗? - ajsharma
31
我先回答了,所以技术上接受的答案与这个是一样的:). 此外,虽然在许多Shell中,“source”是“.”的别名,但并不总是如此。例如,在Debian上提供/bin/sh的“dash”软件包中就没有“source”命令。 - larsks
1
在Ubuntu中,source命令无法使用。@ajsharma 参考:source command not found in sh shell - rajeshmag
1
这个评论为什么没有被评为最高赞?这是正确的 #!/bin/sh 语法,而另一个只能在 bash 中工作。 - xddq

64

问题

目前被接受的答案仅在重要条件下有效。给定...

/foo/bar/first.sh:

function func1 {  
   echo "Hello $1"
}

and

/foo/bar/second.sh:

#!/bin/bash

source ./first.sh
func1 World

这只有在从包含 first.sh 的目录内执行 first.sh 时才能正常运行。例如,如果 shell 的当前工作路径是 /foo,则尝试运行命令
cd /foo
./bar/second.sh

打印错误:

/foo/bar/second.sh: line 4: func1: command not found

这是因为 source ./first.sh 是相对于当前工作路径而非脚本的路径。因此,一种解决方法是利用子shell并运行:

(cd /path/to/script && source ./first.sh)
(cd /foo/bar; ./second.sh)

更通用的解决方案

假设...

/foo/bar/first.sh:

function func1 {  
   echo "Hello $1"
}

and

/foo/bar/second.sh:

#!/bin/bash

source $(dirname "$0")/first.sh

func1 World

然后。
cd /foo
./bar/second.sh

打印

Hello World

它是如何工作的

  • $0 返回执行脚本的相对或绝对路径。
  • dirname 返回 $0 脚本所在目录的相对路径。
  • $( dirname "$0" ) 命令 dirname "$0" 返回被执行脚本所在目录的相对路径,然后将其用作 source 命令的参数。
  • 在“second.sh”中,/first.sh 只需附加导入的 Shell 脚本的名称。
  • source 将指定文件的内容加载到当前 Shell 中。

这正是我所需要的。非常感谢! - Eyal Gerber
它按照描述的方式工作了! - dave
6
$(dirname "$0") 可以解决 second.sh 文件名中包含空格的问题。但是,如果 路径 中包含空格,例如 /foo bar/second.sh,则无法正常工作。因此,应该使用 source "$(dirname "$0")/first.sh" - mre

6

second.sh

#!/bin/bash

function func1() {
   fun="$1"
   book="$2"
   echo "$fun, $book\n"
}

function func2() {
   fun2="$1"
   book2="$2"
   printf "$fun2, $book2\n"
}

first.sh

#!/bin/bash

source /absolute_path_to/second.sh
func1 love horror
func2 ball mystery

在使用它之前,你需要记住以下几点:

  • source关键字和.(一个句号)是同一命令。
  • 如果FILENAME不是文件的完整路径,则该命令将在$PATH环境变量指定的目录中搜索文件。如果在$PATH中找不到文件,则该命令将在当前目录中查找文件。
  • 如果给出任何ARGUMENTS参数,则它们将成为FILENAME的位置参数。
  • 如果FILENAME存在,则source命令的退出代码为0;否则,如果找不到文件,则返回1

在这些要点中,应重点关注第个要点:如果您正在使用#!/bin/bash,则实际上需要为文件提供一个ABSOLUTE_PATHRELATIVE_PATH无法正常工作。如果是这种情况,那么您只需要将路径更改为ABSOLUTE_FILE_PATH即可。


1
这是一篇非常古老的帖子,我知道。但是,尽管它在同一个目录中,我发现我无法找到另一个文件。
line 3: ./functions.bash: No such file or directory

我想起来我已经有一个解决这种情况的方法,因为我从一个ini文件导入。所以这是我的解决方案,使我可以在任何地方运行程序(显然包括源文件),而不需要硬编码路径。

app="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
source $app/functions.bash

我不能为此负责,已经有一段时间了,我无法回忆起我从哪里得到这行代码,否则我会给他们功劳,但这就是我用来获取文件的全部内容。

可能来自 https://dev59.com/yXVD5IYBdhLWcg3wL4cA - tripleee

0

如果你定义了

    #!/bin/bash
        fun1(){
          echo "Fun1 from file1 $1"
        }
fun1 Hello
. file2 
fun1 Hello
exit 0

在file1(chmod 750 file1)和file2文件中

   fun1(){
      echo "Fun1 from file2 $1"
    }
    fun2(){
      echo "Fun1 from file1 $1"
    }

运行./file2,你会得到 来自file1的Fun1 Hello 来自file2的Fun1 Hello 惊喜!!! 你用file2中的fun1覆盖了file1中的fun1... 为了避免这种情况,你必须
declare -f pr_fun1=$fun1
. file2
unset -f fun1
fun1=$pr_fun1
unset -f pr_fun1
fun1 Hello

它会保存您之前对fun1的定义,并使用先前的名称恢复它,删除不需要导入的内容。每次从另一个文件导入函数时,您可能需要记住两个方面:

  1. 您可能会用相同的名称覆盖现有的函数(如果这是您想要的,您必须按照上述描述保留它们)
  2. 导入导入文件的所有内容(包括函数和全局变量)。请小心! 这是危险的过程

-7
#vi function.sh

#!/bin/bash
f1() {
    echo "Hello $name"
}

f2() {
    echo "Enter your name: "
    read name
    f1
}
f2

#sh function.sh

这里函数f2将调用函数f1


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接