Bash-скрипты, часть 9: регулярные выражения

Using variables

Variables allow you to store information to use it in your script.

You can define two types of variables in your shell script:

  • Environment variables
  • User variables

Environment variables

Sometimes you need to interact with system variables; you can do this by using environment variables.

#!/bin/bash

# display user home

echo "Home for the current user is: $HOME"

Notice that we put the $HOME system variable between double quotations, and it prints the home variable correctly.

What if we want to print the dollar sign itself?

echo "I have $1 in my pocket"

Because variable $1 doesn’t exist, it won’t work. So how to overcome that?

You can use the escape character, which is the backslash \ before the dollar sign like this:

echo "I have \$1 in my pocket"

Now it works!!

User variables

Also, you can set and use your custom variables in the script.

You can call user variables in the same way as this:

#!/bin/bash

# User variables

grade=5

person="Adam"

echo "$person is a good boy, he is in grade $grade"
chmod +x myscript

./myscript

9.2.2. Examples

9.2.2.1. Simple example using while

Here is an example for the impatient:

#!/bin/bash

# This script opens 4 terminal windows.

i="0"

while 
do
xterm &
i=$
done

9.2.2.2. Nested while loops

The example below was written to copy pictures that are made with a webcam to a web directory. Every five minutes a picture is taken. Every hour, a new directory is created, holding the images for that hour. Every day, a new directory is created containing 24 subdirectories. The script runs in the background.

#!/bin/bash

# This script copies files from my homedirectory into the webserver directory.
# (use scp and SSH keys for a remote directory)
# A new directory is created every hour.

PICSDIR=/home/carol/pics
WEBDIR=/var/www/carol/webcam

