How to Reuse External Shell Scripts in Linux

How to Reuse External Shell Scripts in LinuxHow to Reuse External Shell Scripts in Linux

In Linux development, shell scripts are often written to perform tasks, usually with each script doing one thing. As tasks increase, the number of scripts grows, and the places where they can be reused also increase. At this point, it is necessary to extract common functions from the scripts and place them in a general script that other scripts can reuse.

This article introduces how to execute external scripts in shell scripts, how to call functions from external scripts, and methods related to script reuse.

Ways to Execute External Scripts

Suppose there is a script a.sh in the current directory, with the following content:

#!/bin/bash

echo "a.sh..."

There are several ways to execute external scripts within a script:

  • source external_script_name

In the b.sh script located in the current directory, the content is as follows:

#!/bin/bash

source a.sh
echo "b.sh..."

When executing ./b.sh, the result is as follows:

[root@ecs-centos-7 ~]# ./b.sh 
a.sh...
b.sh...

In the script, the command source a.sh will first execute the a.sh script in the current directory, so the result will first output a.sh... and then output the print of the b.sh script itself.

  • dot external_script_name

Change the statement in b.sh that executes the a.sh script to dot + space + a.sh. The modified script content is as follows:

Note: There must be a space between the dot and a.sh, otherwise an error will occur during execution.

#!/bin/bash

. a.sh
echo "b.sh..."

When executing ./b.sh, the result is as follows:

[root@ecs-centos-7 ~]# ./b.sh 
a.sh...
b.sh...

In the above script, . a.sh will first execute the a.sh script, resulting in the output of a.sh... followed by b.sh....

  • sh external_script_name

sh external_script_name and ./external_script_name are the same, either can be chosen. Below is an example using the former:

Change the b.sh script from source a.sh to sh a.sh. The modified script content is as follows:

#!/bin/bash

sh a.sh
echo "b.sh..."

When executing ./b.sh, the result is as follows:

[root@ecs-centos-7 ~]# ./b.sh 
a.sh...
b.sh...

It can be seen that the output result is the same as the above two methods.

What Are the Differences Between the Three Methods?

There are three ways to call external scripts: source external_script, dot external_script, and sh external_script. What are the differences between them?

Among them, source external_script and dot external_script are the same; the current script inherits the global variables and functions of the external script, which is equivalent to importing the functions and global variables of the external script into the current script.

Modify the a.sh and b.sh scripts, as follows:

Content of a.sh script:

#!/bin/bash

VAR_A=10

func_a()
{
  echo "a.sh...pid:$$,param:$1"
}

Content of b.sh script:

#!/bin/bash

source a.sh 

func_a $1
echo "vara:$VAR_A"
echo "b.sh...pid:$$"

When executing ./b.sh 5, the result is:

[root@ecs-centos-7 ~]# ./b.sh 5
a.sh...pid:21485,param:5
vara:10
b.sh...pid:21485

The $$ in both scripts refers to the process ID of the executing script. From the result, it can be seen that both a.sh and b.sh are executed within the same process, so executing source a.sh in b.sh will import the global variable VAR_A and function func_a from a.sh into b.sh.

Printing the variable VAR_A in b.sh outputs the same value as in a.sh, and calling the func_a function also indicates that it is calling the function from a.sh.

source external_script and dot external_script are the same, so changing source a.sh in b.sh to . a.sh and executing ./b.sh 5 will yield the same result.

Since the sh external_script method executes the current script and external script in two different processes, the current script cannot directly use the functions and global variables from the external script.

Modify the a.sh and b.sh scripts, as follows:

Content of a.sh script:

#!/bin/bash

test_a()
{
  echo "a.sh...test_a"
}

echo "a.sh...pid:$$"

Content of b.sh script:

#!/bin/bash

sh a.sh

echo "b.sh...pid:$$"

test_a

When executing ./b.sh, the result is:

[root@ecs-centos-7 ~]# ./b.sh 
a.sh...pid:21818
b.sh...pid:21817
./b.sh:行7: test_a: 未找到命令

From the result, it can be seen that the process IDs of a.sh and b.sh are different; the b.sh script process cannot find the test_a function, so calling the test_a function in b.sh will prompt command not found.

Calling Functions from External Scripts

The previous section mentioned that the sh external_script method cannot directly use functions and global variables from the external script. Here are a few methods to solve this problem:

  • case branch selection

This method is similar to the switch-case statement in programming code, using the case keyword in shell scripts to implement it.

Content of a.sh script:

#!/bin/bash

VAR_A=10

test_a()
{
   echo "test_a..pid:$$,p1:$1,p2:$2"
}
get_var()
{
  echo ${VAR_A}
}

case "$1" in
    ta)
      test_a $2 $3
      ;;
    var)
      get_var
      ;;
   *)
      echo "parameter err..."
esac

Content of b.sh script:

#!/bin/bash

echo "b.sh...pid:$$"

