These eight suggestions come from the experience and lessons of keyers in writing shell scripts over the past few years. As a matter of fact, I started to write more than these articles. Later, I thought about it again and again, removed some irrelevant ones, and finally left eight. It’s no exaggeration to say that each one is carefully selected, although there are some commonplace points.
1. Specify bash
The first line of the shell script, #! What should be after that? If you ask this question to others, different people may have different answers.
I’ve seen / usr / bin / env bash, and / bin / bash, and / usr / bin / bash, and / bin / sh, and / usr / bin / env sh. This can be regarded as the “four ways to write” in the field of programming.
In most cases, the above five writing methods are equivalent. However, people who have written programs all know that “few cases” often hide unexpected pits.
What if the default shell of the system is not Bash? For example, for a certain version of a Linux distribution, the default SH is not bash.
What if the bash of the system is not in / usr / bin / Bash?
I recommend / usr / bin / env Bash and / bin / bash. The former adds a middle layer through env to let env search bash in $path; The latter is the official endorsement, the conventional bash position, / usr / bin / Bash is just a symbolic link to it.
2. Set – E and set – x
OK, after some discussion, now the first line is settled. Is it time to write the second line?
wait a moment! Before you start thinking about and writing down specific code logic, insert a line set – E and a line set – X.
Set – x will output the executed content when executing each line of shell script. It allows you to see the current execution and the variables involved will be replaced with the actual values.
Set – e ends the program when an error occurs, just like “throw an exception” in other languages（ To be exact, not all errors will end the program. See the note below.)
Note: the conditions for set – e to end the program are quite complicated. In man bash, a paragraph is used to describe various scenarios. Most execution exits on error, unless the shell command is in the following situation:
The non ending part of a pipeline, such as error | OK
The non ending part of a combined statement, such as OK & error | other
The non ending part of a series of statements, such as error; ok
In the judgment statement, including test, if, while and so on.
The combination of the two can save you a lot of time in debugging. For defensive programming, it’s necessary to insert the first line of concrete code before writing it. Ask yourself, how many times can I write code correctly at one time? Most code, before it is submitted, usually goes through the process of repeated debugging and modification. Instead of introducing these two configurations at a time when you’re in a mess, leave room for debugging in the first place. After the code is finally ready to commit, it’s not too late to consider whether to keep it.
3. Take shellcheck with you
Well, now I have three lines of (template) code, and I haven’t written a line of specific business logic. Is it time to start writing?
wait a moment! Sharp tools make good work. This time, I will introduce a Shell Scripting artifact: shellcheck
Shame to say, although I’ve written shell scripts for several years, I still can’t remember some of the syntax. It’s time to rely on shellcheck. Shellcheck can not only remind the syntax problem, but also check the bad code of shell script. Originally, several of my n suggestions were about these bad codes. However, considering that shellcheck could find out these problems, I took pains to exclude them. There is no doubt that using shellcheck has brought me a great leap in shell writing skills.
The so-called “standing on the shoulders of giants”, although we recruits are not as skilled as veterans, we can catch up with each other in equipment! Move hands to install, you can get to know a “teacher”, why not?
By the way, shellcheck is written in Haskell. Who says Haskell can only be used to force?
4. Variable expansion
In shell scripts, you can occasionally see this: echo $XXX | awk / sed / grep / cut. It looks like a big situation. In fact, I just want to modify the value of a variable. Why use ox knife to kill chicken? Bash built-in variable expansion mechanism is enough to meet your needs! Read the f * * k Manaul! Man Bash and then search for parameter expansion. Here are the tips you want.
5. Pay attention to local
As you write more and more code, you start refining repetitive logic into functions. It’s possible you’ll fall into a hole in bash. In Bash, variables are global by default if local qualifiers are not added. Variables are global by default – similar to JS and Lua; But relatively few bash tutorials tell you that at the beginning. In the top-level scope, it doesn’t matter if it’s a global variable. But in a function, declaring a global variable can contaminate other scopes (especially if you don’t notice). So, for variables declared in a function, be sure to add the local qualifier.
6. Trap signal
If you’ve ever written a slightly more complicated program that runs in the background, you should know what “signal” is in POSIX standard. If you don’t know, go straight to the next paragraph. Like other languages, shell supports signal processing. Trap sighandler int can call sighandler function when SIGINT is received. The way other signals are captured, and so on.
However, the main application scenario of trap is not to capture which signal. The trap command supports “capturing” many different processes — precisely, allowing users to inject function calls into specific processes. The most commonly used are trap func exit and trap func err.
Trap func exit allows functions to be called at the end of a script. Because the registered function can be called no matter whether it exits normally or abnormally, I always use it to register the cleaning function when I need to call a cleaning function, instead of simply calling the cleaning function at the end of the script.
Trap func err allows functions to be called when a run error occurs. A common technique is to use the global variable error to store the error information, and then complete the corresponding error report according to the stored value in the registered function. The original fragmented error handling logic is concentrated in one place, which sometimes works wonders. However, remember that when the program exits abnormally, it will call both exit registered function and err registered function.
7. Think twice before you leap
The above are all specific suggestions, and the remaining two are more modest.
The name of the proposal is “think twice”. In fact, no matter what code you write, even if it’s just an auxiliary script, you should think twice and avoid carelessness. No, it’s even more important to keep that in mind when writing scripts. After all, many times, a complex script starts with a few small commands. At first, the people who wrote this script may think it’s just a one-time task. It’s hard to avoid some assumptions about some external conditions in the code, which may be normal at that time, but with the change of the external environment, these become hidden reefs. To make matters worse, few people test scripts. Unless you run it, you don’t know if it will work.
In order to slow down the decay rate of script code, we need to identify which dependencies will change and which are indispensable for the normal operation of script. There should be proper abstraction and changeable code; At the same time, we should have the sense of defensive programming and give our code a moat.
8. Develop strengths and avoid weaknesses
Sometimes, using shell to write scripts means that it is difficult to transplant, to deal with errors uniformly, and to deal with data neatly.
Although the use of external commands can easily and quickly achieve a variety of complex functions, but as the reverse side of the coin, we have to rely on grep, SED, awk and other tools to glue them together.
If there is a need to be compatible with multiple platforms, we should be careful to avoid strange traps such as BSD and GNU coreutils, bash version differences.
Due to the lack of perfect data structure and consistent API, shell script can not handle complex logic.
Solving specific problems requires the right tools. Knowing when to use shell and when to switch to a more general scripting language (such as ruby / Python / Perl) is also the key to writing reliable shell scripts. If your task can be accomplished by combining common commands and only involves simple data, shell scripts are suitable. If your task contains complex logic and data structure, you need to write scripts in languages like ruby / python.
The above is Xiaobian to introduce you to the shell script 8 reliable suggestions (worth collecting), hope to help you, if you have any questions, please leave me a message, Xiaobian will reply you in time. Thank you very much for your support to developer!