Linux and Unix

Shell programming with bash

The bash shell in Linux can be run from the command line or in executable files, due to one command per line. These command files are called scripts, and usually the scripts contain more than just command estates. In fact, we can add control structures to control the execution flow.

Control structures

Control structures are blocks of code whose execution is subject to certain conditions. They are recognized by certain keywords (If, for, while, etc.).

They are mainly of 2 types: conditions and loops.

Conditions

Block IF


If condition; then
Instruction
Fi
—-
The IF block tests a condition and if it is true, executes the statement or statements it contains:

—-
If [ -x /bin/bash ] ; then
echo “/bin/bash is an executable”
Fi
—–

The ELSE block is used to execute a statement if the condition is false:


If [ -x /bin/bash ] ; then
echo “/bin/bash is an executable”
else
echo “/bin/bash is not executable”
Fi
—-

You can add ELIF blocks (for “else if”) as much as you want to test alternative conditions:


If [ -d /bin/bash ] ; then
echo “/bin/bash is a directory”
elif [ -x /bin/bash ] ; then
echo “/bin/bash is an executable”
else
echo “/bin/bash is not an executable or directory”
Fi
—–

The test command

When using the brackets in bash, it is the test command that is executed. This command allows multiple tests to be done on one or more variables and return a binary result (V or F). The nature of the test depends on the operator used: for example, the -x operator checks whether the specified character string matches an executable file. Thus, the condition


If [ -x /bin/bash ] ; then

runs behind the scenes
test -x /bin/bash

If /bin/bash is executable, then the condition (and the test command) will return “true”.

In bash, “true” matches the numeric value 0 and false to any value ranging from 1 to 255 (but usually 1).

When running the test command , the result (0 or 1) will not be displayed on the screen but will be stored in a special variable, $?, which it can be displayed:


root @ sv01: # test -x /bin/bash
root @ sv01: # echo $?
0

To see the list of available operators, refer to the test order manual.

Block CASE


case variable in
chain )
Instruction
chain)
;;
Instruction
;;
ESAC

Allows multiple conditions to be chained when the tests are on character strings. We can use ? and* to replace one or more characters in the strings:

echo do you want to continue?


read answer
CASE $ANSWER in
o*)
echo “we continue! ”
;;
n*)
echo “we stop!”
;;
*)
echo “wrong answer! ”
;;
Esac

 

BLOCK F O R… I N

 

for variable in list
do
Instructions
Done

The list may consist of:

  1. enumerated arguments
  2. bash script arguments ($ @)
  3. command results, file lines, or files in a directory


Example 1
for NAME in Pete John Keith Roger do
echo $NAME
Done

Example 2
for ARG in [email protected] do
echo $ARG
Done

