Bash theory

/ bin / bash

0. Index

  1. Things that happen to most people
  2. Structure of a script
  3. Print on screen
  4. Read user INPUT
  5. Calculations in bash
  6. Terms of use
  7. Loops
  8. Features
  9. getops

1. Things that happen to most people

/ bin / bash or / bin / sh

One of the first things the machine does when executing our script is looking at which shell it should do it with. On most current linux systems / bin / sh is a link to / bin / bash, but this is not always the case, for example in distributions that use busybox they bring Sh and usually they also bring Bash, but if you use / bin / sh, it will not run with Bash. That is why I recommend always using / bin / bash.

Unicode vs. ASCII

Have you ever wondered why you can't use "¿" or "ñ" in your scripts? Or use accents? It can be quite annoying in interactive scripts. That is because the default encoding of Bash is ASCII, or what is the same, the English character set. To change it, we only have to tell our script that we want to use Unicode. For that you have to add a line just after the command interpreter:

# - * - ENCODING: UTF-8 - * -

Be careful, it is important that this line is at the beginning of the script.

Make the script executable

It's funny how many people run the scripts with «$ bashscript.sh" instead of "$ ./script.sh'After all, this is what we have defined a shell for.

To add execution permissions, you must execute:

sudo + x script.sh
If our script is executable, we can add it to our PATH and make it executable from anywhere / folder on our computer. For that we must add either to the .bashrc of our user or to / etc / bashrc the line
BIN = "folder where we have the scripts" PATH = "$ BIN $ PATH"
It is a Bash rule to write variable names in all uppercase. Many people do not follow this rule, but for long scripts it is appreciated because they make them much more readable

2. Structure of a script

  1. Head
  2. Definition of global variables
  3. Help
  4. Features
  5. Main body

The header is where we indicate which shell we want to use and the encoding. The advantage of the functions is to reuse code that is repeated by writing it only once and to make it easier to understand the script, for code that exceeds 100 lines it is very useful.

In order to use functions, they must be defined with before the main body of our script. And if we want to use variables at the global level of all our script, both in the main body and in the functions, we must define them at the beginning of everything, just after the header.

Lastly, it is good practice to write a helper function for when our script runs badly or with bad parameters. Obviously, in those cases we want to exit the script immediately, without reading the functions. For that we can use:

function help () {echo "" "Our well-formatted help text." "" exit if [[-z $ 1 || $ 1 == "-h" || $ 1 == "--help"]]; then help fi

If we add "exit" to the help function, we will exit the script every time we run help, for example after error messages, etc. We save a few lines of code.

The condition indicates display help on the screen and exit if the script is run without parameters or if -h / –help is specified. If you look that is the standard behavior of most linux programs.

The use of the 3 quotes with echo allows you to use line breaks without leaving the message to be displayed by echo. For multi-line messages it is much more convenient to use echo only once.

3. Print on screen

There are 2 main commands for printing to screen in bash: «threw out" Y "printf«. They are both just as fast and both are part of bash. The main difference for a beginner is that echo adds a newline at the end, whereas «printf»Does not.

Echo is very good and is what most people use, however when reading the user's INPUT, or when you want to print variables taken from files by word processing, strange things can happen. They are usually easily solved, as easy as changing the double quotes to single or vice versa, or taking the variable references out of the quotes. «Echo»Does strange things also depending on how it was compiled, if we always use Ubuntu or always Fedora, it doesn't affect us, but if we change the distribution it does.

That is why I use «printf«, Which does not give me headaches and also behaves more like«printf»From C or the«print»Of Python, that is very important if you ever want to port your script to another programming language.

For a more extensive discussion you can visit this question from Unix & Linux on Stack Exchange.

4. Read user INPUT

Everything we write after the name of our script and before hitting the ENTER key is automatically saved in special variables. These variables are of the type $ X where X is a number.

«$0»Indicates the name of our script and from«$1»To infinity everything that we have written later is variable. For example:

cat << EOF >> test.sh #! / bin / bash # - * - ENCODING: UTF-8 - * - printf "\ $ 0 = $ 0 \ n" printf "\ $ 1 = $ 1 \ n" printf "\ $ 2 = $ 2 \ n "EOF chmod + x script.sh ./script.sh my file.txt

We create a test script, make it executable, and run it with 2 parameters. We obtain the screen output of:

$ 0 = ./script.sh $ 1 = my $ 2 = file.txt

Using quotes we could have passed "my file.txt" to "$ 1".

We can also read the INPUT of a user with the "read" command, directly indicating the variable where we want to save the parameter. For example:

printf "What's your name? \ n" read NAME printf "Hello, $ NAME. \ n"
Be careful with the assignment of variables. "$ VAR = content" is going to produce an error, no spaces can be left between the equal sign, the variable name and the content. The correct usage is "VAR = content"

5. Calculations in Bash

For that we can use «expose«, As long as we don't need to do complex calculations. Two things should be noted, the first is that «expose»Only admits whole numbers, the second is that the division returns the whole result, to see the rest we can use«%«.

Usually we will want to assign the result of expr to a variable. We can do that in two ways:

VAR2 = `expr $ VAR1 / 10` VAR2 = $ (expr $ VAR1 / 100)

You can also skip «expose»Using double parentheses:

VAR2 = $ (($ VAR1 / 100))
For a further explanation of «expose»Or an alternative that uses whole numbers, you can look at this KZKG ^ gaara entry.

6 Terms

It has already been written in a very extensive way about «if","else","elif»And conditions. You can read about that in:

I just want to highlight the difference between the use of simple square brackets, «[]«, And double brackets,«[[]]«, For the conditions. With double brackets we can use additional conditions:

  • «&&»For and
  • «||»For or

To use "&&" Y "||»With simple square brackets, each part should be separated in separate square brackets. The example used for the part of the script that looks to see if help needs to be executed would be:

if [-z "$ 1"] || ["$ 1" == "-h"] || ["$ 1" == "--help"]]; then help fi

