📜 ⬆️ ⬇️

Console to the masses. Go to the bright side. Bash

keep-calm-and-bin-bash

Introduction


Ease of use of a tool is how much it helps in solving a specific problem. It is also important that we can customize this tool to your needs. A nice bonus is the fact that we can expand and complement our tool with new features.

We got to the most interesting and fascinating topic - these are bash scripts. When you start a terminal, a special shell program works inside it - the shell (English) - the command interpreter. Shell understands all the commands you type on the keyboard and processes them. It also displays error messages, monitors the correctness of commands and their syntax. An example of such commands might be: change directory, create a new directory, add a text file, edit a text file, save changes, and others .

Shell programs are quite a few. One of the first successful implementations is called sh . It was developed by Stephen Born in 1977 ( wiki ). In 99% of cases, when you are working on a computer running OS *nix , the default shell that processes commands is bash. Of course, there is also a shell like zsh and others, but that’s another story.
')
An important point. This article is not a complete guide to bash scripts. The main goal is to examine the basics of bash programming, show examples, give the reader basic knowledge that is enough to move on. At the end of the article there are useful links and you can study this topic in more detail.

The basics


The first section is devoted to the basics. Consider how to start any script. How variables are determined and how to change the values ​​of these variables. We will also understand how to pass arguments to the script and process these arguments inside the script. The next section is branching. And the last, but no less important section is cycles.

Any bash script should start with the line:

 #!/bin/bash 

Thus, we tell the interpreter what program to call if the script is executable.

To improve the portability of the code, the following line should be indicated:

 #!/usr/bin/env bash 

It is not guaranteed that on different systems the path to bash will be the same. By specifying the env we protect ourselves from such surprises. Again, this is if you plan to use your scripts on different systems, computers, etc. If this is not necessary, the first option is perfect. All examples use the first option.

By default, all files created by the user are not executable. We must explicitly specify this parameter for a specific file using the command:

 chmod +x <filename> 

You can run the script without this parameter using the command:

 bash <filename> 

Variables


A variable is a named area in memory. This means that the variable has a name and it refers to a specific memory location in the computer. Usually variables are used to store some information. Any variable consists of two parts: name and value.

Variable Name:



Variable value:



Create (rewrite) variable:


 path="$HOME" 

Please note that before the equal sign and after it there should be no spaces.

Read variable:


 "$path"  "${path}" 

In most cases, both options work the same way. In some cases, when an ambiguity of interpretation occurs, only the following form of writing will work correctly: "${path}"

Passing arguments to the script:


 ./script.sh arg1 arg2 arg3 … argN 

Argument processing inside the script:


 "$1" #   "$2" #   "$0" #   "$#" #   "$*" #       () "$@" #  $*,          (), # ..       

Well, it's time to write the first script. As it should be, the script will display the string “Hello, world!” On the user's screen.

#!/bin/bash
echo "Hello, world!"
# ./hw.sh - how this script should be called
view raw hw.sh hosted with ❤ by GitHub
Just like that, on bash you can implement an example with “Hello, world!”. I remind you that all the examples you can find in the repository . Let's look at another example. This time we will work with variables, as well as with the arguments that the user passes to the script.

#!/bin/bash
var1="$1"
var2="$2"
echo "Arguments are ${var1} and ${var2}"
# ./variables.sh Hello world - how this script should be called
view raw variables.sh hosted with ❤ by GitHub
Please note that all programs written in bash usually have the .sh extension.

Branching


With basics and variables sorted out a bit. We now turn to the branching. The following constructions are available for working with branches in bash:

  1. if
  2. if/else
  3. if/elif/else
  4. case/in/esac

The first three options are similar and each following construction complements the previous one. Consider the simplest option:

if [[ condition ]]
then
# an action, if condition is true
fi
view raw if.sh hosted with ❤ by GitHub
Notice that there is a semicolon at the end of each line. It is possible to write the conditions (and not only) in one line, then a semicolon is needed.

When working with branches, we need to somehow check whether the string is empty or not, whether the number is zero or not equal, whether we are working with a file or this is a directory. For such tasks, bash has its own syntax. I divided it into four categories: strings, numbers, files, logical. Let's look at them.

Conditions (lines):


-z <string> # string is empty
-n <string> # string is not empty
<str1> == <str2> # strings are equal
<str1> != <str2> # strings are not equal

Conditions (numbers / lines):


<numbers/strings> operation <numbers/strings>
-eq, (==) # equal
-ne, (!=) # not equal
-lt, (<) # less than
-le # less than or equal
-gt, (>) # more than
-ge # more than or equal

Conditions (files):


-e <path> # path is exist
-f <path> # is file
-d <path> # is directory
-s <path> # file size more than 0
-x <path> # file is executable

Conditions (logical):