while true; do 
	DATE=`date +%Y%m%d`
	HOUR=`date +%H`
	mkdir $WEBDIR/"$DATE"
	
	while ; do 
		DESTDIR=$WEBDIR/"$DATE"/"$HOUR"
		mkdir "$DESTDIR"
		mv $PICDIR/*.jpg "$DESTDIR"/
		sleep 3600
		HOUR=`date +%H`
	done
done

Note the use of the true statement. This means: continue execution until we are forcibly interrupted (with kill or Ctrl+C).

This small script can be used for simulation testing; it generates files:

#!/bin/bash

# This generates a file every 5 minutes

while true; do
touch pic-`date +%s`.jpg
sleep 300
done

Note the use of the date command to generate all kinds of file and directory names. See the man page for more.

Use the system
 

The previous example is for the sake of demonstration. Regular checks can easily be achieved using the system’s cron facility. Do not forget to redirect output and errors when using scripts that are executed from your crontab!

9.2.2.3. Using keyboard input to control the while loop

This script can be interrupted by the user when a Ctrl+C sequence is entered:

#!/bin/bash

# This script provides wisdom

FORTUNE=/usr/games/fortune

while true; do
echo "On which topic do you want advice?"
cat << topics
politics
startrek
kernelnewbies
sports
bofh-excuses
magic
love
literature
drugs
education
topics

echo
echo -n "Make your choice: "
read topic
echo
echo "Free advice on the topic of $topic: "
echo
$FORTUNE $topic
echo

done

A here document is used to present the user with possible choices. And again, the true test repeats the commands from the CONSEQUENT-COMMANDS list over and over again.

9.2.2.4. Calculating an average

This script calculates the average of user input, which is tested before it is processed: if input is not within range, a message is printed. If q is pressed, the loop exits:

#!/bin/bash

# Calculate the average of a series of numbers.

SCORE="0"
AVERAGE="0"
SUM="0"
NUM="0"

while true; do

  echo -n "Enter your score  ('q' for quit): "; read SCORE;

  if (("$SCORE" < "0"))  || (("$SCORE" > "100")); then
    echo "Be serious.  Common, try again: "
  elif ; then
    echo "Average rating: $AVERAGE%."
    break
  else
    SUM=$
    NUM=$
    AVERAGE=$[$SUM / $NUM]
  fi

done

echo "Exiting."

Note how the variables in the last lines are left unquoted in order to do arithmetic.

Double Quotes

Double quotes in bash will suppress special meaning of every meta characters except «$», «\» and «`». Any other meta characters will be read literally. It is also possible to use single quote within double quotes. If we need to use double quotes within double quotes bash can read them literally when escaping them with «\». Example:

#!/bin/bash#Declare bash string variableBASH_VAR="Bash Script"# echo variable BASH_VARecho $BASH_VAR# meta characters and its special meaning in bash is # suppressed when using double quotes except "$", "\" and "`"echo "It's $BASH_VAR  and \"$BASH_VAR\" using backticks: `date`" 

Control bash loop with

Here is a example of while loop controlled by standard input. Until the redirection chain from STDOUT to STDIN to the read command exists the while loop continues.

#!/bin/bash# This bash script will locate and replace spaces# in the filenamesDIR="."# Controlling a loop with bash read command by redirecting STDOUT as# a STDIN to while loop# find will not truncate filenames containing spacesfind $DIR -type f | while read file; do# using POSIX class  to find space in the filenameif ]* ]]; then# substitute space with "_" character and consequently rename the filemv "$file" `echo $file | tr ' ' '_'`fi;# end of while loopdone 

Bash Functions

!/bin/bash# BASH FUNCTIONS CAN BE DECLARED IN ANY ORDERfunction function_B {        echo Function B.}function function_A {        echo $1}function function_D {        echo Function D.}function function_C {        echo $1}# FUNCTION CALLS# Pass parameter to function Afunction_A "Function A."function_B# Pass parameter to function Cfunction_C "Function C."function_D 

Bash Select

#!/bin/bashPS3='Choose one word: ' # bash selectselect word in "linux" "bash" "scripting" "tutorial" do  echo "The word you have selected is: $word"# Break, otherwise endless loop  break  doneexit 0 

Case statement conditional

#!/bin/bashecho "What is your preferred programming / scripting language"echo "1) bash"echo "2) perl"echo "3) phyton"echo "4) c++"echo "5) I do not know !"read case;#simple case bash structure# note in this case $case is variable and does not have to# be named case this is just an examplecase $case in    1) echo "You selected bash";;    2) echo "You selected perl";;    3) echo "You selected phyton";;    4) echo "You selected c++";;    5) exitesac 

Переменные в скриптах

Написание скриптов на Bash редко обходится без сохранения временных данных, а значит создания переменных. Без переменных не обходится ни один язык программирования и наш примитивный язык командной оболочки тоже.

Возможно, вы уже раньше встречались с переменными окружения. Так вот, это те же самые переменные и работают они аналогично.

Например, объявим переменную string:

Значение нашей строки в кавычках. Но на самом деле кавычки не всегда нужны. Здесь сохраняется главный принцип bash — пробел — это специальный символ, разделитель, поэтому если не использовать кавычки world уже будет считаться отдельной командой, по той же причине мы не ставим пробелов перед и после знака равно.

Чтобы вывести значение переменной используется символ $. Например:

Модифицируем наш скрипт:

И проверяем:

$ ./script

Hello world

Bash не различает типов переменных так, как языки высокого уровня, например, С++, вы можете присвоить переменной как число, так и строку. Одинаково все это будет считаться строкой. Оболочка поддерживает только слияние строк, для этого просто запишите имена переменных подряд:

Проверяем:

Обратите внимание, что как я и говорил, кавычки необязательны если в строке нет спецсимволов. Присмотритесь к обоим способам слияния строк, здесь тоже демонстрируется роль кавычек

Если же вам нужны более сложные способы обработки строк или арифметические операции, это не входит в возможности оболочки, для этого используются обычные утилиты.

How do they work?

This is just a little bit of background knowledge. It’s not necessary to understand this in order to write scripts but it can be useful to know once you start getting into more complex scripts (and scripts that call and rely on other scripts once you start getting really fancy).

In the realm of Linux (and computers in general) we have the concept of programs and processes. A program is a blob of binary data consisting of a series of instructions for the CPU and possibly other resources (images, sound files and such) organised into a package and typically stored on your hard disk. When we say we are running a program we are not really running the program but a copy of it which is called a process. What we do is copy those instructions and resources from the hard disk into working memory (or RAM). We also allocate a bit of space in RAM for the process to store variables (to hold temporary working data) and a few flags to allow the operating system (OS) to manage and track the process during it’s execution.

Essentially a process is a running instance of a program.

There could be several processes representing the same program running in memory at the same time. For example I could have two terminals open and be running the command cp in both of them. In this case there would be two cp processes currently existing on the system. Once they are finished running the system then destroys them and there are no longer any processes representing the program cp.

When we are at the terminal we have a Bash process running in order to give us the Bash shell. If we start a script running it doesn’t actually run in that process but instead starts a new process to run inside. We’ll demonstrate this in the next section on variables and it’s implications should become clearer. For the most part you don’t need to worry too much about this phenomenon however.

File comparisons

You can compare and check for files using the following operators:

-d my_file Checks if its a folder.

-e my_file Checks if the file is available.

-f my_file Checks if its a file.

-r my_file Checks if it’s readable.

my_file –nt my_file2 Checks if my_file is newer than my_file2.

my_file –ot my_file2 Checks if my_file is older than my_file2.

-O my_file Checks if the owner of the file and the logged user match.

-G my_file Checks if the file and the logged user have an identical group.

As they imply, you will never forget them.

Let’s pick one of them and take it as an example:

#!/bin/bash

mydir=/home/likegeeks

if ; then

	echo "Directory $mydir exists"

	cd $mydir

	ls

else

	echo "NO such file or directory $mydir"

fi

We are not going to type every one of them as an example. You just type the comparison between the square brackets as it is and complete your script normally.

There are some other advanced if-then features, but let’s make it in another post.

That’s for now. I hope you enjoy it and keep practicing more and more.

Thank you.

Global vs. Local variables

#!/bin/bash#Define bash global variable#This variable is global and can be used anywhere in this bash scriptVAR="global variable"function bash {#Define bash local variable#This variable is local to bash function onlylocal VAR="local variable"echo $VAR}echo $VARbash# Note the bash global variable did not change# "local" is bash reserved wordecho $VAR

Passing arguments to the bash script

#!/bin/bash# use predefined variables to access passed arguments#echo arguments to the shellecho $1 $2 $3 ' -> echo $1 $2 $3'# We can also store arguments from bash command line in special arrayargs=("$@")#echo arguments to the shellecho ${args} ${args} ${args} ' -> args=("$@"); echo ${args} ${args} ${args}'#use $@ to print out all arguments at onceecho $@ ' -> echo $@'# use $# variable to print out# number of arguments passed to the bash scriptecho Number of arguments passed: $# ' -> echo Number of arguments passed: $#' 
/arguments.sh Bash Scripting Tutorial 

Executing shell commands with bash

#!/bin/bash# use backticks " ` ` " to execute shell commandecho `uname -o`# executing bash command without backticksecho uname -o 

Reading User Input

#!/bin/bashecho -e "Hi, please type the word: \c "read  wordecho "The word you entered is: $word"echo -e "Can you please enter two words? "read word1 word2echo "Here is your input: \"$word1\" \"$word2\""echo -e "How do you feel about bash scripting? "# read command now stores a reply into the default build-in variable $REPLYreadecho "You said $REPLY, I'm glad to hear that! "echo -e "What are your favorite colours ? "# -a makes read command to read into an arrayread -a coloursecho "My favorite colours are also ${colours}, ${colours} and ${colours}:-)" 

Bash Trap Command

#!/bin/bash# bash trap commandtrap bashtrap INT# bash clear screen commandclear;# bash trap function is executed when CTRL-C is pressed:# bash prints message => Executing bash trap subrutine !bashtrap(){    echo "CTRL+C Detected !...executing bash trap !"}# for loop from 1/10 to 10/10for a in `seq 1 10`; do    echo "$a/10 to Exit."     sleep 1;doneecho "Exit Bash Trap Example!!!" 

Bash Arithmetics

#!/bin/bashecho '### let ###'# bash additionlet ADDITION=3+5echo "3 + 5 =" $ADDITION# bash subtractionlet SUBTRACTION=7-8echo "7 - 8 =" $SUBTRACTION # bash multiplicationlet MULTIPLICATION=5*8echo "5 * 8 =" $MULTIPLICATION# bash divisionlet DIVISION=4/2echo "4 / 2 =" $DIVISION# bash moduluslet MODULUS=9%4echo "9 % 4 =" $MODULUS# bash power of twolet POWEROFTWO=2**2echo "2 ^ 2 =" $POWEROFTWOecho '### Bash Arithmetic Expansion ###'# There are two formats for arithmetic expansion: $ # and $(( expression #)) its your choice which you useecho 4 + 5 = $((4 + 5))echo 7 - 7 = $echo 4 x 6 = $((3 * 2))echo 6 / 3 = $((6 / 3))echo 8 % 7 = $((8 % 7))echo 2 ^ 8 = $echo '### Declare ###'echo -e "Please enter two numbers \c"# read user inputread num1 num2declare -i resultresult=$num1+$num2echo "Result is:$result "# bash convert binary number 10001result=2#10001echo $result# bash convert octal number 16result=8#16echo $result# bash convert hex number 0xE6Aresult=16#E6Aecho $result 

Nested If statements

Talking of indenting. Here’s a perfect example of when it makes life easier for you. You may have as many if statements as necessary inside your script. It is also possible to have an if statement inside of another if statement. For example, we may want to analyse a number given on the command line like so:

nested_if.sh

  1. #!/bin/bash
  2. if
  3. then
  4. echo Hey that\’s a large number.
  5. if (( $1 % 2 == 0 ))
  6. then
  7. echo And is also an even number.
  8. fi
  9. fi

Let’s break it down:

  • Line 4 — Perform the following, only if the first command line argument is greater than 100.
  • Line 8 — This is a light variation on the if statement. If we would like to check an expression then we may use the double brackets just like we did for variables.
  • Line 10 — Only gets run if both if statements are true.

Yo dawg, I herd you like if statements so I put an if statement inside your if statement.

Xzibit

(Xzibit didn’t actually say that but I’m sure he would have, had he hosted Pimp My Bash Script.)

You can nest as many if statements as you like but as a general rule of thumb if you need to nest more than 3 levels deep you should probably have a think about reorganising your logic.

Introduction

Bash if statements are very useful. In this section of our Bash Scripting Tutorial you will learn the ways you may use if statements in your Bash scripts to help automate tasks.

If statements (and, closely related, case statements) allow us to make decisions in our Bash scripts. They allow us to decide whether or not to run a piece of code based upon conditions that we may set. If statements, combined with loops (which we’ll look at in the next section) allow us to make much more complex scripts which may solve larger tasks.

Like what we have looked at in previous sections, their syntax is very specific so stay on top of all the little details.

Bash nested if Statements

Nested if are useful in the situation where one condition will be checked based on results of outer condition.

if 
then
    if 
    then
       <code block>
    else
       <code block>
    fi
else
    if 
    then
       <code block>
    fi
fi

For example below small shell program is for finding the greatest value between 3 values taken input by user. This program will work with numeric values only. If two values are similar it will print only one value.

#!/bin/bash

read -p "Enter value of i :" i
read -p "Enter value of j :" j
read -p "Enter value of k :" k

if 
then
    if 
    then
        echo "i is greatest"
    else
        echo "k is greatest"
    fi
else
    if 
    then
        echo "j is greatest"
    else
	echo "k is greatest"
    fi
fi

Test

The square brackets ( ) in the if statement above are actually a reference to the command test. This means that all of the operators that test allows may be used here as well. Look up the man page for test to see all of the possible operators (there are quite a few) but some of the more common ones are listed below.

Operator Description
! EXPRESSION The EXPRESSION is false.
-n STRING The length of STRING is greater than zero.
-z STRING The lengh of STRING is zero (ie it is empty).
STRING1 = STRING2 STRING1 is equal to STRING2
STRING1 != STRING2 STRING1 is not equal to STRING2
INTEGER1 -eq INTEGER2 INTEGER1 is numerically equal to INTEGER2
INTEGER1 -gt INTEGER2 INTEGER1 is numerically greater than INTEGER2
INTEGER1 -lt INTEGER2 INTEGER1 is numerically less than INTEGER2
-d FILE FILE exists and is a directory.
-e FILE FILE exists.
-r FILE FILE exists and the read permission is granted.
-s FILE FILE exists and it’s size is greater than zero (ie. it is not empty).
-w FILE FILE exists and the write permission is granted.
-x FILE FILE exists and the execute permission is granted.

A few points to note:

  • = is slightly different to -eq. will return false as = does a string comparison (ie. character for character the same) whereas -eq does a numerical comparison meaning will return true.
  • When we refer to FILE above we are actually meaning a path. Remember that a path may be absolute or relative and may refer to a file or a directory.
  • Because is just a reference to the command test we may experiment and trouble shoot with test on the command line to make sure our understanding of its behaviour is correct.
  1. test 001 = 1
  2. echo $?
  3. 1
  4. test 001 -eq 1
  5. echo $?
  6. touch myfile
  7. test -s myfile
  8. echo $?
  9. 1
  10. ls /etc > myfile
  11. test -s myfile
  12. echo $?

Let’s break it down:

  • Line 1 — Perform a string based comparison. Test doesn’t print the result so instead we check it’s exit status which is what we will do on the next line.
  • Line 2 — The variable $? holds the exit status of the previously run command (in this case test). 0 means TRUE (or success). 1 = FALSE (or failure).
  • Line 4 — This time we are performing a numerical comparison.
  • Line 7 — Create a new blank file myfile (assuming that myfile doesn’t already exist).
  • Line 8 — Is the size of myfile greater than zero?
  • Line 11 — Redirect some content into myfile so it’s size is greater than zero.
  • Line 12 — Test the size of myfile again. This time it is TRUE.

Summary

if
Perform a set of commands if a test is true.
else
If the test is not true then perform a different set of commands.
elif
If the previous test returned false then try this one.
&&
Perform the and operation.
||
Perform the or operation.
case
Choose a set of commands to execute depending on a string matching a particular pattern.
Indenting
Indenting makes your code much easier to read. It get’s increasingly important as your Bash scripts get longer.
Planning
Now that your scripts are getting a little more complex you will probably want to spend a little bit of time thinking about how you structure them before diving in.

String Comparisons

= equal
!= not equal
< less then
> greater then
-n s1 string s1 is not empty
-z s1 string s1 is empty
#!/bin/bash#Declare string S1S1="Bash"#Declare string S2S2="Scripting"if ; then	echo "Both Strings are equal"else 	echo "Strings are NOT equal"fi 

#!/bin/bash#Declare string S1S1="Bash"#Declare string S2S2="Bash"if ; then	echo "Both Strings are equal"else 	echo "Strings are NOT equal"fi 

Bash File Testing

-b filename Block special file
-c filename Special character file
-d directoryname Check for directory existence
-e filename Check for file existence
-f filename Check for regular file existence not a directory
-G filename Check if file exists and is owned by effective group ID.
-g filename true if file exists and is set-group-id.
-k filename Sticky bit
-L filename Symbolic link
-O filename True if file exists and is owned by the effective user id.
-r filename Check if file is a readable
-S filename Check if file is socket
-s filename Check if file is nonzero size
-u filename Check if file set-ser-id bit is set
-w filename Check if file is writable
-x filename Check if file is executable
#!/bin/bashfile="./file"if ; then	echo "File exists"else 	echo "File does not exists"fi 

Similarly for example we can use while loop to check if file does not exists. This script will sleep until file does exists. Note bash negator «!» which negates the -e option.

#!/bin/bashwhile ; do# Sleep until file does exists/is createdsleep 1done 
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Adblock
detector