Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the basic problems of bash shell programming in Linux system

2025-04-08 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article will share with you what are the basic questions about bash shell programming in Linux systems. The editor thinks it is very practical, so share it with you as a reference and follow the editor to have a look.

Question 1: why is it called shell?

Before we introduce what shell is, let's re-examine the relationship between users and computers. We know that the operation of a computer cannot be separated from the hardware, but the user cannot drive the hardware directly. The driver of the hardware can only be controlled by a software called "Operating System". In fact, the linux we talk about every day is strictly an operating system, which we call the "kernel". However, from the user's point of view, there is no way for users to operate kernel directly, but to communicate with kernel through kernel's "shell" program, also known as shell.

This is precisely the image naming relationship between kernel and shell.

From a technical point of view, shell is an interactive interface (interface) between the user and the system, which mainly allows the user to use the system through the command line (command line) to complete the work. Therefore, the simplest definition of shell is-Command interpreter (Command Interpreter):

Translate the user's commands to the core processing

At the same time, the core processing results are translated to the user.

Every time we finish logging in to the system (login), we get an interactive mode shell, also known as login shell or primary shell. From the point of view of process, the orders we give in shell are all sub-journeys generated by shell. For the time being, we can call this phenomenon fork. If you are executing a script (shell script), the commands in the script are executed by a child shell (sub shell) in another non-interactive mode. That is, the primary shell generates the sub shell stroke, and the sub shell generates the stroke of all the commands in the script. With regard to the itinerary, we will have a chance to add it later. )

Here, we must know that kernel and shell are two different sets of software, and both can be replaced:

Different operating systems use different kernel

On the same kernel, different shell can also be used.

In the default system of linux, several different shell can be found and are usually listed in the following file:

/ etc/shells

Different shell have different functions, and they are different from each other, or "more or less the same". Common shell is divided into two main streams:

Sh:

Burne shell (sh)

Burne again shell (bash)

Csh:

C shell (csh)

Tc shell (tcsh)

Korn shell (ksh)

The default shell for most Linux systems is bash for two reasons:

Free software

Powerful function

Bash is one of the most successful products of gnu project. Since its launch, it has been loved by the majority of Unix users, and has gradually become the system standard of many organizations.

Question 2: what is the relationship between shell prompt (PS1) and Carriage Return (CR)?

When you successfully log into a text interface, in most cases, you will see a constantly flashing square or bottom line on the screen (depending on the version), which we call a cursor. The function of the cursor is to tell you where the next key you enter from the keyboard is inserted, and each key you enter the cursor moves a grid to the right, and if you enter too much in a row, it will be automatically entered on the next line. If you have just finished logging in and have not typed any keys before you see the left part of the same line where the cursor is located, we call it a prompt. The format of prompt symbols may vary from system to system. On Linux, you only need to pay attention to the visible prompt that is closest to the cursor, usually one of the following:

$: for general user accounts

#: for root (administrator) account

In fact, the meaning of shell prompt is simple: shell tells the user that you can now enter the command line. We can say that the user can only type the command line when he gets the shell prompt, and cursor instructs the keyboard to enter the position on the command line. Each time the user enters a key, the cursor moves back one grid until the command line reads the CR (Carriage Return, generated by the Enter key) character. The meaning of CR is also simple: the user tells shell: dude, you can follow my orders.

Strictly speaking: the so-called command line is the text entered between the shell prompt and CR characters. (think about it: why do we insist on using CR characters instead of enter? The answer will be revealed in later study. Different commands may have different command line formats. In general, a standard command line format is as follows:

Command-name options argument

From the perspective of technical details, shell will disassemble the text entered by command line into "word" according to IFS (Internal Field Seperator). Then the special characters (meta) are processed first, and then the whole line of command line is reorganized. Note: please be sure to understand the meaning of the above two sentences. We will often come back here to think about it in our future study. )

IFS is a field separator used by shell presets and can be composed of one or more of the following keys:

Blank key (White Space)

Table key (Tab)

Enter key (Enter)

The name of the command acceptable to the system (command-name) can be obtained from:

Specify the external command specified by the path

Command alias (alias)

Custom function (function)

Shell built-in commands (built-in)

External commands under $PATH

Every command line must contain a command name, which is indispensable.

Q 3: other people echo, you also echo, is to ask how much echo knows?

Following the command line introduced in the previous chapter, here we use the command echo to further explain. Review-the standard command line consists of three parts:

Command_name option argument

Echo is a very simple, straightforward Linux command: send argument to standard output (STDOUT), usually on a display (monitor). (note: we will explain stdout later.) For better understanding, let's run the echo command first:

$echo

You will find that there is only one blank line, and then go back to shell prompt. This is because echo will send out a newline symbol (new-line charactor) by default after displaying the argument. But the above command does not have any argument, so there is only a newline symbol left.

If you want to cancel this newline symbol, you can use echo's-n option:

$echo-n

Let's go back to the concept of command line and discuss the echo command in the example above: command line only has command_name (echo) and option (- n), but does not have any argument.

To see echo's argument, it's not easy! next, you can try the following input:

$echo first linefirst line$ echo-n first linefirst line$

In the last two echo commands, you will find that the part of argument is displayed on your screen, while the newline symbol varies depending on the presence or absence of-n option.

Obviously, the second echo is canceled because of the newline symbol, and the next shell prompt is on the same line as the output. ^ _ ^

In fact, in addition to-n options, the common options for echo are:

-e: enable the conversion of backslash control characters (see table below)

-E: turn off the conversion of backslash control characters (by default)

-n: cancel the newline symbol at the end of the line (agree with the\ c character under the-e option)

The backslash control characters supported by the echo command are shown in the following table:

