Bash Technique Translated 100%

oschina Delivered at 11:00 on January 3, 2019 (13 paragraphs in total, translation completed on January 16)
Reading 2859
Collection seven
top five
Loading

Bash is not the best programmer friendly tool. It needs to be careful, low-level knowledge and not allow any mistakes (you know you can't enter foo=42, right?). On the other hand, bash is ubiquitous (even on Windows 10). It is very portable and powerful. In fact, it is the most practical choice for task automation. Fortunately, following a simple set of rules can save you from many minefields.

 Tocy
Translated at 11:06 on January 4, 2019
top
zero

1. Shebang

At present, there are several shebang available, which you can use to reference the interpreter you want to execute code. Some of them are:

  • #!/ usr/bin/env bash

  • #!/ bin/bash

  • #!/ bin/sh

  • #!/ bin/sh –

We all know that shebang is just a path to the shell interpreter (absolute fire is relative to the current directory), but which one is more popular?

Long story short – for portability, you should use #/ usr/bin/env bash。 that is because POSIX There is no standardized path name, so different UNIX based systems may place bash in different locations. You can't completely assume - for example - that/bin/bash is inevitable (some BSD systems put bash executables in/usr/local/bin/bash).

 

 Tocy
Translated at 14:45, January 7, 2019
top
zero

Env Utilities can help us avoid this limitation: #/ The usr/bin/env bash will be the first interpreter found under the PATH path when the code is executed. Although this is not the perfect solution (what if the same problem also applies to/usr/bin/env? Fortunately, as far as I know, every UNIX OS places env in the same location), this is the best solution we can achieve.

However, I realized that there was an exception: for the system startup script, since/bin/sh is the standard command line interpreter of the system, use it.  

For more information, please refer to This and This article.

 Tocy
Translated at 14:55, January 7, 2019
top
zero

2. Always use quotation marks

This is the simplest and best advice you should follow to avoid many possible pitfalls. Incorrect shell references are the most common cause of headaches for bash programmers. Unfortunately, it's not as easy as being important.

There are many good articles covering this specific topic. I have nothing more to say, but I recommend it to you This as well as T This article article.

It's worth remembering that you should usually use double quotes.

 Tocy
Translated at 15:00, January 7, 2019
top
zero

3. Use of variables

$foo is a classic method for referencing variables in bash, but bash2.0 version (viewed through echo $BASH_VERSION) provides us with a new marking method - variable extension. This method is marked by using curly braces around variable identifiers, such as ${foo}. Why is this a good practice? Because it brings us some new features:

Extension of array element: ${array [42]}

Extension of parameters, such as ${filename%. *} (deleted file extension), ${foo//} (deleted spaces), ${BASH_VERSION%%. *} (obtained the large version number of bash)

The concatenation of variable names: ${dirname}/${filename}

Splice the string after the variable: ${HOME}/. bashrc

Access parameter variables (input parameters of script) through positional parameters, such as $9

Support access to substrings: ${foo: 1:5}

Indirect reference: ${! Foo} will be expanded into a value indirectly represented by the value named foo and stored in it (bar=42; foo="bar"; echo "${! Foo}" will print 42)

Case modification: ${foo ^} will change the first letter of foo to an uppercase letter, (a single comma will convert it to a lowercase letter) This dual form method (^ ^ and,,) will convert all letters.

 SVD
Translated at 00:14, January 6, 2019
top
zero

In most common cases, the use of variable extension forms makes us no better than the classical extension forms, but in order to maintain code consistency, it can be considered a good practice to use it everywhere. stay this Read more about it.

You also need to know about variables in bash. By default, all variables are global variables. This can cause problems such as shallow copying, overwriting, or ambiguous references. The local operator limits the scope of variables to prevent them from leaking into the global namespace. Remember - set the variables of all functions to local variables.

 Tocy
Translated at 15:04, January 7, 2019
top
zero

4. Observe the running directory of the script

You often interact with other files in the bash script. Therefore, you must be very careful about using relative paths. By default, the current working path is obtained from the script's parent shell environment.

 $ pwd /home/jakub $ cat test/test #!/ usr/bin/env bash echo "$(pwd)" $ ./ test/test /home/jakub

When the path of pwd and script is inconsistent, there will be some problems. At this time, we cannot simply pass/ The script name is the way to run the script, because it will not point to the file next to your script. To make it easier to apply the script to files in a specific path and avoid accidentally referencing other system files, you should consider using this convenient single line command to change the working directory of the subshell to the directory where the bash script is located:

 cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null && pwd)" || return $ pwd /home/jakub $ cat test/test #!/ usr/bin/env bash cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" || return echo "$(pwd)" $ ./ test/test /home/jakub/test

Does it look more natural?

 SVD
Translated at 13:15, January 6, 2019
top
zero

5. You really don't need ls

The method of using ls in bash scripts is almost always flawed, and I can't even remember a reason for doing so. To explain why, let's look at two common examples:

 for file in $(ls *.txt)

When any file name contains spaces, participle This for loop will be broken. More importantly, if the file name contains glob Characters (also known as wildcards, such as *,?, [,]) will be recognized as global mode and extended by the shell, but this may not be what you want. Another problem is that POSIX allows path names to contain any characters except 0 (including |,/or even newline characters). This makes it impossible to determine the end position of the first pathname and the start position of the second pathname when processing ls output.

 for file in "$(ls *.txt)"

Enclosing ls in double quotes causes its output to be treated as a single word -- not the expected list of files.

How to traverse the file list in the correct way? There are two possible strategies:

 for file in ./*. txt

This will use the bash globbing function above. Remember the double reference "${file}"!

 find .  -type f -name '*.txt' -exec ...

This is probably the best solution. Find The tool allows you to use regular expression based search (- regex), recursion and has many built-in functions that you may find useful. here There is a good introduction.

 find .  -type f -name '*.txt' -print0 | xargs -0 ...

Another alternative to find is to use xargs It is neither simple nor short, but the advantage of xargs is that it supports parallel pipeline execution. Read more about this difference This article

In summary, never try to parse the output of the ls command. It is not designed for parsing at all, and you cannot make it work properly. Click here Read more.

 Tocy
Translated at 18:05 on January 11, 2019
top
zero

6. Expecting accidents

You often forget to check for non-zero status codes that execute commands in bash scripts. It is easy to imagine what will happen when our cd command silently fails before file operation (because, for example, "there is no such file or directory").

 #!/ usr/bin/env bash cd "${some_directory}" rm -rf ./*

The above example will work well, but only if there is no error. The purpose is to delete the contents of the some_directory/directory, but eventually rm - rf./* may be executed in a completely different directory location.

cd“$ {some_directory}”&& rm -rf ./* And cd "${some_directory}" | | return are the simplest self describing solutions. In both cases, if cd returns non-zero, the deletion will not be performed. It is worth noting that this code is still vulnerable to common programming errors - spelling errors.

Execute cd "${some_dierstore}"&&rm - rf/* The file you may want to keep will be deleted eventually (as long as there is no misspelled some_dierstore variable declaration). "${some_dierstore}" will be expanded to "", which is a fully valid cd parameter and will take us to the home directory. But don't worry, this is not the end of the story.

 tsingkuo2019
Translated at 09:45, January 10, 2019
top
zero

Bash programming has some noteworthy programmer friendly switches:

  • Set - o noinset can set bash to treat referencing uninitialized variables as errors. This feature can prevent us from making low-level errors such as spelling.

  • Set - o errexit can set the bash script to exit immediately when the return value of the statement is non-zero. Although using errexit can help us effectively verify program errors, it requires some skills to use errexit correctly. Some commands intentionally return non-zero values to generate alarms, and programmers know exactly how to handle the error values returned by specific commands. See here for more information.

  • Set - o pipefail can change the default behavior when using pipes. By default, bash will use the status code returned by the command in front of the pipeline as the input of the command behind the pipeline, which means that false | true returns 0 (the status before the pipeline symbol is not 0, and the command after the pipeline symbol will be executed immediately). This result may not be what you expect, because the result of the command before the pipe symbol will be ignored in this case. At this time, the pipefail command needs to be used. Through the set - o pipefail setting, you can set the exit code of the pipeline to the rightmost command that returns non-zero (or set the exit code to 0 if all instructions are successfully executed).

Of course, the handling of error problems is not only applicable to the cd command mentioned above. Your bash script should consider possible problems in various situations, such as the space in the path name, the missing file, and the directory not created, Or incorrectly using non-existent commands (for example, as you know, not all Linux operating systems running your bash scripts have the awk command pre installed)

 SVD
Translated at 23:52, January 5, 2019
top
zero
All translations in this article are only for learning and communication purposes. Please be sure to indicate the translator, source and link of the article when reprinting.
Our translation work follows CC protocol If our work infringes your rights and interests, please contact us in time.
Loading

Comments( zero )

 Back to top
Top