sh a.sh ta 3 5

ret=$(sh a.sh var)

echo "ret:$ret"

When executing ./b.sh, the result is:

[root@ecs-centos-7 ~]# ./b.sh 
b.sh...pid:24813
test_a..pid:24814,p1:3,p2:5
ret:10

The b.sh script first prints its own process ID.

The statement sh a.sh ta 3 5 calls the a.sh script, passing three parameters: ta, 3, and 5. When executing a.sh, the first parameter ta matches the case and calls the test_a function, passing the remaining two parameters 3 and 5 to the function.

The statement ret=$(sh a.sh var) calls the a.sh script, passing a var parameter. After matching the case, it calls the get_var function, which outputs the value of the global variable VAR_A from the script. The $() is used to get the return value of the command in parentheses, so here the value of ret is the value of the global variable VAR_A from the a.sh script.

Note: If you want to get the return value of a function, you can print the output value using echo in the function, and then use $(function_name parameter_list) to get the value printed in the function, as shown in the b.sh script with ret=$(sh a.sh var). The value of the variable ret is the value printed by the get_var function in the a.sh script.

It should be noted that if there are echo debugging logs in the function, then the debugging logs will also be returned together.

  • Function Call Template

The method introduced above using the case keyword to match and call different functions has a drawback: every time a function is added to the a.sh script, a new branch needs to be added to the case, and it needs to be noted whether the function has parameters and whether the number of parameters is correct.

We can add the following statement at the end of each external calling script to solve the above problem:

if [ $# -ge 1 ]; then
   name="$1"
   shift 1
   $name "$@"
fi

The above statement first checks the number of parameters passed when calling the script; only when the number of parameters is greater than or equal to 1 is it valid. The first parameter passed indicates the function name, and all parameters from the second to the last will be passed to the function.

Here, shift 1 shifts the parameters passed to the script to the left by one position. For example, if there are three parameters $1 $2 $3, after shifting, $2 moves to the position of $1, $3 moves to the position of $2, and the number of parameters becomes 2.

Reason: The first parameter in the passed script parameters indicates the function name, and the parameters from the second one onwards are the function parameters. If no left shift is performed, the first parameter (the function name) will also be passed to the function as a parameter.

Below is the complete script content:

Content of a.sh script:

#!/bin/bash

VAR_A=10

test_a()
{
   echo "test_a..pid:$$,p1:$1,p2:$2"
}

get_var()
{
  echo ${VAR_A}
}

if [ $# -ge 1 ]; then
   name="$1"
   shift 1
   $name "$@"
fi

Content of b.sh script:

#!/bin/bash

echo "b.sh...pid:$$"

sh a.sh test_a 3 5

ret=$(sh a.sh get_var)

When executing ./b.sh, the result is:

[root@ecs-centos-7 ~]# ./b.sh 
b.sh...pid:25086
test_a..pid:25087,p1:3,p2:5
ret:10

It can be seen that the result is the same as the method above using case.

Now, other scripts can call functions in the reusable script using sh a.sh function_name parameter_list in this way, and the return value of the function can be obtained using $(sh a.sh function_name parameter_list).

  • Advantages and Disadvantages of Both

Compared to the case branch selection method, the advantage of the function call template is that the caller only needs to care about the function name in the reusable script, the parameters passed to the function, and the return value of the function.

The disadvantage is that if multiple scripts call the functions in the reusable script, when the function name in the reusable script changes, all places that called it need to be modified.

The disadvantage of the function call template is precisely the advantage of the case branch selection method: the case branch selection method calls different functions based on the passed string parameter, where the string parameter acts as an alias for the function. As long as this parameter remains unchanged, the function names in the script can change freely.

The comparison of the advantages and disadvantages above is relative; in practical applications, the differences may not be very obvious, and in most cases, both methods can be used.

Conclusion

In the process of writing shell scripts, one often encounters inexplicable problems. Some problems may remain unsolved despite extensive effort. Script reuse can extract common functionalities, forming modular functions, which not only helps reduce errors during script writing but also aids in the maintenance of scripts in the future.

Liang Xu’s WeChat

Add Liang Xu’s WeChat to receive 3 sets of essential materials for programmers

→ Selected technical materials shared

→ High-level exchange community

How to Reuse External Shell Scripts in Linux

All articles in this public account have been organized into a directory. Please reply “m” in the public account to get it!

Recommended Reading:

Hi: Please install this magical plugin for both VSCode and IDEA

The poem-like code written by Lei Jun in 1994, I have run it!

Detailed explanation of the ss command for Linux network status tools

5T technical resources giveaway! Including but not limited to: C/C++, Linux, Python, Java, PHP, artificial intelligence, microcontrollers, Raspberry Pi, etc. Reply “1024” in the public account to get it for free!!

How to Reuse External Shell Scripts in Linux

Leave a Comment

Your email address will not be published. Required fields are marked *