It also saves us from having to write variable names in quotes to prevent errors. For example:

if [$ 1 = 1]; then printf "The parameter is equal to 1."; fi if ["$ 1" = 1]; then printf "The parameter is equal to 1."; fi if [[$ 1 = 1]]; then printf "The parameter is equal to 1."; fi

If script.sh is run without any parameters, the first case would give an error:

bash: [: =: unary operator expected
In Bash "=" and "==" are both interpreted in the same way. This does not happen in other programming languages ​​where "=" is used only to assign variables.

What has not been talked about is «CASE​«, Used to simplify«if«. Let's start at the beginning, when we don't have any «if»All the code will be executed. If we add a condition «if»We will have two cases, one in which the code block that is inside the«if»And the other case where this block is not executed.

If we add a «else«We will also have two cases, but these two cases are different from the previous ones. Because now there will be two conditional code blocks, A and B, and a C block, which is the rest of the program. A or B will be executed, and C. In the previous case it was A and C or only C.

To avoid writing conditions «if / else" within "else»And to simplify the reading of the code, it was created«elif«. When we have many conditions that depend on the previous one, for example range of numbers or the type:

VAR1 = $ 1 if [[$ VAR1 = 1]]; then printf "1 \ n" elif [[$ VAR1 = 2]]; then printf "2 \ n" elif [[$ VAR1 = 3]]; then printf "3 \ n" else printf "none \ n" fi

In the case of the last «elif»Many conditions will be read. With case this process is streamlined:

VAR1 = $ 1 case $ VAR in 1) printf "1 \ n" ;; 2) printf "2 \ n" ;; 3 | 4) printf "3 or 4, it depends \ n" ;; *) printf "none \ n" ;; that C

A variable will be read, in this case VAR1, and it will be checked if it is equivalent to any of the cases, if not, the default case "*" will be executed. Double semicolons are equivalent to «break«, They tell«CASE​»That has to end.

«Case»Can also be used as a sequence of«if«, For that you have to use« ;; & »(continue) instead of« ;; » (stop).

7. Loops

Very few loops are known in any programming language. In Bash they are «while","until" Y "for«. It has already been written in the blog about these:

There are two types of loops «for«, Those that are of the type«$ for VAR in FOODS»And what are of type C«$ for ((I = 0; I <= 10; I ++))«. The second type of loops «for»Are very useful, it has 3 parts at the beginning of the loop:

  • Declaration and initiation of variables (In this case an auxiliary variable "I = 0").
  • Execution condition (until I is less than or equal to 10).
  • Increase of the auxiliary variable

In my opinion it is the most powerful loop of all. An example, which prints all numbers from 0 to 10, inclusive:

#! / bin / bash for ((I = 0; I <= 10; I ++)); do printf "$ I \ n" done

8. Functions

There are some things that Bash doesn't allow us to do, right? At first glance, bash functions prevent you from doing 3 things: declaring local variables in functions, passing parameters to functions, and returning parameters. Everything has a solution.

Do nothing like:

#! / bin / bash VAR = 1 printc "$ VAR \ n" function hello () {VAR = 2 printf "$ VAR \ n"} hello printf "$ VAR \ n"

This prints to screen 1, 2 and 2.

To declare local variables, add «local»When declaring:

#! / bin / bash VAR = 1 printf "$ VAR1 \ n" function foo () {local VAR1 = 2 printf "$ VAR1 \ n"} printf "$ VAR1 \ n" foo printf "$ VAR1 \ n"

This prints 1, 1, 2, 1 on screen.

How do you pass parameters to a function?

#! / bin / bash # - * - ENCODING: UTF-8 - * - function hello () {printf "Hello $ 1 \ n"}

printf "What's your name? \ n"
readVAR1
hello $ VAR1

How are parameters returned?

#! / bin / bash # - * - ENCODING: UTF-8 - * - function hello () {printf "Hello holita"} printf "What's your name? \ n" read VAR1 VAR1 = $ (hello) # HERE IT IS printf "$ VAR1 $ VAR2 \ n"

As you can see, this has two drawbacks, you can only return one parameter, which can be a vector 😀, and if you want to return a parameter, you can no longer print on the screen from that function.

