23 - bash. Team unification

Lecture



In this lecture, we continue to learn bash . Today let's talk about combining commands in bash , about the construction of case and some other com *** oh. But first, about the case construction that was undeservedly forgotten in the last lecture.

case

case is more convenient to use for branching when the value needs to be checked for exact matching and it can take three or more values. Instead of case, one could use if , but such constructions look cumbersome and are not so convenient to “read” in scripts.

The general syntax of the case command is as follows:

case value in
(template)
command block
;;
(template)
command block
;;
...
...
(template)
command block
;;
esac

For us, case is also important because it is contained in all scripts in the /etc/init.d/ directory. Letters, numbers, strings and patterns of the form [az] , [1-9] , as well as ? - one any character and * - any combination. Although the pattern in general syntax is indicated in parentheses, in practice it is allowed not to write the first bracket. Let's look at a practical example:

#! / bin / bash
case $ 1 in
[a, b, c, d]) # values ​​specified in the template explicitly
echo "abcd"
;;
[0-9]) # sequence of numeric values
echo "This is the number $ 1"
;;
[az]) # sequence
echo "This is the letter $ 1"
;;
??)
echo "These are any two characters"
;;
stop | restart | start)
echo "This is the word stop or restart or start"
;;
*)
echo "Other meaning"
;;
esac

As a value, here we used construction $ 1 , which contains the first parameter passed to the script. In the last lecture, we said that parameters can be passed not only to functions, but also to the script. The value begins to be compared with the templates from top to bottom, and as soon as a match is found, a block of commands between the template and ;; . An example of the script:

one
2
3
four
five
6
7
eight
9
ten
igor @ adm-ubuntu: ~ / linux $ ./case.sh a
abcd
igor @ adm-ubuntu: ~ / linux $ ./case.sh k
This is the letter k
igor @ adm-ubuntu: ~ / linux $ ./case.sh qq
These are any two characters.
igor @ adm-ubuntu: ~ / linux $ ./case.sh 1
This is the number 1
igor @ adm-ubuntu: ~ / linux $ ./case.sh start
This word is stop or restart or start.

The /etc/init.d/ directory contains Linux service control scripts. Using case , they implement a mechanism for processing parameters passed to a script: start , stop , restart , reload, and others. Below is a fragment of the /etc/init.d/reboot script:

one
2
3
four
five
6
7
eight
9
ten
eleven
12
13
14
15
sixteen
case "$ 1" in
start)
# No-op
;;
restart | reload | force-reload)
echo "Error: argument '$ 1' not supported"> & 2
exit 3
;;
stop)
do_stop
;;
*)
echo "Usage: $ 0 start | stop"> & 2
exit 3
;;
esac

You will find a similar case in every script in the /etc/init.d/ directory.

Team unification

I have already said that if we write two keywords of any construction in one line of the script, they must be separated by a symbol ; . The semicolon is the simplest way to combine commands (keywords are also perceived by bash as commands). Commands recorded in this way will be executed sequentially, regardless of the result of the previous command. The examples below can be typed both in the command line and in the script.

one
2
3
four
igor @ adm-ubuntu: ~ / linux $ a = 127; echo $ a; b = 172; echo $ b; let c = $ a + $ b; echo $ c
127
172
299

As you can see from the example, there are 6 commands on one line that were executed sequentially.

To run the commands in parallel, use the ampersand already familiar to you. Such commands will be run in the background and will be executed in parallel.

one
2
3
four
igor @ adm-ubuntu: ~ $ sleep 10 & sleep 10 & echo Hello!
[1] 8043
[2] 8044
Hello!

Further more interesting designs. After its execution, any command returns the numeric result code of the command execution. If it is 0, it means com *** and it was successful, if this number is nonzero, it means com *** and ended with an error. You can build the execution of a sequence of commands given this point. To do this, the following elements exist: a double ampersand - && and a double “pipe” - || . If the commands are combined only through && , then each next com *** a will be executed only in case of successful completion of the previous command. As soon as any com *** and chains return a return code other than zero, the commands following it will not be executed. For example, use the com *** s am true (always returns 0) and false (always returns 1).

one
2
igor @ ubuntu: ~ $ true && echo "1" && false && echo "2"
one

In the example, com *** and true returned 0 and the next com *** and echo “1 ″ was executed and also returned 0, after which com was performed *** and false , which returned 1 and the execution of commands stopped (com ** * and echo 2 is not executed).