Example 3
for FILE in /home/olive/*
do
If [ -d $FILE ] ; then
Echo “$file is a directory”
Fi
Done

for line in $ (cat /home/olive/demo1) do
echo $LIGNE
Done

Block FOR

for (initialization; condition ; iteration)) do

Commands

Done

The initial value of the test variable, the loop shutdown condition, and the increment value are defined in the loop header.
Comparison operators for the condition: >, <, > =, < =, = =,! =; Incrementing operators for iteration: + +,–, + =,-= example


for ((x = 0; x = 10; x + +)) do
echo $x
Done

Block W HI LE

While ondition do

Instruction

done

Only the exit condition of the loop is defined in the header.


CPT = 0
While [ $CPT -lt 10] do
Let CPT = $CPT + 1 echo $CPT
Done

Processing character strings

Concatenate $A and $B

$A$B

example:


A = ABC
B = def echo $A $b

Concatenate $A and a string of characters

$ {A} string

Braces are required to mark the bounds of the variable name.

Example:


A = ABC
echo $ {A} XYZ

Size of a character string

Precede the name of the ‘ # ‘ variable between the braces

$ {#VARIABLE}

example:

A =
echo abcde
${#A}

Extracting segments from a character string

$ {string:POSITION_INITIALE:size}

The size parameter is optional; If it is absent the limit is the end of the character string. Position 0 is the first character.

examples:


A = abcdefghi echo $ {A:0:3}

echo $ {A:5}

echo $ {A:6:2}

Extraction with delimiter (cut command )

Syntax:

Cut -d ‘delimiter’ -fX
where ‘ X ‘ is the index of the field to be extracted, based on 1.

The cut command affects the contents of a file or the standard entry: to use it with a variable we must therefore redirect:


VAR = ‘ a-b-c-d ‘
echo $VAR | cut -d ‘-‘ -f2

Replacing segments of a character string

$ {string/segment/replace}

Replaces only the first found segment occurrence in string; to replace all occurrences one must double the first ‘/’:

$ {string//segment/replace}

examples:


A = abbababba echo $ {A/b/X}
echo $ {a/BB/XX}
echo $ {a/BB/x}
echo $ {a//b/x}
Echo $ {a//BB/x }
—-

Extracting parts of a path in the full name of a file

The basename and dirname commands are used to extract (respectively) the file name and the full path of the directory that contains it. Beware, these commands are based only on the presence of “/” in the character string: no validation of the existence of the files or directories is made.

examples:


A =/etc/X11/xinit/xinitrc basename $A

DirName $A

B = $(basename $A) echo $B

Functions

A function, in bash is the same as in any other programming language, is to give a name to a program block in order to be able to reuse it easily. One defines a function by putting the code it contains in braces:


my_fonction() {

FUNCTION CODE

}

Parentheses are put to identify that is a function, but they cannot contain arguments.

With a script, the arguments of a function are called by variables $1, $2, $3,. ..

[email protected].

Definition files

You can group definitions of variables or functions into files. To put these variables or functions in memory, we use the source command or its shortcut, the point (‘. ‘):


$ > echo A = 1000 >> my_variables
$ > echo B = 2000 >> my_variables
$ > . my_variables
$ > echo $A 1000

Redirect and flow

There are 3 standard streams:

stdin : standard input, & 0
stdout : standard output, &1 stderr : standard error output, &2
A program can require inputs or outputs. For example, ls does not take anything as input but has an output stream (the list of files); read takes data on the standard input but is not connected to stdout.

By default the 3 streams are connected to the Terminal: a program that reads its entries on stdin will read them on the Terminal, and a program that sends its outputs on stdout and stderr displays them on the Terminal

It is also possible to redirect these streams to files
> : standard output to file
< : standard file entry examples:
To read the standard input (default, the console) and set the value to A

read A

To read data and assign it to A, but from a file:
read a < Msg. txt
To redirect what comes out of stdout to a file
ls > txt: stdout

For a program that uses both stdin (read) and stdout (echo), the two streams can be connected to files; for example:

ABCD. sh:


read A
for MOT in A; do
echo \($MOT \)
Done

$ > my_program < input > output File.

Piping

A pipe allows to connect the output of a program to the input of another:


$ > PROG1 | PROG2

It is equivalent to this:
$ > PROG1 > line
$ > PROG2 < line

Logical operators & & and | |

Separates two phrases that must be evaluated as true or false.

&& = ‘ and ‘, returns ‘ true ‘ if both conditions are true
|| = ‘ or ‘, returns ‘ true ‘ if at least one of the conditions is true


A = Hi
B = Hello
If [$A = hello] && [$B = hello]; then echo true conditions
else
echo false conditions
Fi


A=Hello
B= LOB
If [$A = hello] || [$B = hello]; then
Echo true condition
else
ECHO false condition
Fi
—-

In the case of a conjunction (&&), bash evaluates each of the 2 conditions in turn.

In the case of a disjunction, (||), if the first condition is true, bash immediately enters the if block without testing the second condition, because it only needs one of the 2 to be true. The second condition is evaluated only when the first is false.

This has the effect that we can also use ||to replace the denial of a simple if condition:


If [ !-e $FICH] ; then
echo $FICH is absent
Fi

is equivalent to:

test -e $FICH || echo $FICH is absent
—-

We test the first part of the disjunction, and if it is true we do not execute the second part (we go to the next line). If the first part is false the second will be executed.