You can find more things about functions at [url=https://blog.desdelinux.net/programando-en-bash-parte-3/]this article from Usemoslinux[/url].

9. Getops

One of the last things you need to know about Bash to create complex scripts is «getops«. It is used to pass options to the script regardless of the order. The only downside is that it only affects short options:

#! / bin / bash # - * - ENCODING: UTF-8 - * - VARC = 0 function help () {printf "Help message \ n" exit} if [[-z $ 1]]; then help fi while getopts: ha: b: c OPT; do case $ OPT in h) help ;; :) help ;; a) VARA = $ OPTARG ;; b) VARB = $ OPTARG ;; c) VARC = 1 ;; \?) help ;; esac done # Main block of the script that # does things with VARA, VARB and VARC

«getopts»Reads the options one by one, so a loop is needed.

There are 2 types of options that can be passed using «tops«:

  • Parameters called flags, in this case -c or -h. They are specified with the letter we want to use. They are like Boolean variables, «true»(Are) or«false" (They're not here).
  • Parameters with associated arguments, -a anything, -b anything. They are specified with the letter that we want with a colon below. The argument is stored in OPTARG (this name is unchangeable).
The initial double points are to show no errors.

What does this script do?

Displays the help message when no option is passed, when the "-h" parameter is passed, when an invalid parameter is passed (for example "-x", this is done by "\?") Or when a valid parameter with no argument (":"). In the rest of the cases it saves the presence of "-c" as a 1 in VARC, and the values ​​passed with "-a" and "-b" in VARA and VARB.


Leave a Comment

Your email address will not be published. Required fields are marked with *

*

*

  1. Responsible for the data: Miguel Ángel Gatón
  2. Purpose of the data: Control SPAM, comment management.
  3. Legitimation: Your consent
  4. Communication of the data: The data will not be communicated to third parties except by legal obligation.
  5. Data storage: Database hosted by Occentus Networks (EU)
  6. Rights: At any time you can limit, recover and delete your information.

  1.   elav said

    MASTERLY. I do not say more U_U

  2.   Miguel said

    Hello very good article.
    Hey you put to give permissions sudo + x instead of chmod + x

    1.    Henry said

      $ sudo chmod + x script.sh
      (To be more exact, hehe)

      Ah, congratulations and thank you!

  3.   fire cold said

    Very good post, the truth is I congratulate you, keep it up, Greetings

  4.   Gustavo said

    And if you want the script to be visible when it is executed, step by step, seeing for example how variables, conditions and everything behave, you can use:

    sh -x script

    regards

  5.   Dago said

    PIECE of tutelage. Excellent and very well explained.
    Thank you.

  6.   Gabriel said

    Excellent post on the subject 😉

  7.   Mario Guillermo Zavala Silva said

    Very interesting and very important thanks for the information….
    CHEERS !!!

  8.   notfrombrooklyn said

    Thank you all for your congratulations, as for the Miguel command, he does not let me modify the entry once it is published. It will have to do elav I imagine.

  9.   Adrian said

    Very good!

    First of all I wanted to congratulate you on the post, I found it easy to understand and it really helps to follow the guidelines to program well in bash, especially for people who are starting to program.

    However I have found a couple of details which I think should be corrected.

    First: in section «2. STRUCTURE OF A SCRIPT »the function is not closed, which will cause problems when executing it in a script.
    The solution would be to add a brace just after the "exit" command.

    Second: in section «4. READ THE USER INPUT ”you affirm that the parameters that the user can enter range from $ 0 to infinity, however,“ bash ”will only interpret from $ 0 to $ 9, since $ 10 would be equal to $ 1 + 0.
    To solve this problem, you could either use the "shift" command to grab the following variables. Or specify the variable in braces "$ {10}", so that bash takes the values ​​together, not as $ 1 + 0.

    Without further ado, greetings!

    1.    notfrombrooklyn said

      Thanks for your comment. It totally failed to explain the correct use of exit, both in the script and in the functions. As for $ {10} I have never been able to do so much, so I have not run into that problem, it is good to know that there is a solution for that (I have already crossed out the new thing learned today 😀).

  10.   chanio said

    Thank you very much for the article! Some things you mention still lacked clarification. For example, getops.
    In the part of screen outputs, you did not mention the cat that you later mention ...
    cat <
    ***************************************
    * THIS FORM IS VERY EXPRESSIVE *
    ***************************************
    EOF

    In your example:
    cat << EOF >> test.sh
    There are two things to mention ... >> it is 'append' that is, if you repeat the same command, you will have the entire script in duplicate ... You should only use one ...
    cat << EOF> script.sh
    Yes, it should also be called script.sh
    Then in
    if [-z "$ 1"] || ["$ 1" == "-h"] || ["$ 1" == "–help"]]; then
    help
    fi

    I think it should be written ...
    if [[-z "$ 1"] || ["$ 1" == "-h"] || ["$ 1" == "–help"]]; then
    ...

    There is much more to discover from BASH.
    Could you title it "BASICS"? 🙂
    For example, parameter 'testers' like -z to see if they are empty, or -f to see if it exists as a file.

    Thanks again for your effort.
    alberto

  11.   clow_eriol said

    A very good bash script tutorial!

  12.   OCZ said

    -- ENCODING: UTF-8 --

    It's the first time I've seen that line to set character encoding in a bash script. It seems to me more like Python than Bash. Is it really necessary? I have searched for a reference on Google but I can't find anything, do you have a link on hand that talks about this matter? Specifically on the suitability of that line.

    In my opinion, to write scripts in Bash using UTF-8, you only need to save the text file as such (without BOM) and have certain environment variables, (LANG and LC_ *), correctly set.
    Then, obviously, it is necessary that the executed commands are prepared for encodings other than ASCII. For example, if we want to go to uppercase, this doesn't seem to work:
    «Echo áéíóú | tr az AZ »
    o:
    «Echo áéíóú | tr [: lower:] [: upper:] »
    and it is better to use:
    «Echo áéíóú | awk '{print toupper ($ 0)}' ».

    1.    notfrombrooklyn said

      About the «encoding»Has been mentioned in this blog before:

      Bash: how to make a script executable
      Post installation utility script

    2.    borriquito like you said

      Someone correct me, but that encoding line (# -- ENCODING: UTF-8 --) It has nothing to do with bash or the shell: it is a comment line (begins with #) and serves to tell the EDITOR that we use to write the script (vim, emacs ...) the encoding of the file.

      In fact, bash doesn't see such a line, because it is a comment line.

  13.   JoRgE-1987 said

    Excellent tutorial, as Sysadmin knowing Scripting in Bash is essential, it is useful for everything.

    Very, very good!

    Regards!

  14.   Edward Cuomo said

    In case it is useful to anyone, here are several uses and examples to create your own Scripts: https://github.com/reduardo7/hsabx

  15.   Lito Black said

    Very good. New things to add to my scripts. The encodig and printf thing did not have it.
    Thank you!!!

  16.   xxxtonixxx said

    Sooo good article! I keep this one for favorites, it would be nice to correct what is wrong and even expand it with more content. Applause for all this info !!!!