Linux - shell script multitasking concurrent execution

Keywords: Linux

shell script multitasking concurrent execution

Transferred from: https://blog.51cto.com/yttitan/2409618

Normally, the commands in the Shell script are executed serially. When a command is executed, the next command will be executed. For example, the following code:

#!/bin/bash
for i in {1..10};do
echo $i 
done
echo "END"

Execution results:

1
2
3
4
5
6
7
8
9
10
END

As you can see, the "echo $i" command in the loop body is executed serially. However, if the executed command takes a long time, it will lead to a very long execution time of the whole program, and may even cause the program to get stuck and lose response for a long time.

For example, we need to complete such a task: write a script to scan the 192.168.80.0/24 network. The hosts that are currently online are considered online if they can ping.

To complete this task, writing a script is not complicated. Here is the written code:

#!/bin/bash
for i in {1..254};do
        ip="192.168.80.$i"
        ping -c 2 $ip &> /dev/null && echo $ip is up 
done

Here is a brief description of the ping command used in the script. The ping command in Linux will send out packets continuously after execution. Therefore, the "- c" option is used in the ping command in the script to specify that packets are sent only twice. If a response can be received, the target host is considered to be online.

There is no logical problem with this script, but after execution, it takes a long time to ping 254 IP addresses in the network in turn. Moreover, the script cannot be forcibly terminated by Ctrl+C. you can only use Ctrl+Z to transfer to the background, and then use the kill command to forcibly end the process.

[root@localhost ~]# bash ping.sh
192.168.80.1 is up
192.168.80.2 is up
^C
^Z
[1]+  Stopped               bash ping.sh
[root@localhost ~]# jobs -l                                #View background work tasks
[1]+ 101100 stop it                  bash ping.sh
[root@localhost ~]# kill -9 101100                        #Force end process
[root@localhost ~]# 
[1]+  Killed               bash ping.sh

In fact, there is no dependency between the ping commands circularly executed in this script, that is, you don't have to wait until the end of "ping 192.168.80.1" to execute "ping 192.168.80.2". All these ping commands can be executed concurrently.
If Python is used, the concurrent execution of commands can be realized with the help of multithreading technology. However, Shell does not support multithreading, so it can only adopt the multi process method. The specific implementation method is very simple, that is, add "&" after the command to be executed concurrently and transfer it to the background for execution, so that after executing a command, you can immediately transfer to the next command without waiting for its execution to end.
Let's take the previous code as an ex amp le and add "&" after the echo command in the loop body:

#!/bin/bash
for i in {1..10};do
echo $i &
done
echo "END"

Execution results:

[root@localhost ~]# bash test.sh
END
[root@localhost ~]# 1
2
3
6
7
4
8
9
10
5

It can be seen that the execution order of commands cannot be guaranteed during concurrent execution, and the echo "END" command, which should be executed after the execution of the whole cycle, was executed at the beginning of the program. Therefore, during concurrent execution, we usually need to ensure that all commands in the loop body are executed before executing the next command. At this time, we can use the wait command. Using the wait command in the Shell is equivalent to multithreading synchronization in other high-level languages.

The following code is improved by adding the wait command:

#!/bin/bash
for i in {1..10};do
echo $i &
done
wait
echo "END"

In this way, the execution result is normal:

[root@localhost ~]# bash test3.sh
6
7
2
3
4
8
9
10
5
1
END

After understanding the principle of concurrent program execution, we also improved the ping script:

#!/bin/bash
for i in {1..254};do
        ip="192.168.80.$i"
        ping -c 2 $ip &> /dev/null && echo $ip is up &
done
wait

At this point, the execution speed of the script will be greatly improved:

[root@localhost ~]# bash ping.sh
192.168.80.10 is up
192.168.80.20 is up
192.168.80.2 is up
192.168.80.1 is up
192.168.80.135 is up

Therefore, when there is no dependency between the commands to be executed circularly, the method of concurrent execution can be adopted, which can greatly improve the efficiency of code execution. Of course, concurrent execution also has defects, that is, when the number of commands to be executed in parallel is very large, especially when the executed commands occupy a lot of system resources, it may exhaust all the resources of the whole system and affect the operation of other programs. Therefore, other technologies can also be used to limit the number of concurrent execution processes. Due to their complexity, this paper will not introduce them.

Posted by Jax2 on Mon, 06 Dec 2021 19:17:27 -0800