Often, when people refer to the "Shell scripting language", they are bash,ksh,sh or other similar Linux/unix scripting languages emerging in their minds. Scripting languages are another way to communicate with a computer. Users can move the mouse and click on a variety of objects, such as buttons, lists, marquee, and so on, using a graphical window interface (regardless of whether it's Windows or Linux). But this approach is inconvenient every time a user wants the computer/server to do the same task (for example, bulk conversion of photos, or downloading of new movies, MP3, etc.). For all of these things to be simple and automated, we can use shell scripts.
Some programming languages, such as Pascal, FoxPro, C, Java, and so on, need to be compiled before executing. They need the right compiler to get our code to do a task.
Other programming languages, like PHP, JavaScript, VisualBasic, and so on, do not need compilers, so they need interpreters, and we can run programs without compiling the code.
The shell script is also like an interpreter, but it is often used to invoke external compiled programs. It then captures the output, exits the code, and processes it according to the situation.
One of the most popular Shell scripting languages in the Linux world is bash. And I think (this is my own opinion) the reason is that by default, the bash shell allows users to easily navigate through historical commands (previously performed), and, conversely, ksh requires some tweaking of. Profile, or remembering some "magic" combinations to check history and revise commands.
Well, I think these introductions are enough, the rest of the environment is the best for you, leave you to judge it. From now on, I'll just talk about bash and its scripts. In the example below, I will use CentOS 6.6 and bash-4.1.2. Make sure you have the same version, or later.
Shell Script Stream
The Shell scripting language is similar to chatting with several people. You just think of all the commands as the people who can help you do things, as long as you ask them in the right way. For example, you want to write a document. First, you need paper. Then, you need to say something to someone and ask him to write it for you. Finally, you want to store it somewhere. Or, you want to build a house, so you need to ask the right person to clear the space. When they say, "It's done," then some other engineers can help you with the drywall. Finally, when the engineers tell you "it's done", you can call the painter to whitewash the house. What happens if you let the painter whitewash the wall before it is ready? I think they're going to start whining. Almost all of these human-like commands speak, and if they do work without problems, they tell "standard output." If they can't do what you ask them to do-they will tell "standard mistakes". So, finally, all the commands listen to you through "standard input".
Quick example-when you open a Linux terminal and write some text-you are talking to bash through "standard input". So, let's ask bash shell who am I? ) bar.
Copy Code code as follows:
Root@localhost ~]# who am I <---you through the standard input to the bash Shell said
Root pts/0 2015-04-22 20:17 (192.168.1.123) <---Bash shell answers you through standard output
Now, let's say some of the things that bash doesn't understand:
Copy Code code as follows:
[Root@localhost ~]# Blablabla <---ha, you're talking to the standard input again.
-bash:blablabla:command not found <---Bash is whining through standard bugs.
":" The first word before is usually an order to grumble at you. In fact, each of these streams has its own index number (LCTT: File handle number):
Standard input (stdin)-0
Standard output (stdout)-1
Standard error (STDERR)-2
If you really want to know which output command says something-you need to redirect that statement to (use the greater-than sign ">" and Stream Index) files after the command:
Copy Code code as follows:
[Root@localhost ~]# Blablabla 1> output.txt
-bash:blablabla:command not found
In this example, we try to focus on directional streaming 1 (stdout) to a file named Output.txt. Let's look at what we did with the contents of the file, and use the Cat command to do this:
Copy Code code as follows:
[Root@localhost ~]# Cat Output.txt
[Root@localhost ~]#
It seems to be empty. Okay, now let's redirect Stream 2 (stderr):
Copy Code code as follows:
[Root@localhost ~]# Blablabla 2> error.txt
[Root@localhost ~]#
Well, we're not seeing any complaints. Let's check that file:
Copy Code code as follows:
[Root@localhost ~]# Cat Error.txt
-bash:blablabla:command not found
[Root@localhost ~]#
That's true! We saw that all the whining had been recorded in the Errors.txt file.
Sometimes, commands produce both stdout and stderr. To redirect them to a different file, we can use the following statement:
Command 1>out.txt 2>err.txt
To shorten a little statement, we can ignore "1" because stdout is redirected by default:
Command >out.txt 2>err.txt Well, let's try to do something "bad". Let's delete file1 and folder1 with the RM command:
Copy Code code as follows:
[Root@localhost ~]# rm-vf folder1 file1 > OUT.txt 2>err.txt
Now check out the following output file:
Copy Code code as follows:
[Root@localhost ~]# Cat OUT.txt
Removed ' file1 '
[Root@localhost ~]# Cat Err.txt
Rm:cannot remove ' folder1 ': is a directory
[Root@localhost ~]#
As we can see, the different streams are separated into different files. Sometimes it's not very convenient, because we want to see what happens in front of or behind certain actions when there is an error. To achieve this, we can redirect two streams to the same file:
Command >>out_err.txt 2>>out_err.txt
Note: Please note that I use ">>" instead of ">". It allows us to attach to a file instead of overwriting it.
We can also redirect one stream to another:
Command >out_err.txt 2>&1
Let me explain. The standard output of all commands will be redirected to Out_err.txt, and the error output will be redirected to stream 1 (as explained above) and the stream will be redirected to the same file. Let's look at this example:
Copy Code code as follows:
[Root@localhost ~]# rm-fv folder2 file2 >out_err.txt
[Root@localhost ~]# Cat Out_err.txt
Rm:cannot remove ' Folder2 ': is a directory
Removed ' file2 '
[Root@localhost ~]#
Looking at the output of these combinations, we can describe it as: first, the RM command tries to remove Folder2, and it will not succeed because Linux requires the-R key to allow the RM command to delete the folder, and the second file2 will be deleted. By providing the-V (Details) key for RM, we have the RM command tell us each deleted file or folder.
These are all the things you need to know about redirects. I mean, almost, because there's a more important redirection tool, it's called "piping." By using the | (pipe) notation, we usually redirect the stdout stream.
For example, we have such a text file:
Copy Code code as follows:
[Root@localhost ~]# Cat Text_file.txt
This line does not contain H e l o Word
This lilne contains Hello
This also containd Hello
This one no due to HELLO all capital
Hello Bash world!
And we need to find some of these rows with "Hello", and there's a grep command in Linux that can do the job:
Copy Code code as follows:
[Root@localhost ~]# grep Hello text_file.txt
This lilne contains Hello
This also containd Hello
Hello Bash world!
[Root@localhost ~]#
When we have a file and want to search inside it, it's pretty good. What if we need to look up something in the output of another command? Yes, of course, we can redirect the output to a file and then look it up in the file:
Copy Code code as follows:
[Root@localhost ~]# Fdisk-l>fdisk.out
[Root@localhost ~]# grep "Disk/dev" fdisk.out
disk/dev/sda:8589 MB, 8589934592 bytes
disk/dev/mapper/volgroup-lv_root:7205 MB, 7205814272 bytes
disk/dev/mapper/volgroup-lv_swap:855 MB, 855638016 bytes
[Root@localhost ~]#
If you're going to grep some double quotes that come with spaces!
Note: The Fdisk command displays information about the Linux operating system disk drives.
As we can see, this is a very inconvenient way to make the temporary file space a mess. To complete this task, we can use the pipeline. They allow us to redirect the stdout of one command to the stdin stream of another:
Copy Code code as follows:
[Root@localhost ~]# fdisk-l | grep "Disk/dev"
disk/dev/sda:8589 MB, 8589934592 bytes
disk/dev/mapper/volgroup-lv_root:7205 MB, 7205814272 bytes
disk/dev/mapper/volgroup-lv_swap:855 MB, 855638016 bytes
[Root@localhost ~]#
As you can see, we don't need any temporary documents to get the same result. We redirected the Fdisk stdout to grep stdin.
NOTE: Pipe redirection is always left to right.
There are several other redirects, but we'll put them in the back.
Displaying custom information in the shell
As we know, usually, the interaction with the shell and the communication within the shell is done in a conversational manner. So let's create some real scripts that will also talk to us. This will allow you to learn some simple commands and have a better understanding of the concept of scripting.
Let's say we're the head desk manager for a company and we want to create a shell script that registers call information: Phone number, username, and a brief description of the problem. We intend to store this information in regular text file Data.txt for future statistics. The script itself is working in a conversational way, which makes it easier for the staff at the General Service desk to live a little. Well, first we need to show questions. For display information, we can use the Echo and printf commands. Both are used to display information, but printf is more powerful, because we can format the output nicely, so we can align it right, left, or leave a special space for the information. Let's start with a simple example. To create a file, use your usual text editor (Kate,nano,vi, ...). , and then create a file named Note.sh that writes these commands:
echo "Phone number?" How do I run/execute a script?
After we save the file, we can use the Bash command to run and take our file as its parameters:
Copy Code code as follows:
[Root@localhost ~]# Bash note.sh
Phone number?
In fact, it's not convenient to execute scripts like this. It is more comfortable if you do not use the Bash command as a prefix. To make the script executable, we can use the chmod command:
Copy Code code as follows:
[Root@localhost ~]# Ls-la note.sh
-rw-r--r--. 1 root Apr 20:52 note.sh
[Root@localhost ~]# chmod +x note.sh
[Root@localhost ~]# Ls-la note.sh
-rwxr-xr-x. 1 root Apr 20:52 note.sh
[Root@localhost ~]#
Note: The LS command displays the files in the current folder. By adding the-la key, it displays more file information.
As we can see, before the chmod command executes, the script has only read (R) and write (w) permissions. After the chmod +x is executed, it obtains the Execute (x) permission. (For more details on permissions, I'll cover them in the next article.) Now, all we need to do is run:
Copy Code code as follows:
[Root@localhost ~]#./note.sh
Phone number?
Before the script name, I added the./group. (dot) in the Unix world means the current position (current folder),/(slash) is the folder separator. (in Windows, we use backslashes to represent the same function) so this whole combination means "execute note.sh script from current folder". I thought that if I ran the script with the full path, you'd be clearer:
Copy Code code as follows:
[Root@localhost ~]#/root/note.sh
Phone number?
[Root@localhost ~]#
It can also work.
If all Linux users have the same default shell, then everything is OK. If we just execute the script, the default user shell will be used to parse the script content and run the command. Different shell syntax, internal commands, and so on are a little different, so to ensure that our script will use bash, we should add #!/bin/bash to the first line of the file. In this way, the default user shell will invoke/bin/bash, and only then will the commands in the script be executed:
Copy Code code as follows:
[Root@localhost ~]# Cat note.sh
#!/bin/bash
echo "Phone number?"
Until now, we were 100% sure bash would be used to parse our scripting content. Let's move on.
Read input
After the message is displayed, the script waits for the user to answer. There is a read command to receive the user's answer:
Copy Code code as follows:
#!/bin/bash
echo "Phone number?"
Read phone
After execution, the script waits for the user to enter until the user presses the [Enter] key to end the input:
Copy Code code as follows:
[Root@localhost ~]#./note.sh
Phone number?
12345 <---Here's what I entered.
[Root@localhost ~]#
Everything you enter will be stored in the variable phone, to display the value of the variable, we can also use the echo command:
Copy Code code as follows:
[Root@localhost ~]# Cat note.sh
#!/bin/bash
echo "Phone number?"
Read phone
echo "You have entered $phone as a phone number"
[Root@localhost ~]#./note.sh
Phone number?
123456
You have entered 123456 as a phone number
[Root@localhost ~]#
In the bash shell, we typically use the $ (dollar) symbol to indicate that this is a variable, except that it is not available in the variable and a few other times (will be explained in the future).
OK, now we're going to add the remaining questions:
Copy Code code as follows:
#!/bin/bash
echo "Phone number?"
Read phone
echo "Name?"
Read name
echo "Issue?"
Read issue
[Root@localhost ~]#./note.sh
Phone number?
123
Name?
Jim
Issue?
The script is not working.
[Root@localhost ~]#
Using stream redirection
It's perfect! The rest is to redirect everything to the file data.txt. As a field separator, we will use the/(slash) symbol.
Note: You can choose any separator that you think is the best, but make sure that the contents of the file do not contain the symbols, otherwise it causes extra fields to be generated in the line of text.
Don't forget to use ">>" instead of ">" because we want to append the output to the end of the file!
Copy Code code as follows:
[Root@localhost ~]# Tail-2 note.sh
Read issue
echo "$phone/$name/$issue" >>data.txt
[Root@localhost ~]#./note.sh
Phone number?
987
Name?
Jimmy
Issue?
Keybord issue.
[Root@localhost ~]# Cat Data.txt
987/jimmy/keybord issue.
[Root@localhost ~]#
Note: The tail command displays the last n lines of the file.
Get. Let's run one more time to see:
Copy Code code as follows:
[Root@localhost ~]#./note.sh
Phone number?
556
Name?
Janine
Issue?
Mouse was broken.
[Root@localhost ~]# Cat Data.txt
987/jimmy/keybord issue.
556/janine/mouse was broken.
[Root@localhost ~]#
Our files are growing, so let's add a date ahead of each line, which can be useful for fiddling with these statistics in the future. To do this, we can use the date command and specify a format because I don't like the default format:
Copy Code code as follows:
[Root@localhost ~]# Date
Thu APR 21:33:14 eest 2015 <----The default output of the date command
[Root@localhost ~]# Date "+%y.%m.%d%h:%m:%s"
2015.04.23 21:33:18 <----formatted output
There are several ways to read the output of a command to a variable, and in this simple case we will use ' (is inverted quotes, not single quotes, and wave numbers ~ in the same keys):
Copy Code code as follows:
[Root@localhost ~]# Cat note.sh
#!/bin/bash
now= ' Date ' +%y.%m.%d%h:%m:%s '
echo "Phone number?"
Read phone
echo "Name?"
Read name
echo "Issue?"
Read issue
echo "$now/$phone/$name/$issue" >>data.txt
[Root@localhost ~]#./note.sh
Phone number?
123
Name?
Jim
Issue?
Script hanging.
[Root@localhost ~]# Cat Data.txt
2015.04.23 21:38:56/123/jim/script hanging.
[Root@localhost ~]#
Well...... Our script looks a little ugly, let's beautify it. If you want to read the read command manually, you will find that the Read command can also display some information. To implement this feature, we should use the-P key to add information:
Copy Code code as follows:
[Root@localhost ~]# Cat note.sh
#!/bin/bash
now= ' Date ' +%y.%m.%d%h:%m:%s '
Read-p "Phone number:" Phone
Read-p "Name:" Name
Read-p "Issue:" Issue
echo "$now/$phone/$name/$issue" >>data.txt
You can find a lot of interesting information about each command directly from the console by simply typing: man read, man Echo, man date, man ...
Do you agree? It looks a lot more comfortable!
Copy Code code as follows:
[Root@localhost ~]#./note.sh
Phone number:321
Name:susane
Issue:mouse was stolen
[Root@localhost ~]# Cat Data.txt
2015.04.23 21:38:56/123/jim/script hanging.
2015.04.23 21:43:50/321/susane/mouse was stolen
[Root@localhost ~]#
The cursor is behind the message (not on a new line), which is somewhat interesting. (LCTT: If you use the Echo command to display the output, you can use the-n argument to avoid line wrapping.) )
Cycle
It's time to improve our script. If users are on the phone all day and if they have to run every time, wouldn't it be a hassle? Let's let these activities go through endlessly:
Copy Code code as follows:
[Root@localhost ~]# Cat note.sh
#!/bin/bash
While True
Todo
Read-p "Phone number:" Phone
now= ' Date ' +%y.%m.%d%h:%m:%s '
Read-p "Name:" Name
Read-p "Issue:" Issue
echo "$now/$phone/$name/$issue" >>data.txt
Done
I've exchanged the position of read phone and Now=date line. This is because I want to enter the phone number and then get the time. If I put it in the first line of the loop, then once the loop is over, the variable now gets the time immediately after the data is stored in the file. And that's not good, because the next call could be 20 minutes later, or even later.
Copy Code code as follows:
[Root@localhost ~]#./note.sh
Phone number:123
Name:jim
Issue:script still not works.
Phone number:777
Name:daniel
Issue:i broke my monitor
Phone Number: ^c
[Root@localhost ~]# Cat Data.txt
2015.04.23 21:38:56/123/jim/script hanging.
2015.04.23 21:43:50/321/susane/mouse was stolen
2015.04.23 21:47:55/123/jim/script still.
2015.04.23 21:48:16/777/daniel/i broke my monitor
[Root@localhost ~]#
Note: To exit from an infinite loop, you can press the [Ctrl]+[c] key. The shell will show ^ to represent the CTRL key.
Using pipe redirection
Let's add more features to our Frankenstein (Frankenstein) and I want the script to display a statistic after each call. For example, I want to look at the individual numbers and call me several times. For this, we should cat file Data.txt:
Copy Code code as follows:
[Root@localhost ~]# Cat Data.txt
2015.04.23 21:38:56/123/jim/script hanging.
2015.04.23 21:43:50/321/susane/mouse was stolen
2015.04.23 21:47:55/123/jim/script still.
2015.04.23 21:48:16/777/daniel/i broke my monitor
2015.04.23 22:02:14/123/jimmy/new script also not working!!!
[Root@localhost ~]#
Now, all of the output can be redirected to the Cut command, so that cuts will slice each line into one piece (we use the separator "/") and then print the second field:
Copy Code code as follows:
[Root@localhost ~]# Cat Data.txt | Cut-d "/"-f2
123
321
123
777
123
[Root@localhost ~]#
Now, we can redirect this output to another command sort:
Copy Code code as follows:
[Root@localhost ~]# Cat Data.txt | Cut-d "/"-f2|sort
123
123
123
321
777
[Root@localhost ~]#
Then leave only the unique line. To count unique entries, simply add the-C key to the Uniq command:
Copy Code code as follows:
[Root@localhost ~]# Cat Data.txt | Cut-d "/"-F2 | Sort | Uniq-c
3 123
1 321
1 777
[Root@localhost ~]#
Just add this to the end of our loop:
Copy Code code as follows:
#!/bin/bash
While True
Todo
Read-p "Phone number:" Phone
now= ' Date ' +%y.%m.%d%h:%m:%s '
Read-p "Name:" Name
Read-p "Issue:" Issue
echo "$now/$phone/$name/$issue" >>data.txt
echo "===== We got calls from ====="
Cat Data.txt | Cut-d "/"-F2 | Sort | Uniq-c
echo "--------------------------------"
Done
Run:
Copy Code code as follows:
[Root@localhost ~]#./note.sh
Phone number:454
Name:malini
Issue:windows license expired.
===== We got calls from =====
3 123
1 321
1 454
1 777
--------------------------------
Phone Number: ^c
The current scenario runs through several familiar steps:
Display message
Get user input
Store values to file
Working with stored data
But if a user has a sense of responsibility, he sometimes needs to enter data, sometimes to count, or perhaps to look up something in the stored data? For these things, we need to use switches/cases and know how to format the output nicely. This is useful for "drawing" tables in a shell.
From: Linux China
Original: http://linoxide.com/linux-shell-script/guide-start-learning-shell-scripting-scratch/
Author: Petras Liumparas
Translation: Lctt https://linux.cn/article-5591-1.html
Translator: Golinux