\ a:ALERT / BELL (ringtone sent from the system speaker)

\ b:BACKSPACE, that is, the left backspace key

\ c: cancel the newline symbol at the end of the line

\ E:ESCAPE, jump key

\ f:FORMFEED, paging character

\ n:NEWLINE, newline character

\ r:RETURN, enter key

\ t:TAB, tabular jump key

\ v:VERTICAL TAB, vertical table jump key

\ n: ASCII octal code (begins with x as hexadecimal)

\: the backslash itself

(the table data are from Learning the Bash Shell,2nd Ed of O'Reilly Publishing House.)

Perhaps, we can learn about the options and control characters of echo through an example:

Example 1:

$echo-e "a\ tb\ tc\ nd\ te\ tf" a b cd e f

The above example uses\ t to distinguish between abc and def, and\ nswaps def to the next line.

Example 2:

$echo-e "\ 141\ 011\ 142\ 011\ 143\ 012\ 144\ 011\ 145\ 011\ 146" a b cd e f

The result is the same as in example 1, only using ASCII octal coding.

Example 3:

$echo-e "\ x61\ x09\ x62\ x09\ x63\ x0a\ x64\ x09\ x65\ x09\ x66" a b cd e f

Similar to example 2, except that the ASCII hexadecimal code is used this time.

Example 4:

$echo-ne "a\ tb\ tc\ nd\ te\ bf\ a" a b cd f $

Because the e letter is followed by a backspace key (\ b), there is no e in the output. At the end, I heard a bell direction, it was a masterpiece!

Because the-n option is also used, shell prompt comes immediately after the second line. If you don't use-n, then you add\ c after\ a, and it has the same effect.

In fact, the echo command is one of the most commonly used commands in future shell operations and shell script designs.

For example, check the value of a variable with echo:

$echo $AB$ echo $? 0

(note: we will not explain the concept of variables until the next two chapters. )

Well, for more information about the format of command line and the options for the echo command, please practice and use it by yourself.

Question 4: what is the difference between "(double quotation marks) and" (single quotation marks)?

Let's come back to our command line.

After studying in the first two chapters, it should be clear that when you hit the keyboard behind the shell prompt until you press Enter

The text you enter is command line, and then shell will execute the commands you give it in the way of itinerary. However, do you know that every word you enter in command line can be classified into categories for shell?

To put it simply (I dare not say that this is an accurate decision, Note 1), each charactor of command line is divided into the following two categories:

Literal: that is, plain text, no special function for shell.

Meta: for shell, a special reserved character with a specific function. (note 1: for instructions on the order of bash shell in dealing with command line, please refer to Learning the Bash Shell,2nd Edition of O'Reilly Press, page 177-180, especially the flowchart Figure 7-1 on page 178. )

There is nothing to talk about about Literal. All the words such as abcd and 123456 are literal. (easy? ). But meta often puzzles us. ... (confused? In fact, in the first two chapters, we have come across two meta that we encounter almost every time in command line:

IFS: consisting of or one of the three (we often use space).

CR: generated by.

IFS is used to disassemble every word (word) of command line, because shell command line is processed by word. CR is used to end command line, which is why we run when we hit the command. In addition to IFS and CR, common meta includes:

=: set the variable.

$: replace variables or operations (please don't be confused with shell prompt).

>: redirect to stdout.

< : 重导向stdin。 | : 命令管线。 & : 重导向file descriptor,或将命令置于背境执行。 () : 将其内的命令置于nested subshell执行,或用于运算或命令替换。 {} : 将其内的命令置于non-named function中执行,或用在变量替换的界定范围。 ; : 在前一个命令结束时,而忽略其返回值,继续执行下一个命令。 && : 在前一个命令结束时,若返回值为true,继续执行下一个命令。 || : 在前一个命令结束时,若返回值为false,继续执行下一个命令。 !: 执行history列表中的命令 ... 假如我们需要在command line中将这些保留字元的功能关闭的话,就需要quoting处理了。在bash中,常用的quoting有如下三种方法: hard quote:' '(单引号),凡在hard quote中的所有meta均被关闭。 soft quote:""(双引号),在soft quote中大部份meta都会被关闭,但某些则保留(如$)。(注二:在soft quote中被豁免的具体meta清单,我不完全知道,有待大家补充,或透过实作来发现及理解。) escape : \(反斜线),只有紧接在escape(跳脱字符)之后的单一meta才被关闭。 下面的例子将有助于我们对quoting的了解: $ A=B C #空白键未被关掉,作为IFS处理。$ C: command not found.$ echo $A$ A="B C" #空白键已被关掉,仅作为空白键处理。$ echo $AB C 在第一次设定A变量时,由于空白键没被关闭,command line将被解读为:A=B然后碰到,再执行C命令 在第二次设定A变量时,由于空白键被置于soft quote中,因此被关闭,不再作为IFS:A=BC 事实上,空白键无论在soft quote还是在hard quote中,均会被关闭。Enter键亦然: $ A='B>

C >'$echo "$A" BC

In the above example, because it is placed in hard quote, it is no longer treated as a CR character. Here is simply a line break symbol (new-line), because command line does not get the CR character, so enter the second shell prompt (PS2, represented by the > symbol), command line will not end, until the third line, what we enter is not in the hard quote, so it is not closed, at this time, the command line encounters the CR character, so it ends and gives it to shell to deal with.

If the above example is placed in soft quote, CR will also be closed:

$A = "B > C >" $echo $AB C

However, because the variable at echo $An is not in soft quote, when the variable replacement is completed and the command line is reorganized, it will be interpreted as IFS rather than as New Line characters.

Similarly, you can turn off CR characters with escape:

$echo B\ > C\ > $Avatar $ABC

In the above example, the first and second are both turned off by escape characters, so they are not treated as CR

But the third one ends the command line as a CR because it is not skipped.

However, due to the particularity of the key itself in shell meta, after\ jumping, only its CR function is cancelled, but its IFS function is not retained.

You may find that the characters generated by a key alone may be as follows: CR, IFS, NL (New Line), FF (Form Feed), NULL. As for when to explain why the characters, I did not dig deep, or left to the readers to slowly fumble. ^ _ ^

As for the difference between soft quote and hard quote, it is mainly explained by $as to whether some meta is closed or not:

$echo B\ C $echo "$A" B C $Aguilera

In the first echo command line, $is placed in soft quote and will not be closed, so it continues to process variable substitution, so echo outputs the variable value of A to the screen, and gets the result of B / C. In the second echo command line, $is placed in hard quote and closed, so $is just a $symbol and is not used for variable replacement, so the result is the $symbol followed by the letter A: $A.

# exercise and thinking: why are the following results different? The outermost is the single quotation mark "$A" $echo "$A'" # the outermost is the double quotation mark 'BC'

Hint: both single and double quotation marks are turned off in quoting. )

In the shell version of CU, I found that there are a lot of beginner's problems related to what quoting understands. For example, if we call some previously set variables in the command parameters of awk or sed, we often ask why not. To solve these problems, the key point is:

Distinguish between shell meta and command meta

The meta we mentioned earlier has a special purpose in command line. For example, {} executes a series of command line in an unnamed function (which can be simply regarded as command block), but awk needs {} to distinguish the BEGIN,MAIN,END of the awk. If you enter this in command line:

$awk {print $0} 1.txt

Since {} is not turned off in shell, shell treats {print $0} as command block, but does not have a; symbol as a command partition, resulting in a syntax error result of awk. To solve this, you can use hard quote:

$awk'{print $0} '1.txt

The above hard quote should be easy to understand, that is, turn off the original {, $(Note 3),} shell meta to avoid being processed in the shell and become the complete command meta in the awk parameter. (note 3: the $0 of which is the built-in field number of awk, not the variable of awk, and the variable of awk itself does not need to use $. )

If you understand the functions of hard quote, it is not difficult to understand soft quote and escape:

Awk "{print\ $0}" 1.txtawk\ {print\\ $0\} 1.txt

However, what if you want to change the value of $0 of awk to be read in from another shell variable? For example, if you already have a variable $A whose value is 0, how to solve the problem of $$An of awk in command line? You can directly negate hard quoe's plan:

$awk'{print $$A} '1.txt

That's because the $of $A cannot replace variables in hard quote. Smart readers (such as you! After studying in this chapter, I think we can explain why we can use the following operations:

A=0awk "{print\ $A}" 1.txtawk\ {print\\ $A\} 1.txtawk'{print $'$A'} '1.txtawk' {print $'"$A"} '1.txt # Note: "$A" package is in soft quote

Maybe you can come up with more plans. . ^ _ ^

Question 5: var=value? What is the difference between before and after export?

Let's put aside command line for a while this time and take a look at the bash variable (variable).

The so-called variable is to use a specific "name" to access a variable "value" (value).

Set (set)

In bash, you can use "=" to set or redefine the contents of a variable:

Name=value

The following rules must be followed when setting variables:

The left and right sides of the equal sign cannot use the distinguishing symbol (IFS), and the reserved character (meta charactor) of shell should be avoided.

Variable names cannot use the $symbol.

The first letter of a variable name cannot be a number (number).

The length of the variable name cannot exceed 256 letters.

The case of the variable name and variable value is different (case sensitive).

Here are some common errors in variable setting:

A = B # cannot have IFS

1A=B # cannot start with a number

$AbeliB # the name cannot have $

This is different from axib (this is not an error, remind windows users to pay special attention to it)

The following are acceptable settings:

A = "B" # IFS is turned off (please refer to the previous quoting section) A1roomB # does not start with a number and can be used in variable values This_Is_A_Long_Name=b # concatenates longer names or values with different capitalization.

Variable substitution (substitution)

One of the factors that makes Shell so powerful is that it can substitution variables on the command line.

On the command line, the user can use the $symbol plus the variable name (except when defining the variable name with the = sign)

Replace the value of the variable, and then rebuild the command line.

For example:

$A-$B $C

(note: the first $of the above command line is shell prompt and is not on the command line. )

It must be emphasized that the variable substitution we mentioned only occurs on command line. (yes, let's go back to command line!) by carefully analyzing the last line of command line, it is not difficult to find that before being executed (before typing the CR character)

The $symbol replaces each variable (replaces the value of the variable and then reorganizes the command line), resulting in the following command line:

Ls-la / tmp

Do you still remember the two sentences I asked you to "be sure to understand" in the second chapter? If you forget, I'll post it again here:

From the perspective of technical details, shell will disassemble the text entered by command line into "word" according to IFS (Internal Field Seperator). Then the special characters (meta) are processed first, and then the whole line of command line is reorganized. The $here is one of the most classic meta in command line, which is used for variable replacement!

In daily shell operations, we often use the echo command to view the values of specific variables, such as:

$echo $A-$B $C

We have learned that the echo command simply sends its argument to "standard output" (STDOUT, usually our screen). So the above command will get the following result on the screen:

Ls-la / tmp

This is because when the echo command is executed, it replaces $A (ls), $B (la), and $C (/ tmp) first.

Taking advantage of shell's ability to replace variables, we have more flexibility in setting variables:

A=BB=$A

In this way, the variable value of B can inherit the variable value of A variable "at that time". However, do not use "mathematical logic" to apply the setting of variables, such as:

A=BB=C

This does not make the variable value of A become C. Another example is:

A=BB=$AA=C

Nor will it change the value of B to C. Above are simply two variables with different names: an and B, whose values are B and C, respectively.

If the variable is repeatedly defined, the old value will be replaced by the new value. (isn't this the "variable quantity"? ^ _ ^)

When we set variables, keep this in mind: store a value with a name, that's all. In addition, we can also use the variable substitution capability of the command line to "append" the variable value:

A=B:C:DA=$A:E

In this way, we set the value of A to "B:C:D" in the first line, and then expand the value to "B:C:D:E" in the second line.

In the above expansion example, we use the partition symbol (:) to achieve the expansion purpose. If there is no separation symbol, it is problematic as follows:

A=BCDA=$AE

Because the second time is to inherit the value of A to the exchange result of $AE, rather than $A plus E! To solve this problem, we can handle it with a more rigorous replacement:

BCDA ${A} E

In the above example, we use {} to clearly define the scope of the variable name, so that we can extend the variable value of A from BCD to BCDE. (hint: more variable processing capabilities can actually be achieved with ${name}, which belong to more advanced variable processing.

Export

Strictly speaking, the variables we define in the current shell are "local variables" (local variable) and can become environment variables (environment variable) only after being processed by the "output" of the export command:

$export B $ABA

Or:

$export axib

After export output processing, variable A can become an environment variable for subsequent commands to use. When using export, don't forget shell's substitution handling of variables on the command line, such as:

$export B $Broad C $A

The above command does not output An as an environment variable, but B as output, because on this command line, $An is first replaced with B, and then "plugged back" as an argument to export. To understand this export, you actually need to understand it thoroughly from the perspective of process. Please note that I will explain the concept of process in the next chapter.

Cancel variable

To cancel a variable, use the unset command in bash to handle it:

Unset A

Like export, the unset command line also does variable substitution (which is actually one of the functions of shell), so:

$unset B $Broad C $A

In fact, the canceled variable is B, not A.

In addition, once a variable is cancelled by unset, the result is that the entire variable is removed, not just its value.

The following two lines are actually very different:

$$unset A

The first line simply sets variable A to "null value", but the second line makes variable A no longer exist.

Although from the eye point of view, the two variable states are the same in the following command result:

$$echo $A $unset A $echo $A

Students must be able to recognize the essential difference between null value and unset, which is very strict in some advanced variable handling. For example:

$str= # set to null$ var=$ {str=expr} # define var$ echo $var$ echo $str$ unset str # Undefine $var=$ {str=expr} # define var$ echo $varexpr$ echo $strexpr

Smart readers (yes,you! ), if you think about it, it should not be difficult to see why the same var=$ {str=expr} is different under null and unset. If you can't see it, it may be one of the following reasons:

You're so stupid.

Do not understand the advanced processing of var=$ {str=expr}

I haven't had time to digest and absorb this explanation.

I don't speak well.

I don't know, which one do you choose? . ^ _ ^ mm-hmm. All right, I'll just unscramble var=$ {str=expr}:

First of all, var=$str is understandable to everyone. The next direction of thinking is, which of the following is the amount of $str?

Unset

Null

Not null

If it is unset, then the result of var=$ {str=expr} will be:

Var=exprstr=expr

If it is null, the result of var=$ {str=expr} is:

Var=str=

If it is not null (xyz for example), the result of var=$ {str=expr} is:

Var=xyzstr=xyz

Question 6: what is the difference between exec and source?

This time, let's start with an example post in the CU Shell version: (the original connection has been invalidated after the revision of the forum). The original text of the question is as follows:

Cd / etc/aa/bb/cc can be executed, but shell does not execute when this command is written to shell! What is the reason? (meaning: did not move to the / etc/aa/bb/cc directory after running the script)

What was my answer at that time? don't dig into it for a moment, let's take a look at the concept of process. First of all, any program we execute is a child process generated by the parent parent process, which will be returned to the parent at the end. This phenomenon is called fork in Linux system. Why should the trip be fork? Well, it might be easier to understand by drawing a picture. ^ _ ^)

When the child itinerary is generated, it will get some resource allocation from the parent itinerary and (more importantly) inherit the environment of the parent itinerary! let's go back to the "environment variables" mentioned in the previous chapter:

The so-called environment variables are actually those variables that will be passed to the substroke. To put it simply, "heredity" is the decisive indicator to distinguish local variables from environmental variables.

However, from a genetic point of view, it is not difficult to find another important feature of environmental variables:

Environment variables can only be inherited one-way from parent to child. In other words: how the environment changes in the child trip will not affect the environment of the parent trip.

Next, let's take a look at the concept of command scripts (shell script). The so-called shell script is very simple, which is to write the multiple lines of command line that you usually enter after shell prompt into a file in sequence. In addition, some techniques such as conditional judgment, interactive interface, parameter application, function call and so on can make script execute more "intelligently", but if we put aside these skills, we can really simply regard script as just executing a pre-written command line in turn.

Combining the above two concepts (process + script), it should not be difficult to understand the meaning of the following sentence:

Normally, when we execute a shell script, we actually generate a substroke of sub-shell first, and then sub-shell generates a substroke of the command line.

However, let's go back to the example mentioned at the beginning of this chapter and think again: cd / etc/aa/bb/cc can be executed, but shell does not execute when this command is written to shell! What is the reason? My answer at the time was this:

Because, generally speaking, the shell script we run is executed with subshell. From process's point of view, it is parent process that generates a child process to execute, and when the child ends, it returns parent, but the environment of the parent will not change due to the change of child. There are many so-called environment elements, such as effective id,variable,workding dir and so on. The workding dir ($PWD) is exactly the question of the landlord: when you use subshell to run script, the $PWD of subshell will change because of cd, but when primary shell is returned, $PWD will not change.

It's good to understand the cause of the problem and how it works, but? I'm afraid we are more interested in how to solve the problem! isn't it? ^ _ ^

Well, next, let's take a look at the source command again. When you have the concept of fork, it is not difficult to understand source:

The so-called source is to let the script execute within the current shell, rather than generating a sub-shell to execute.

Since all the execution results are completed in the current shell, if the environment of the script changes, of course, it will change the current environment! therefore, as long as we change the previously entered script command line into the parameters of the source command, we can easily solve the problem mentioned in the previous example.

For example, this is how we used to execute script:

. / my.script

Now you can change it to this:

Source. / my.script

Or:

.. / my.script

Speaking of which, I think you are interested in taking a look at the many configuration files under / etc. It should not be difficult to understand how they can be read and inherited by other script after they are defined. If so, if you have the opportunity to write your own script in the future, it should not be difficult to specify a configuration file for different script to "share" together. ^ _ ^

Okay, if you can understand the difference between fork and source, then take on another challenge:

So what's the difference between exec and source/fork?

Oh. It may be complicated to understand exec, especially when it comes to File Descriptor. But to put it simply:

Exec also asked script to execute on the same itinerary, but the original itinerary was terminated. In short: whether the original trip will be terminated is the biggest difference between exec and source/fork.

Well, just from the theory to understand, may not be so easy to digest, not as impressive as the hands-on "implementation + thinking" oh. Let's write two simple script, named 1.sh and 2.sh:

1.sh

#! / bin/bashA=Becho "PID for 1.sh before exec/source/fork:$$" export Aecho "1.sh:\ $An is $A" case $1 in exec) echo "using exec …" Exec. / 2.sh * *; source) echo "using source …" .. / 2.shten; *) echo "using fork by default …" . / 2. "PID for 1.sh after exec/source/fork:$$" echo "1.sh:\ $An is $A"

2.sh

#! / bin/bashecho "PID for 2.sh: $$" echo "2.sh get\ $Atropa from 1.sh" A=Cexport Aecho "2.sh:\ $An is $A"

Then, run the following parameters to observe the results:

$. / 1.sh fork$. / 1.sh source$. / 1.sh exec

Well, don't forget to carefully compare the differences in the output and the reasons behind it. If you have any questions, please feel free to discuss ~ ~ happy scripting! ^ _ ^

Q 7: what is the difference between () and {}?

Well, take it easy this time, don't talk too much... ^ _ ^

First of all, why do you use () or {}? Many times, in shell operations, we need to execute multiple commands at a time under certain conditions, that is, either not or all of them, rather than judging whether to execute the next command sequentially each time. Or, you need to get an exemption from some command execution priority, such as arithmetic 2 * (3-4).

At this point, we can introduce the concept of "command group" (command group): centralized processing of multiple commands. In shell command line, most people may not care much about the difference between the two pairs of symbols () and {}. Although both can group multiple commands, they are very different in terms of technical details:

() put command group into sub-shell to execute, also known as nested sub-shell.

{} is done in the same shell, also known as non-named command group.

If you remember the concepts of fork and source in the previous chapter, it is not difficult to understand the difference between the two.

If variables and other environment changes are involved in command group, we can use () or {} according to different requirements.

Generally speaking, if the changes are temporary and do not want to affect the original or future settings, then we nested sub-shell, and vice versa, we use non-named command group.

Yes, from the point of view of command line alone, the difference between () and {} is over, isn't it easy enough?

However, if these two meta are used in other command meta or domains (such as Regular Expression), there are still many differences. It's just that I'm not going to explain it any more. I'll leave it to the reader to discover it slowly. I just want to add one concept here, that is, function.

The so-called function is to name a command group with a name, and then call that name to execute the command group. Inferred from non-named command group, you can probably guess that I am going to say {}, right? (yes! You are so smart! ^ _ ^)

In bash, function is defined in two ways:

Method 1:

Function function_name {command1command2command3 … }

Method 2:

Fuction_name () {command1command2command3 … }

It doesn't matter which way you use, but mode 2 may fail if the intended name conflicts with an existing command or alias (Alias). But the second way can at least avoid typing the English letter function, for lazy people (such as me), why not do it? ^ _ ^

Function can also be called a "function" to some extent, but please don't be confused with the function (library) used in traditional programming, after all, the two are very different. The only thing we have in common is that we can all call them with defined names at any time.

If we need to execute certain commands repeatedly in a shell operation, the first thing that comes to mind may be to write the command as a command script (shell script). However, we can also write it as function, and then type function_name in the command line and use it as a script.

However, if you define the function in shell, in addition to the available unset function_name cancellation, once you exit the shell,function will also be cancelled.

However, using function in script has many benefits. In addition to improving the overall script performance (because it has been loaded), you can also save a lot of repetitive code.

To put it simply, if you can write multiple commands as script for calling, you can think of function as a script in script. ^ _ ^

Moreover, with the source commands introduced in the previous chapter, we can define many useful function ourselves, write them centrally in specific files, and then load them with source in other script and execute them repeatedly.

If you are a user of RedHat Linux, you may have guessed what the / etc/rc.d/init.d/functions file is for.

Okay, I said it would be easier, so let's stop here for the time being. I wish you all a happy study! ^ _ ^

Q 8: what is the difference between $() and $() and ${}?

We introduced the difference between () and {} in the previous chapter, this time let's expand to see more changes: what are $() and ${}?

In bash shell, both $() and ``(backquotes) are used for command substitution (command substitution). The so-called command substitution is similar to the variable substitution we learned in Chapter 5, which is used to reorganize the command line:

Complete the command line in quotation marks, replace the results, and then reorganize the command line.

For example:

$echo the last sunday is $(date-d "last sunday" +% Y-%m-%d)

In this way, it is convenient to get the date of last Sunday. ^ _ ^

In operation, it doesn't matter to use $() or ``, but I personally prefer to use $() for the following reasons:

It's easy to get confused with'(single quotation marks), especially for beginners. Sometimes in some strange glyph display, the two symbols are identical (vertical two points). Of course, experienced friends can tell the difference between the two at a glance. However, if we can better avoid chaos, why not? ^ _ ^

In a multi-level composite replacement, additional jump (\ `) processing is required, while $() is more intuitive. For example, this is wrong:

Command1 `command2 `command3``

The original intention is to replace the command3 in command2 `command3` to command2 for processing, and then pass the result to command1 `command2. To deal with it. However, the real result is divided into `command2` and ``paragraphs on the command line. The correct input should be as follows:

Command1 `command2\ `command3\ ``

Otherwise, it will be no problem to change it to $():

Command1 $(command2 $(command3))

As long as you like, it's okay to replace as many layers as you like.

However, $() is not without an end.

First of all, `can basically be used in all unix shell, if written as shell script, its portability is relatively high. And $() does not see every kind of shell can be used, I can only tell you, if you use bash3`, there will be no problem. ^ _ ^

Next, let's look at ${} again. It is actually used for variable substitution. In general, $var is no different from ${var}. But using ${} will define the scope of the variable name more precisely, for example:

$Apostb $echo $AB

It was originally intended to replace the result of $An and then add a B letter to it, but on the command line, the real result is only the value of the variable name AB. If you use ${}, there will be no problem:

$echo ${A} BBB

However, if you only see that ${} can only be used to define variable names, you are underestimating bash! for completeness, I will use some examples to illustrate some of the special features of ${}.

Suppose we define a variable as follows:

File=/dir1/dir2/dir3/my.file.txt

We can replace it with ${} to get different values:

${file#*/} # remove the first / and its left string: dir1/dir2/dir3/my.file.txt$ {file##*/} # remove the last / and its left string: my.file.txt$ {file#*.} # remove the first one. And the string to the left: file.txt$ {file##*.} # remove the last one. And its left string: txt$ {file%/*} # remove the last / and its right string: / dir1/dir2/dir3 ${file%%/*} # remove the first / and its right string: (null) ${file%.*} # remove the last one. And the string to the right: / dir1/dir2/dir3/my.file$ {file%%.*} # remove the first one. And the string to the right: / dir1/dir2/dir3/my

The method of memory is:

# is to remove the left (on the keyboard # to the left of $)

% is removed from the right (on the keyboard% to the right of $)

A single symbol is the minimum match, and the two symbols are the maximum match.

${file:0:5} # extract the leftmost 5 bytes: / dir1 ${file:5:5} # extract the 5 consecutive bytes to the right of the 5th byte: / dir2

We can also replace the string in the value of the variable:

${file/dir/path} # convert the first dir to path:/path2/dir2/dir3/my.file.txt$ {file//dir/path} # convert all dir to path:/path2/path3/path4/my.file.txt

Using ${}, you can also assign values for different variable states (not set, null, non-null).

${file-my.file.txt} # if $file is not set, use my.file.txt as the return value. (null and non-null values are not processed) ${file:-my.file.txt} # if $file is not set or is a null value, use my.file.txt as the return value. (not processed when non-null) ${file+my.file.txt} # if $file is set to null or non-null, my.file.txt is used as the return value. (no processing if not set) ${file:+my.file.txt} # if $file is a non-null value, use my.file.txt as the return value. (no processing when not set and null) ${file=my.file.txt} # if $file is not set, use my.file.txt as the return value and assign $file to my.file.txt. (null and non-null values are not processed) ${file:=my.file.txt} # if $file is not set or is null, use my.file.txt as the return value and assign $file to my.file.txt. (not processed when non-null) ${file?my.file.txt} # if $file is not set, output my.file.txt to STDERR. (null and non-null values are not processed) ${file:?my.file.txt} # if $file is not set or is null, the my.file.txt is output to STDERR. (not processed when non-null)

Tips:

The above understanding is that you must distinguish between unset and null and non-null. Generally speaking, it is related to null, if it does not take:, null will not be affected, if it does, even null will be affected.

Also, ${# var} can calculate the length of the variable value:

${# file} # gets 27 because / dir1/dir2/dir3/my.file.txt is exactly 27 bytes.

Next, I would like to introduce the array processing method of bash.

Generally speaking, a variable such as A = "a b c def" simply replaces $A with a single string, but instead of A = (a b c def), it defines $An as an array. For the array substitution method of bash, please refer to the following method:

${A [@]} or ${A [*]} # can get a b c def (all groups)

${A [0]} # gives a (the first group number), and ${A [1]} is the second group number.

${# A [@]} or ${# A [*]} # can get 4 (total number of groups)

${# A [0]} # gets 1 (the length of the first group number (a)), and ${# A [3]} gets 3 (the length of the fourth group number (def))

A [3] = xyz # redefines the fourth group number as xyz.

Being able to make good use of $() and ${} of bash can greatly improve and simplify the processing power of shell on variables.

All right, finally, let's introduce the purpose of $(): it is used for integer operation. In bash, the integer operation symbols of $() are roughly these: +-* /: "add, subtract, multiply, divide", respectively. %: remainder operation. & | ^! : "AND, OR, XOR, NOT" operations, respectively.

Example:

$aq5 / a+b*c / c) 6$ echo $(aqb)% c)) 1

The variable name in $(()) can be replaced with a $symbol before it, or it may not be used, such as:

$(($a + $b * $c)) can also get a result of 19

In addition, $(()) can also be calculated for different digits (such as binary, octal, hexadecimal), but the output results are all decimal:

Echo $((1602a)) # result is 42 (hexadecimal to decimal)

Let's take a practical example: if the current umask is 022, the permission to create a new file is:

$umask 022$ echo "obase=8;$ ((8x 666 & (8x 777 ^ 8m $(umask)" | bc644

In fact, the value of a variable can be redefined simply with (), or as testing:

((averse +)) # can redefine $an as 6

(amury -) is axi4.

((a < b)) gets the return value of 0 (true).

The common test symbols used for () are as follows:

: greater than

=: greater than or equal to

= =: equal to

! =: not equal to

However, when using () for an integer test, please don't get confused with the integer test of []. (I will introduce you to more tests in Chapter 10.) how about it? Isn't it funny. ^ _ okay, say so much for the time being.

The above introduction does not list every available status in detail. For more information, please refer to the manual document.

Q 9: what is the difference between $@ and $*?

Before you say $@ and $*, you need to start with shell script's positional parameter.

We already know how variables (variable) are defined and replaced, and there is no need to say any more about this. However, we also need to know that there are some variables that are shell-defined and whose names we can't change at will, including positional parameter. In shell script, we can use $0, 1, 2, 3. Such variables extract the following parts of the command line:

Script_name parameter1 parameter2 parameter3...

We can easily guess that $0 represents the shell script name (path) itself, and $1 is the first parameter after it, and so on. It is important to note the role of IFS, that is, if IFS is processed by quoting, then positional parameter will also change. Such as the following example:

My.sh p1 "p2 p3" p4

Because the blank bond between p2 and p3 is turned off by soft quote, $2 in my.sh is "p2 p3" and $3 is p4.

Remember when we mentioned fucntion in the last two chapters, didn't I say it was script in script? Yes, function can also read its own (different from script) postitional parameter, with the exception of $0. For example: suppose there is a fucntion in my.sh called my_fun. If you run my_fun fp1 fp2 fp3 in script, then $0 in function is my.sh, and $1 is fp1 instead of p1. Why don't you write a simple my.sh script:

#! / bin/bashmy_fun () {echo'$0 inside function is'$0echo'$1 inside function is'$1echo'$2 inside function is'$2} echo'$0 outside function is'$0echo'$1 outside function is'$1echo'$2 outside function is'$2my_fun fp1 "fp2 fp3"

Then run script in command line and you will know:

Chmod + x my.sh./my.sh p1 "p2 p3" $0 outside function is. / my.sh$1 outside function is p1 $2 outside function is p2 p3 $0 inside function is. / my.sh$1 inside function is fp1 $2 inside function is fp2 fp3

However, when using positional parameter, we should pay attention to some traps:

Instead of replacing the 10th parameter, $10 replaces the first parameter ($1) and then adds a 0 after it!

That is, the $10 in a command line,my.sh like my.sh one two three four five six seven eigth nine ten is not ten but one0. Look out! look out! There are two ways to catch ten:

The first way is to use the ${} we introduced in the previous chapter, that is, ${10}.

The second method is shift. In popular terms, the so-called shift is to cancel the leftmost parameter in the positional parameter ($0 is not affected). The default value is 1, that is, either shift or shift 1 cancels $1, while the original $2 becomes $1 and $3 becomes $2. If shift 3 cancels the first three parameters, that is, the original $4 will become $1.

So, dear reader, how many parameters do you say you have to shift before you can get ${10} with $1? ^ _ ^

Okay, when we have a basic concept of positional parameter, let's look at other relevant variables.

The first is $#: it can capture the number of positional parameter. Take the previous my.sh p1 "p2 p3" as an example: because the IFS between p2 and p3 is in soft quote, $# gets a value of 2. But if p2 and p3 are not placed in quoting, then $# will get the value of 3. The same truth is the same in function.

Therefore, we often test whether script has read parameters in shell script with the following methods:

[$# = 0]

If it is 0, it means that script has no parameters, otherwise it has parameters.

Then there are $@ and $*: to be exact, there is only a difference in soft quote, otherwise, both represent "all parameters" (except $0). For example: if you run my.sh p1 "p2 p3" p4 on command line, whether it is $@ or $*, you will get p1 p2 p3 p4. However, if you put it in soft quote:

For "$@", you can get three different words "p1", "p2 p3" and "p4" (word).

"$*" gives a single string of words "p1, p2, p3, p4".

We can modify the previous my.sh to read as follows:

#! / bin/bashmy_fun () {echo "$#"} echo 'the number of parameter in "$@" is' $(my_fun "$@") echo 'the number of parameter in "$*" is' $(my_fun "$*")

Then execute. / my.sh p1 "p2 p3" p4 to know the difference between $@ and $*. ^ _ ^

The number of parameter in "$@" is 3the number of parameter in "$*" is 1

Q 10: & & and | | what's the difference?

It's not easy to get into the double-digit chapter... It was hard all the way, wasn't it? Are you happy, too? Before answering the title of this chapter, let's understand a concept: return value!

Every command or function we run under shell returns a value called return value at the end of the parent journey. $? is available in shell command line. This variable gets the most "new" return value, which is the value returned from the trip that just ended. The value of Return Value (RV) is between 0 and 255. it is up to the author of the program (or script) to decide:

If you are in script, use exit RV to specify its value, and if not, it ends with the RV of the last command.

If you are in function, use return RV instead of exit RV.

The function of Return Value is to determine the exit status (exit status) of a trip. There are only two ways:

If 0 is true (true)

If it is not 0, it is false.

Let's take an example: suppose there is a file of my.file in the current directory, but no.file does not exist:

$touch my.file$ ls my.file$ echo $? # first echo0 $ls no.filels: no.file: No such file or directory$ echo $? # second echo1 $echo $? # third echo0

The first echo in the above example is about the RV of ls my.file, and you can get a value of 0, so if the second echo is about RV of ls no.file for true;, you get a value of non-zero, so the third echo for false; is about the second echo $? RV, which is a value of 0, and therefore true

Remember: every command will be sent back to return value at the end! No matter what kind of orders you run... However, there is a command that is "specifically" used to test a condition and sends return value for true or false judgment. It is the test command!

If you are using bash, please type man test or man bash under command line to understand the use of this test. This is the most accurate document you can use as a reference, if you listen to what others say, just for reference. I will just briefly give some supplementary instructions below, and the rest will be subject to man:

First of all, the expression of test, which we call expression, has two command formats:

Test expression or: [expression]

(be sure to pay attention to the blank keys between []!)

It doesn't matter which format you use, it all has the same effect. Personally, I prefer the latter. Second, bash's test currently supports only three types of test objects:

String: strings, that is, plain text.

Integer: integer (0 or positive integer, excluding negative or decimal point).

File: file.

Beginners must understand the difference between the three, because the expression used by test is not the same.

Take the variable Apati123 as an example:

["$A" = 123] # is a string test to test whether $An is the three consecutive "characters" of 1, 2, and 3. ["A"-eq 123] # is an integer test to test whether $An equals "123". [- e "$A"] # is a test of the document to test the existence of the "file" 123.

Third, when the expression test is true, test sends back a return value of 0 (true), otherwise it sends a non-0 (false). If you add a! (exclamation point) before expression, 0 is sent only when expression is false, otherwise non-0 is sent.

At the same time, test also allows multiple composite tests:

Expression1-an expression2 # sends 0 when both exrepssion are true, otherwise it is not 0. Expression1-o expression2 # sends 0 as long as one of the exrepssion is true, and non-0 only if both are false.

For example:

[- d "$file"-a-x "$file"]

It means that test is true only if $file is a directory and has x permission.

Fourth, when using test in command line, don't forget the "reorganization" feature of the command line, that is, when you encounter meta, you will first deal with meta and then rebuild the command line. (I repeatedly emphasized this feature in chapters 2 and 4.) for example, if test encounters a variable or command replacement, and if it does not meet the expression format, you will get a syntax error. For example: for the test format [string1 = string2], there must be a string on both sides of the = sign, including an empty (null) string (available in soft quote or hard quote).

If $An is not currently defined or is decided to be an empty string, the following words will fail:

$unset A $[$A = abc] [: =: unary operator expected

This is because when the command line encounters the meta of $, it replaces the value of $A, and then reorganizes the command line, which becomes: [= abc]

In this way, there is no string to the left of the = sign, resulting in a syntax error in test! however, the following writing rule is true:

$["$A" = abc] $echo $? 1

This is because the result after the command line reorganization is: ["= abc]. Because on the left we use soft quote to get an empty string and let the test syntax pass. Readers, please pay attention to these details, because a little carelessness will lead to a change in the result of test!

If you are not experienced with test, you might as well apply the following "rule" when using test:

If you encounter variable substitution in test, it is safest to use soft quote! if you are not familiar with quoting, please review Chapter 4. ^ _ ^

Okay, about more test usage, there is an old saying: please take a look at man page! Although you have talked a lot about it, maybe you are still muttering. . So... What's the use of that return value?! That's a good question!

Let me tell you something: return value is very useful! if you want your shell to be "smart", it all depends on it:

With return value, we can make shell do different things according to different states.

At this point, let me reveal the answer to this chapter ~ ~ ^ _ ^ & & and | | are used to "build" multiple command line:

Command1 & & command2: this means that command2 can only be executed if RV is 0 (true).

Command1 | | command2: this means that command2 can only be executed if RV is non-0 (false).

Here, let's take an example:

$echo 123 $[- n "$A"] & & echo "yeships ture." yeships ture.$ unset A$ [- n "$A"] & & echo "yeships ture." $[- n "$A"] | | echo "no,it's NOT ture." no,it's NOT ture.

(note: [- n string] if the test string length is greater than 0, it is true. )

The first & & command line in the above example executes the echo command on the right because the previous test returns a RV value of 0, but it won't do it the second time, because test returns a non-zero result. In the same way, | | the echo on the right will be executed, but it is precisely because the test on the left returns non-zero.

In fact, we can use multiple & & or | | to build on the same command line:

$echo 123 $[- n "$A"] & & echo "yescrafts ture." | | echo "no,it's NOT ture." yeships ture.$ unset A$ [- n "$A"] & & echo "yesinherits ture." | | echo "no,it's NOT ture." no,it's NOT ture.

So, from this moment on, do you think our shell is "very smart"? All right, finally, let's assign an exercise for everyone to do.

The following judgment is: when $An is assigned a value, check whether it is less than 100, otherwise send too bigweights:

$$too $[- n "$A"] & & ["$A"-lt 100] | | echo 'too bigger picture too big!

If I cancel A, according to reason, I should not send words (because the first condition is not valid).

$unset A $[- n "$A"] & & ["$A"-lt 100] | | echo 'too bigger dogs too big! Thank you for reading! This is the end of this article on "what are the basic problems of bash shell programming in Linux system". I hope the above content can be of some help to you, so that you can learn more knowledge. if you think the article is good, you can share it for more people to see!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 277

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report