172

I have 2 shell scripts.

The second shell script contains following functions second.sh

func1 
func2

The first.sh will call the second shell script with some parameters and will call func1 and func2 with some other parameters specific to that function.

Here is the example of what I am talking about

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

How can I achieve it?

2
  • 5
    v=$(echo $1) is completely redundant. Just write fun2=$1. The only difference is that the $() (or backticks) will remove trailing newlines. Commented May 30, 2012 at 19:58
  • 1
    Since there's nothing special about calling a function from a shell script as opposed to the command line, this question can be reduced down to "How to call a bash function?" Commented Sep 16, 2016 at 7:48

7 Answers 7

237

You can refactor your second.sh script like this:

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"
}

And then call these functions from script first.sh like this:

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

OUTPUT:

func=love,book=horror
func2=ball,book2=mystery
Sign up to request clarification or add additional context in comments.

7 Comments

To avoid unintentional side effects, beware that the source command will actually run the script passed as argument.
@dvlcube it would be better if you could suggest an alternative.
This answer is clear and good. Importantly, stackoverflow.com/a/42101141/852196 describes a problem some people might run into and provides an elegant generalized solution.
@Alpha2k I'm not really sure there is an alternative. the idea is "be careful what you're putting into the file you source"
What she’ll are you using?
|
105

You can't directly call a function in another shell script.

You can move your function definitions into a separate file and then load them into your script using the . command, like this:

. /path/to/functions.sh

This will interpret functions.sh as if it's content were actually present in your file at this point. This is a common mechanism for implementing shared libraries of shell functions.

6 Comments

Isn't this the same as the accepted answer, since . is an alias for source ?
Well, I answered first, so technically the accepted answer is the same as this one :). Also, while source is an alias for . in many shells, that isn't always the case. For example, the dash package on Debian, which provides /bin/sh, does not have a source command.
source command didn't work in Ubuntu @ajsharma. Reference: source command not found in sh shell
so weird that this comment is not upvoted as the highest? This is correct #!/bin/sh syntax and the other will work only in bash.
Plus the question asks for shell, not bash. So source shouldn't be a good answer.
|
70

The problem

The currenly accepted answer works only under important condition. Given...

/foo/bar/first.sh:

function func1 {  
   echo "Hello $1"
}

and

/foo/bar/second.sh:

#!/bin/bash

source ./first.sh
func1 World

this works only if the first.sh is executed from within the same directory where the first.sh is located. Ie. if the current working path of shell is /foo, the attempt to run command

cd /foo
./bar/second.sh

prints error:

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

That's because the source ./first.sh is relative to current working path, not the path of the script. Hence one solution might be to utilize subshell and run

(cd /foo/bar; ./second.sh)

More generic solution

Given...

/foo/bar/first.sh:

function func1 {  
   echo "Hello $1"
}

and

/foo/bar/second.sh:

#!/bin/bash

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

func1 World

then

cd /foo
./bar/second.sh

prints

Hello World

How it works

  • $0 returns relative or absolute path to the executed script
  • dirname returns relative path to directory, where the $0 script exists
  • $( dirname "$0" ) the dirname "$0" command returns relative path to directory of executed script, which is then used as argument for source command
  • in "second.sh", /first.sh just appends the name of imported shell script
  • source loads content of specified file into current shell

2 Comments

it worked as described!
$(dirname "$0") catches the problem, that second.sh could contain spaces in its name. But it does not work, if the path contains spaces, e.g. /foo bar/second.sh. You should use source "$(dirname "$0")/first.sh" instead.
10

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

You need to keep these things in your mind before using it

  • Keyword source and . (a period) are the same command.
  • If the FILENAME is not a full path to a file, the command will search for the file in the directories specified in the $PATH environmental variable . If the file is not found in the $PATH, the command will look for the file in the current directory.
  • If any ARGUMENTS are given, they will become positional parameters to the FILENAME.
  • If the FILENAME exists, the source command exit code is 0, otherwise, if the file is not found it will return 1.

Among these points the point to be focused on is the second one, you actually need to provide a ABSOLUTE_PATH to the file if you are using #!/bin/bash, RELATIVE_PATH just doesn't work if that is the case with you then my friend you just need to change the path to ABSOLUTE_FILE_PATH.

Comments

2

This is a very old post, I am aware. However I found I could not source another file even though it was in the same directory.

line 3: ./functions.bash: No such file or directory

Then I remembered I already have in place a method for this exact situation because I import from an ini file. So here is my solution that allows me to run a program from anywhere (and apparently source files) without hardcoding the paths.

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

I cannot take credit for this, its been a while and I can't recall where I got the line from otherwise I would credit them, but that's all I use to source files.

1 Comment

0

If you define

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

in file1(chmod 750 file1) and file2

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

and run ./file2 you'll get Fun1 from file1 Hello Fun1 from file2 Hello Surprise!!! You overwrite fun1 in file1 with fun1 from file2... So as not to do so you must

declare -f pr_fun1=$fun1
. file2
unset -f fun1
fun1=$pr_fun1
unset -f pr_fun1
fun1 Hello

it's save your previous definition for fun1 and restore it with the previous name deleting not needed imported one. Every time you import functions from another file you may remember two aspects:

  1. you may overwrite existing ones with the same names(if that the thing you want you must preserve them as described above)
  2. import all content of import file(functions and global variables too) Be careful! It's dangerous procedure

Comments

-7
#vi function.sh

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

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

#sh function.sh

Here function f2 will call function f1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.