! # denial of boolean expression
&& # boolean "and"
|| # boolean "or"

Option if/else we skip. But if/elif/else propose to consider the example:

#!/bin/bash
if [[ -f "$1" ]]
then
echo "Removing file ${1}"
rm "$1"
elif [[ -d "$1" ]]
then
echo "Removing dir ${1}"
rm -r "$1"
else
echo "Can't remove ${1}"
fi
view raw if_elif_else.sh hosted with ❤ by GitHub
The script expects to receive one argument as input — the file name or the directory name. Next is checking whether it is really a file or directory. If so, do the removal. If the argument passed is neither a file nor a directory, output a message to the user that deletion is impossible.

It remains for us to consider the last variant of branching - this is case/in/esac . If we draw an analogy with JavaScript, then this is a variant of the implementation of switch/case . Consider this construction also by example.

#!/bin/bash
# ./case_in_esac.sh 2 1
# Creating dir 1
# ./case_in_esac.sh 1 2
# Creating file 2
if [[ "$#" -ne 2 ]]
then
echo "You should specify exactly two arguments!"
else
case "$1" in
1)
echo "Creating file ${2}"
touch "$2"
;;
2)
echo "Creating dir ${2}"
mkdir "$2"
;;
*)
echo "Wrong value"
;;
esac
fi
view raw case_in_esac.sh hosted with ❤ by GitHub

This script expects to receive two numeric arguments: one and two. And depending on which argument is the first, a file or directory is created. There is also a check for the number of arguments that the user passed. The number of arguments should not be less than two. If the user passed incorrect arguments, we deduce to him the message on it. The default value is responsible for this.

Cycles


The next section, which we will look at, is working with cycles. For the organization of cycles in the arsenal of bash there are such constructions: for/in and while .

Let's look at the for/in syntax:

for i in array
do
# an action, i on every iteration is getting
# the next value from array
done
view raw for_in.sh hosted with ❤ by GitHub

i is the name of the variable to which the array element will be passed. array is the array itself. At each subsequent iteration, i obtains the next value from the array. Consider an example:

#!/bin/bash
for i in 1 2 3 4 5
do
file_name="file${i}.txt"
if [[ -e "$file_name" ]]
then
continue
fi
echo "Creating file ${file_name}"
touch "$file_name"
done
view raw for_in.sh hosted with ❤ by GitHub

There is a list of values: 1 2 3 4 5 . We start a cycle that runs through all values. Create a variable file_name . Then we check if there is a file with that name. If there is no file, we create it, if we skip the creation step.

You may have noticed the word continue is a keyword. Allows you to skip the iteration and continue to work on. Also there is a keyword break - interrupts the execution of the script. These keywords are used only in the context of if/else .

The following construction, which we consider to work with cycles, will be while :

while [ condition ]
do
# an action, while condition is true
done
view raw while.sh hosted with ❤ by GitHub

I suggest to immediately consider an example:

#!/bin/bash
# while.sh
again="yes"
while [ "$again" = "yes" ]
do
echo "Please enter a name:"
read name # !!!
echo "The name you entered is ${name}"
echo "Do you wish to continue? (yes/no)"
read again # !!!
done
view raw while.sh hosted with ❤ by GitHub

In this example, there are two lines with the read keyword. Let us consider in more detail how these lines work. Namely: line number twelve and line number eight. Let's start with line number twelve. While the $again variable is equal to the value "yes" , the program asks to enter the user name and displays it on the screen. After this, the script asks if we want to continue. And so on until the user enters any other string instead of yes or simply presses Enter . But much more interesting is the code on line number eight. There is a variable $name , which is not declared before. This variable is created dynamically and then its value is read.

useful links


  1. Basics of bash
  2. bash-handbook
  3. Pitfalls Bash
  4. Advanced Bash-Scripting Guide [ru] ( Konkase thanks for the link)
  5. Advanced Bash-Scripting Guide ( aso thanks for the link)
  6. Google's Shell Style Guide ( leoismyname thanks for the link)
  7. What is the meaning and advantage of #! / Usr / bin / env? ( Borz thanks for the link)
  8. Learn the Command Line
  9. The Command Line Crash Course
  10. Linux Command Line in English
  11. Basic Unix commands

Instead of conclusion


On this positive note, we’ll end up with bash. This information is enough to implement simple scripts, as well as to automate everyday tasks.

Looking ahead, I’ll say that in the next article there will be less theory and even more examples on how to automate routine tasks using bash scripts. Keep for updates.

That's all. Thanks for attention. Who read to the end, special thanks. See you in the next article.

UPD. The text of the article is updated based on the discussions in the comments. Thanks to everyone who took an active part in the discussions. You made the article even better. And not just an article.

Source: https://habr.com/ru/post/319670/


All Articles