Com *** a || works exactly the opposite. That is, the next com *** and will be executed only if the previous com *** a returned a nonzero return code (the rule is valid in this form if all the commands are combined only || ). If the com *** returned 0, then the execution of a further chain of commands is interrupted. If in the example above replace && with || , then only com *** and true will be executed, which will return 0 and further commands will not be executed. If we replace true with false , we get the same result:

one
2
igor @ ubuntu: ~ $ false || echo "1" || false || echo "2"
one

Combining && and || You can build quite complex options for combining teams. But in this case it is necessary to understand more deeply how the execution of commands takes place with such a combination.

Consider two examples of combining commands and the result of their execution:

one
2
3
four
five
igor @ ubuntu: ~ $ false || echo "1" || false || echo "2"
one
igor @ ubuntu: ~ $ false || echo "1" || false && echo "2"
one
2

In the first version, com *** and false returns code 1, and the next com will be executed *** and echo “1 ″ , which returns execution code 0. And then the command chain is interrupted as described above. In the second case there should be the same situation, but com *** and echo “2 ″ is performed. The thing is that in fact there is a check of all join operators that participate in the chain. If a com *** a returned 1, and all subsequent union operators && , then it will not execute any com *** a. But if in the chain to meet the operator || , then someone *** and after it will be performed and the check will go on.

Let us consider this chain in more detail:

1) Executes the *** and false and returns the execution code 1
2) Next is the operator || , which means the next com *** will be executed
3) Run the com *** and echo “1 ″ and return code 0
4) Next is the operator || , which means the next com *** and will not be executed
5) The next statement is checked - this is the && operator, so the next comma *** a ( echo “2 ″ ) will be executed.

See some more examples:

one
2
igor @ ubuntu: ~ $ true || echo "1" && false || echo "2"
2
one
2
3
igor @ ubuntu: ~ $ true && echo "1" || false && echo "2"
one
2
one
2
3
igor @ adm-ubuntu: ~ / linux $ true || echo "1" || echo "2" || echo "3" || false && echo "4" && echo "5" || echo "6"
four
five

Commands can be combined into blocks using parentheses:

one
2
3
four
igor @ adm-ubuntu: ~ / linux $ true || echo "1" && (echo "2"; echo "3" || echo "4") && echo "5" || echo "6"
2
3
five

or curly:

one
2
3
four
igor @ adm-ubuntu: ~ / linux $ true || echo "1" && {echo "2"; echo "3" || echo "4"; } && echo "5" || echo "6"
2
3
five

In the second case, braces must be between the spaces, and after the last command there should be a semicolon.

Differences between round braces and curly braces in that the commands in curly brackets are executed within the same process as the script, and the commands in parentheses are executed as if in its subprocess. Let's see an example script:

one
2
3
four
#! / bin / bash
true && (echo a; sleep 1 && exit 0 || echo b) && echo c
echo d
echo e

and the result of the execution:

one
2
3
four
five
igor @ adm-ubuntu: ~ / linux $ ./script.sh
a
c
d
e

And now the same example with curly braces:

one
2
3
four
#! / bin / bash
true && {echo a; sleep 1 && exit 0 || echo b; } && echo c
echo d
echo e

and the result of its implementation:

one
2
igor @ adm-ubuntu: ~ / linux $ ./script.sh
a

In the first case, the *** and the exit interrupted the execution of commands located in parentheses, but not the script as a whole. In the second case, the whole script stops.

At the end of the lecture a few more teams.

If you need to run the executable file from the script, it is enough to write the file name (if it is located in the directory described in the PATH variable) or the full path to the file if it is located in a specific directory:

one
/home/igor/linux/testscript.sh

In this case, the file testscript.sh will be launched, and the execution of the current script will be suspended and resumed after the completion of the work of the called script. If it is necessary that when the new script or program is called, the current script terminates, then it must be called using the exec command.

one
2
3
four
...
exec /home/igor/linux/testscript.sh
echo "1"
echo "2"

With this option, the echo 1 and echo 2 commands will not be executed, since after exec /home/igor/linux/testscript.sh the script will finish its work.

Also, the work of the script can be interrupted anywhere in the *** *** th exit . It is advisable to specify the result code of the script. For example, exit 0 .


Comments


To leave a comment
If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

LINUX operating system

Terms: LINUX operating system