shell脚本编写_Shell脚本2编写脚本

shell脚本编写

In part one of this series, I talked about the basics of Linux system. I could have simply given what I follow to write the shell script but that would not be useful. If you are writing it for enterprises and are managing some serious applications generating big revenues, you code deliverable should be good. The first part was necessary because if you are already good with Linux basics/administration, honestly you wouldn’t be reading this blog as the admins are already way smarter on the scripting part. These two blogs are intended for application developers who, I believe, must know about the Linux basics and scripting because as you write the core application, you will also be responsible to write some testing automation scripts, application failover and recovery scripts, Dockerfile, Jenkins pipeline, application upgrades and disaster recovery workflows.

在本系列的第一部分中,我讨论了Linux系统的基础。 我可以简单地给出我编写外壳脚本所遵循的内容,但这将无用。 如果您是为企业编写它,并且正在管理一些严肃的应用程序并产生可观的收入,那么您交付的代码应该是不错的。 第一部分是必要的,因为如果您已经熟悉Linux基础知识/管理,那么坦白地说您不会读此博客,因为管理员在脚本编写方面已经变得更加聪明。 我相信这两个博客是为必须了解Linux基础知识和脚本的应用程序开发人员准备的,因为在编写核心应用程序时,您还将负责编写一些测试自动化脚本,应用程序故障转移和恢复脚本,Dockerfile,Jenkins管道,应用程序升级和灾难恢复工作流。

Here also I would not be covering basic syntax for the code. There are tonnes of write-ups you will find on the internet. I will focus on how you can use the basic blocks along with some crucial items to write an enterprise-level and production-ready shell script.

在这里我也不会覆盖代码的基本语法。 您可以在互联网上找到大量的文章。 我将重点介绍如何使用基本块以及一些关键项目来编写企业级且可用于生产的Shell脚本。

基本概念 (Basic Concepts)

I hope from the previous blog, you are already familiar with the terms — terminal and shell, what are the different kinds of shell available out there.

我希望您在上一篇博客中已经熟悉术语-终端和外壳,那里提供了哪些不同类型的外壳。

The first basic shell script would be like this —

第一个基本的shell脚本将是这样的-

>> cd /home/innovationchef
>> vi script.sh
.. echo "Hello World"
:wq
>> chmod 775 script.sh
>> ./script.sh
Hello World

Once you get the above script running. We are good to start writing the basic scripts. Here I would enlist the items that you need to go and learn about separately —

一旦上述脚本运行。 我们很高兴开始编写基本脚本。 在这里,我将列出您需要单独学习的项目-

变量和函数声明 (Variable and function declaration)

You need to know about the local/global variables, reading a var from stdin using the read command, variable concatenations and substitutions, using declare to create read-only and integer variables and unset a variable. Give special focus on arrays and associative arrays. Always do a explicit declaration of the variable along with assignment and don’t leave it up to the shell to decide.

您需要了解局部/全局变量,使用read命令从stdin读取var,变量串联和替换,使用define创建只读和整数变量并取消设置变量。 特别关注数组和关联数组。 始终对变量和赋值进行明确的声明,而不要让外壳决定。

# Declaration
>> declare -a DEPLOYMENT_NAME=(order basket, login)
>> declare -a DEPLOYMENT_NAME=([1]=order [2]=basket [3]=login)
>> declare -A DEPLOYMENT_REPLICAS_MAP=([order]=4 [login]=10 [basket=7])
# To access the values >> for val in "${DEPLOYMENT_REPLICAS_MAP[@]}"; do
>> echo "${val}"
>> done
4 10 7
# To access the keys>> for key in "${!DEPLOYMENT_REPLICAS_MAP[@]}"; do
>> echo "${key}"
>> done
order login basket
# Size of the array >> ${#DEPLOYMENT_REPLICAS_MAP[@]}# Adding an item>> DEPLOYMENT_NAME+=(payment categories)
>> DEPLOYMENT_NAME+=([4]=payment [5]=categories)
>> DEPLOYMENT_REPLICAS_MAP+=([payment]=3 [categories]=2)
# Deleting an item>> unset DEPLOYMENT_REPLICAS_MAP[1]
>> unset DEPLOYMENT_REPLICAS_MAP[payment]
# Command Substitution>> arr=( $(ls) )# Split arrays >> ${arr[@]:1:4}

For function declaration, you should keep in mind is that it is like any other functional programming ways of going it. The way you can pass arguments to a function in shell script is same as the way you pass arguments to the shell script itself. We will look at the latter below.

对于函数声明,您应该记住的是,它就像其他任何函数式编程方式一样。 将参数传递给Shell脚本中的函数的方式与将参数传递给Shell脚本本身的方式相同。 我们将在下面查看后者。

环境变量 (Environment Variables)

Some important Internal variables —

一些重要的内部变量-

Note that I already gave a list of around 35 basic commands in the previous blog. These are global env variables available to your user with which you are logging in. These are the commonly user ones —

请注意,在先前的博客中,我已经给出了大约35个基本命令的列表。 这些是全局env变量,可供您登录的用户使用。这些是常见的用户变量-

>> $BASH             -- the path to the Bash binary itself>> $BASH_VERSINFO[0] -- Major version no. of installed bash>> $FUNCNAME         -- name of the current function>> $GROUPS           -- groups current user belongs to>> $HOME             -- home directory of the user>> $HOSTNAME>> $IFS              -- This var determines how Bash recognizes fields, or word boundaries when it interprets character strings.>> $LINENO           -- line number of the shell script in which this variable appears>> $PATH             -- path to binaries, a list of directories, separated by colons>> $PPID             -- The process ID of parent process>> $PWD>> $REPLY            -- default value when a variable is not supplied to read>> $SECONDS          -- The number of seconds the script has been running.>> $UID              -- user id of the current user

Note that a script can export variables only to child processes, that is, only to commands or processes which that particular script initiates. A script invoked from the command-line cannot export variables back to the command-line environment. Child processes cannot export variables back to the parent processes that spawned them.

请注意,脚本只能变量导出到子进程 ,即只能导出到该特定脚本启动的命令或进程。 从命令行调用的脚本无法将变量导出回命令行环境。 子进程无法将变量导出回产生它们的父进程。

基本代码流程 (Basic code flow)

There are logical operators defined like —

定义了逻辑运算符,例如-

# for arithmetic manipulations
-eq, -nq, -le, -gt, -ge, -lt
# for string manipulation==, =, !=, \>, \<
-z
is string empty-n for not empty# for file checking -f filename -> true if it is a regular file-d directory -> true if it is a dir -s filename -> if it is non-zero size.# && and || are supported like in any other language

We can use this in the basic code flow just like any other language. You can look on the internet about — if-else block, for and while loops with break and continue statements and the switch case — esac block.

我们可以像其他语言一样在基本代码流中使用它。 您可以在Internet上查看有关-if-else块, forwhile循环以及breakcontinue语句以及切换用例的情况-esac块。

替代 (Substitution)

命令替换 (Command Substitution)

Command substitution allows the output of a command to replace the command itself. Bash performs the expansion by executing command and replacing the command substitution with the standard output of the command, with any trailing newlines deleted. Embedded newlines are not deleted, but they may be removed during word splitting.

命令替换允许命令的输出替换命令本身。 Bash通过执行命令并将命令替换替换为命令的标准输出来执行扩展,并删除所有尾随的换行符。 嵌入的换行符不会被删除,但是可以在分词过程中将其删除。

>> declare -a ARR=$(seq 1 5)
>> echo $ARR

1 2 3 4 5

流程替代 (Process Substitution)

Process substitution allows a process’s input or output to be referred to using a filename.

进程替换允许使用文件名引用进程的输入或输出。

>> <(command)
>> >(command)

You will find a good example here —

您会在这里找到一个很好的例子-

可变替代 (Variable Substitution)

${var}            -- Substitute the value of var.${var:-word}      -- value of word is var is null or unset${var:=word}      -- If var is null or unset, var is set to the value of word.${var:?message}   -- If var is null or unset, message is printed to standard error. This checks that variables are set correctly.${var:+word}      -- If var is set, word is substituted for var. The value of var does not change.## Examples >> foo=${bar:-something}
>> echo $foo

something>> echo $bar>> foo=${bar:=something}>> echo $foo something>> echo $bar <-- something too, as there's an assignement to barsomething

扩张 (Expansion)

Brace Expansion: Provide a comma-separated list of values with curly brackets. There are two optional parts — preamble and postscript. Preamble: to add text at the front of each generated string and Postscript: to append text at the end of the generated string.

括号扩展:用逗号括起来的值列表,并用大括号括起来。 有两个可选部分- 前言后记Preamble :在每个生成的字符串的开头添加文本, Postscript: 在生成的字符串的末尾添加文本

{ String1, String2,... ,StringN }
{ <start> . . <end> }
<preamble> { string or range } <postscript>>> echo {"I like ","Learn "}{"PHP","Programming"}I like PHP I like Programming Learn PHP Learn Programming>> echo {01..03}{A..C}01A 01B 01C 02A 02B 02C 03A 03B 03C >> echo "Hi "{Ankit, Lohani, Innovationchef}Hi Ankit Hi Lohani Hi Innovationchef

Tilde expansion:

波浪号扩展:

~   -- replaced with /home~+  -- replaced with current working dir

Path expansion:

路径扩展:

>> cd Desktop>> echo D*DawnFolder DCIM.jpg
>> echo /usr/*/share
/usr/local/share /usr/mongo/share

Arithmetic expansion: Arithmetic expansion and evaluation is done by placing an integer expression using the following format and the result is substituted.

算术扩展:算术扩展和评估是通过使用以下格式放置整数表达式来完成的,并替换结果。

$((expression))>> if (( VARIABLE == 0 )); then echo "true"; fi

The expression can contain anything from usual mathematical operators like +- to bitwise operators like > < to logical operators like == and +=.

该表达式可以包含任何内容,从常用的数学运算符(如+-)到按位运算符(如> <)到逻辑运算符(如==和+ =)。

Note that there are other ways of arithmetic manipulation, however, I prefer double parenthesis over let and expr

请注意,还有其他算术操作方式,但是,我更喜欢使用双括号而不是letexpr。

分组命令 (Grouping Commands)

## command2 is executed if, and only if, command1 returns an exit status of zero (success).
>> command1 && command2
## command2 is executed if, and only if, command1 returns a non-zero exit status.>> command1 || command2## In pipes - Each command in a pipeline is executed in its own subshell, which is a separate process. It is a stream and all actions happen together unlike the last example. >> ps -ef | grep java | wc -l## In the below groupings, the redirection is applied on the complete block.## below causes a subshell environment to be created and all cmds are executed there.>> ( ls ps )## below causes the list to be executed in the current shell context.>> { mkdir log.log, chmod 775 log.log, cat log.log }

标准流和重定向 (Standard Streams & Redirection)

Streams can be seen as a channel or a pipe through which data is going to flow and reach the shell. It is up to you where you connect these streams or pipes. There are three standard streams — one for input, another for output and finally for error. You can, if you wish, connect the input stream to a keyboard or a microphone, your output stream to your terminal on the screen and your error to your printer. It is just a pipe whose one end is connected to the shell and other other is free to be connected to anything you like! Note that input stream takes in the data and output/error spits it out. So, connecting input stream to a printer is useless!

流可以看作是数据将流经并到达外壳的通道或管道。 连接这些流或管道的位置取决于您。 有三个标准流-一个用于输入,另一个用于输出,最后一个用于错误。 您可以根据需要将输入流连接到键盘或麦克风,将输出流连接到屏幕上的终端,将错误连接到打印机。 它只是一根管道,其一端连接到外壳,而另一端可以自由连接到您喜欢的任何东西! 请注意,输入流接收数据,而输出/错误将其吐出。 因此,将输入流连接到打印机是没有用的!

These 3 streams are represented in linux as numeric values and are also called file descriptors. STDIN=0, STDOUT=1, STDERR=2. By default, 0 is connected to your keyboard and 1 and 2 are connected to the terminal. Hence you see everything on the screen and you never realize the difference.

这3个流在linux中表示为数值,也称为文件描述符。 STDIN = 0,STDOUT = 1,STDERR = 2。 默认情况下,0连接到键盘,1和2连接到终端。 因此,您可以看到屏幕上的所有内容,而您永远不会意识到两者之间的区别。

How do we use it? Well, when your command runs in the screen, you see everything on the terminal and you will never need to understand all of this. However, when you go for certain automation and background running jobs, the out is not channel’ed out to the terminal. You will probably direct it to a file and you need to understand how that works. Let’s look at redirection.

我们如何使用它? 好了,当您的命令在屏幕上运行时,您可以在终端上看到所有内容,而您将不需要了解所有这些内容。 但是,当您执行某些自动化和后台运行作业时,输出不会引导到终端。 您可能会将其定向到文件中,并且需要了解其工作方式。 让我们看一下重定向。

As noted earlier, everything in unix is a file. It means that the devices attached to the system appears as files in the local file system, even the keyboard and the mouse! These devices can be enlisted here — /dev. Now, say you do a ssh to your terminal from two different places, there will be two devices enlisted as files in the /dev dir, namely — /dev/pts/1 and /dev/pts/2. All the three streams 0, 1 and 2 are connected to the same terminal where you are going to write commands and read output/error.

如前所述,unix中的所有内容都是一个文件。 这意味着连接到系统的设备在本地文件系统中显示为文件,甚至包括键盘和鼠标! 这些设备可以在这里征募- / dev。 现在,假设您从两个不同的位置对终端执行ssh,则在/ dev目录中将有两个设备列为文件,即- / dev / pts / 1/ dev / pts / 2。 三个流0、1和2都连接到要写入命令和读取输出/错误的同一端子。

## If you do terminal 1>> ls -l## Output will show up in terminal 1
## To redirect it, you can use > ## We will redirect it to a file /dev/2>> ls -l 1> /dev/pts/2## This file descriptor is associated to your 2nd terminal and the
## moment something is written to it, the terminal 2 will display it
## Voila, you just printed the output of command in T1 to T2 term

Now that we know what streams and redirection is, there are 2 basic items in the checklist you should know

现在我们知道什么是流和重定向了,清单中有2个基本项目您应该知道

输出和错误重定向 (Output & Error Redirection)

File redirection: You can redirect to a file using the > filename.log symbol. This will override the existing file — also k/a clobbering. If you want to append rather than over-write use >>.

文件重定向:您可以使用> filename.log符号重定向到文件。 这将覆盖现有文件,也将破坏文件。 如果要附加而不是覆盖,请使用>>。

## Clobbering>> ls -l > logfile.log
>> ls -l 1> logfile.log
## Appedning>> ls -l >> logfile.log## Redirecting both STDOUT and STDERR together>> ls -l &> logfile.log

Stream redirection: You can redirect the output to other stream instead of a file. You will use a ampersand here.

流重定向:您可以将输出重定向到其他流而不是文件。 您将在这里使用“&”号。

## Redirect STDOUT to STDERR and STDERR to STDOUT>> ls -l 1>&2 
>> ls -l 2>&1
## Redirect both in the same command>> ls -1 1> stdout.log 2> stderr.log
>> ls -l
> out.log 2>&1## Note, in the second way, we first redirect to file and then
## provide the stream redirection logic

Read another interesting topic on /dev/null here —

在此处阅读有关/ dev / null的另一个有趣的话题-

I will leave two open questions for you to google and figure out —

我将给您留下两个悬而未决的问题,供您搜索和查找-

  1. Why do we have STDOUT and STDERR different? Eventually we will need error to be presented like any other output, right?

    为什么我们的STDOUT和STDERR不同? 最终,我们将需要像其他任何输出一样显示错误,对吗?
  2. What would happen if I redirect both STDOUT and STDERR to the same file but separately? Like this — >> ls -1 1> out.log 2> out.log

    如果我将STDOUT和STDERR都分别重定向到同一个文件,该怎么办? 像这样— >> ls -1 1> out.log 2> out.log

输入重定向 (Input Redirection)

Till now, I only talked about the output redirection. When we come to redirection in STDIN, there are two thing we need to talk about

到目前为止,我只谈论了输出重定向。 当我们在STDIN中进行重定向时,我们需要谈论两件事

piping: In my previous blog, I already talked about the pipes and out they stream output of one command to input of other command. That is nothing but a redirection. In the below example, the output of ps aux is redirected to grep as its input stream

管道:在我以前的博客中,我已经讨论过管道,并且它们将一个命令的输出流传输到另一个命令的输入。 就是重定向。 在下面的示例中,将ps aux的输出重定向到grep作为其输入流

>> ps aux | grep java
>> ps aux |& grep java

Note that the connections are provided before any redirection. In that sense it is asynchronous or lazily loaded in some sense. By default, only STDOUT is piped as input to the following command. If you want the STDERR to go as well, you may use |& instead of a single |.

请注意,在进行任何重定向之前均已提供连接。 从某种意义上说,它是异步的或在某种意义上是延迟加载的。 默认情况下,仅将STDOUT传递给以下命令的输入。 如果还希望STDERR也可以使用,则可以使用|&而不是单个|。

reading from files: Using a < symbol we can take in the input stream from another file

从文件读取:使用<符号,我们可以从另一个文件获取输入流

>> wc < /home/innotionchef/myBlog.txt## this is same as below pipe redirection>> cat /home/innotionchef/myBlog.txt | wc 

创建新的文件描述符 (Creating New File Descriptors)

I found this block here and it was enough to understand the concept

我在这里找到了这个方块,足以理解这个概念

[j]<>filename
# Open file "filename" for reading and writing, and assign file descriptor "j" to it.
# If "filename" does not exist, create it.
# If file descriptor "j" is not specified, default to fd 0, stdin.
#
# An application of this is writing at a specified place in a file.
echo 1234567890 > File # Write string to "File".
exec 3<> File # Open "File" and assign fd 3 to it.
read -n 4 <&3 # Read only 4 characters.
echo -n . >&3 # Write a decimal point there.
exec 3>&- # Close fd 3.
cat File # ==> 1234.67890
# Random access, by golly.----------------------------------------------------------------exec 6<&0 # Link file descriptor #6 with stdin.
# Saves stdin.
exec < data-file # stdin replaced by file "data-file"
read a1 # Reads first line of file "data-file".
read a2 # Reads second line of file "data-file."exec 0<&6 6<&-
# Now restore stdin from fd #6, where it had been saved,
#+ and close fd #6 ( 6<&- ) to free it for other processes to use.
#
# <&6 6<&- also works.

Redirection can be applied to a block of code as well

重定向也可以应用于代码块

{
# part of script
} > file1 2>file2

I will show two important use cases where these concepts come in handy. After reading this, you would definitely want to use it in your application somewhere.

我将展示两个重要的用例,这些概念将派上用场。 阅读本文之后,您肯定希望在应用程序中的某个地方使用它。

用例1:创建一个shell脚本,将输出写入日志文件和控制台 (Use case 1: Create a shell script that writes the output to the logfile as well as the console)

I have seen scripts doing this —

我已经看到脚本可以做到这一点-

>> $LOGFILE=/home/innovationchef/log.txt
>> echo "File not found"
>> echo "File not found" >> $LOGFILE

That’s Bad! There will be so much boilerplate code hanging around in your script. We will use 2 things that we learnt so far — process substitution and redirection and along with a new exec and tee command to do this.

那很糟! 您的脚本中会有很多样板代码。 到目前为止,我们将使用两件事:进程替换和重定向,以及新的exectee命令来完成此任务。

First we will understand the tee command because we are not aware of it. tee command reads the STDIN and writes it to both the STDOUT and one or more files

首先,我们将了解tee命令,因为我们对此并不了解。 tee命令读取STDIN并将其写入STDOUT和一个或多个文件

>> ps -ef | grep java | tee /home/innovationchef/logs.log
## The output of the blocked pat goes to a file and is also displayed on the screen

The exec command comes from the family of core process theory — fork(), exec(), wait(), exit(). As discussed in the previous blog, it replaces the current process with new process. This replacement also happens for the redirection of the original process it replaced. In bash, the exec can be followed with a command, its arguments and any redirection. If you don’t provide any redirection, the original processes redirection will be applied. In our use case, we will over-write it.

exec命令来自核心流程理论系列-fork(),exec(),wait(),exit()。 如前一个博客中所讨论的,它用新过程代替了当前过程。 对于替换的原始进程的重定向,也会发生此替换。 在bash中,可执行文件后可以跟随命令,其参数和任何重定向。 如果您不提供任何重定向,则将应用原始进程重定向。 在我们的用例中,我们将覆盖它。

Coming back to the use case —

回到用例—

# The below would be creating a subshell
>> (command)
# The below would create a process file
>> (
tee -"${LOGFILE}")# The below will give us a file representing its STDOUT>> >(tee -"${LOGFILE}")## the exec command with the redirection you want
## Below redirects the current shell to whatever yhou provide in the right>> exec &> >(## And we are done>> echo "This will be logged to the file and to the screen"## As I discussed in redirection, you can also use the below>> exec > >(tee -"${LOGFILE}")
>> 2>&1

Of course there are multiple other ways, but I find the above one the most elegant way of doing it.

当然,还有其他多种方法,但是我发现上述方法是最优雅的方法。

用例2:从一台服务器管理多个群集节点 (Use case 2: Managing multiple cluster nodes from 1 server)

HERE Document

这里的文件

It is a special form of I/O redirection that tell bash shell to read input from the current source until a line containing only the delimiter is seen.

这是I / O重定向的一种特殊形式,它告诉bash shell从当前源读取输入,直到看到仅包含定界符的行。

interactive-command <<unique-delimiter-string
.. line 1
.. line 2
..
.. line n
unique-delimiter-string

This SO answer already shows a the usages —

这样的答案已经显示了用法-

The use case you normally find on the internet is the cat one. I will show you another cool use case.

您通常在互联网上找到的用例就是 。 我将向您展示另一个不错的用例。

Suppose you are managing kafka cluster spread over 5 nodes and you wrote a script to do all cool stuff easily — like check status of the node, start and stop a node. Then you will have to it for both zookeeper and broker. The script would be big and you will have to execute it on each node. That means, you have to keep the same script on 5 different nodes and then go to each node and execute manually.

假设您正在管理分布在5个节点上的kafka集群,并且编写了一个脚本来轻松完成所有很酷的工作-例如检查节点的状态,启动和停止节点。 然后,动物园管理员和经纪人都必须这样做。 该脚本将很大,您将必须在每个节点上执行该脚本。 这意味着,您必须在5个不同的节点上保留相同的脚本,然后转到每个节点并手动执行。

One day you will get irritated and will want to control the 5 nodes from 1 server. You would want to ssh to the 5 node from one server and execute script on that server. But, you would still need to keep the master script on each node.

有一天,您会感到恼火,并且想要从一台服务器控制5个节点。 您可能希望从一台服务器ssh到5个节点并在该服务器上执行脚本。 但是,您仍然需要将主脚本保留在每个节点上。

Then you decided that you would keep the master script on the root server and do a scp every time you want to operate on the node. So, you would be basically doing a scp, then a ssh and finally you wild also want to delete the copy of the master script from the kafka node. That is still clumsy.

然后,您决定将主脚本保留在根服务器上,并在每次要在节点上进行操作时执行一次scp。 因此,您基本上会先做一个scp,然后是ssh,最后您也疯狂地想要从kafka节点中删除主脚本的副本。 那仍然很笨拙。

Then you found out that you can do this —

然后您发现可以执行此操作-

>> sshpass -p password scp -o StrictHostKeyChecking=no /localpath/kafka-manager.sh username@ip-address:/remotepath>> sshpass -p password ssh -o StrictHostKeyChecking=no username@ip-address "cd /remotepath; ./kafka-manager.sh; rm kafka-manager.sh; exit"

Great right? I myself used this solution for sometime until I found the better way of doing it! Why create a script and transfer it? It was because the kafka-manager.sh script was too big and I didn’t want to write in the inverted commas after the ssh command. That was ugly and my IDE did not provide a great formatting of my script file.

很好吗? 我自己使用该解决方案已有一段时间,直到找到更好的解决方案! 为什么要创建脚本并进行传输? 这是因为kafka-manager.sh脚本太大,并且我不想在ssh命令后用逗号分隔。 那太丑了,我的IDE并没有提供很好的脚本文件格式。

Then I used the here document concept —

然后,我使用了这里文档的概念-

>> sshpass -p password ssh -o StrictHostKeyChecking=no username@ip-address <<MYUNIQUESTRING## Here I worte the contents of the kafka-manager.sh file
## All 200 lines of code hereMYUNIQUESTRING

Now this is what we call an elegant solution! No SCP and everything in 1 script at the controller server node.

现在,这就是我们所说的优雅解决方案! 在控制器服务器节点上没有SCP和1个脚本中的所有内容。

Shell脚本的重要组件 (Important Components of a Shell Script)

舍邦 (Shebang)

This fancy term is sitting every where on google. Go and check it out.

这个花哨的名词在Google上无处不在。 去检查一下。

When you login to a shell, you get a default interpreter for your session which can be updated by your server administrator or you can do it yourself using the bashrc files. I would still mention the most common one that is being used and is usually available to you by default. Still having it in your script is a recommended activity. You can also provide a /usr/bin/env. It makes your code portable in the sense that whatever bash executable appears first in the running user’s $PATH variable will be used.

登录到Shell时,您将获得会话的默认解释器,该解释器可以由服务器管理员更新,也可以使用bashrc文件自己进行更新。 我仍然会提到最常用的一种,默认情况下通常可以使用。 建议您在脚本中保留它。 您还可以提供/ usr / bin / env。 从某种意义上说,这将使您的代码可移植,因为将使用在运行用户的$ PATH变量中首先出现的bash可执行文件。

#!/bin/sh

外壳参数 (Shell Parameters)

Note that there are two kind of parameters — the positional parameters that are provided as argument to you shell script and special parameters that are denoted by some special character.

请注意,有两种参数-作为Shell脚本的参数提供的位置参数和由某些特殊字符表示的特殊参数。

>> ./my-script.sh foo bar## Within the script foo bar can be accessed as
## ${1}, ${2}.... ${9}, ${10} etc
## ${0} is the name of the script itself
## $* and $@ represent all args as one
## $# is the number of args passed
## $$ is the PID of the script run instance
## $? return code of the last executed command ## Shift can manually move the pointer to an argument
while (( $# )); do
echo $1shift
done

getopts (getopts)

getopts is the very important and nice feature that you will need in almost every script that you write. You will have to provide common arguments like script logging type — console or file or both, environment — prod or non-prod, and probably at least I command to tell what the script has to do in this execution instance (unless your script is meant to do the exact thing always).

getopts是几乎所有编写的脚本中都需要的非常重要且不错的功能。 您将必须提供常用参数,例如脚本日志记录类型(控制台或文件或两者,环境)-prod或非prod,并且可能至少我命令告诉脚本在此执行实例中必须执行的操作(除非您的脚本是要这样做的)总是做确切的事情)。

Kevin here has covered all the important aspects to get you started —

凯文(Kevin)涵盖了所有重要方面,可帮助您入门-

选件 (Options)

Options are built-in settings from the shell that would change it’s behavior. There are many but I would point out only the few that you would require.

选项是外壳程序的内置设置,可以更改其行为。 有很多,但我仅指出您需要的少数几个。

Use (-)dash to activate and + to deactivate them

使用(-)破折号激活和+停用它们

## Exit as soon as any command returns a non-0 exit code
set -o errexit## Exit the script if there are any un-initialized variable
set -o nounset## If the pipe redirection fails after any pipe - fail fast
set -o pipefail## Print each command to stdout before executing it
set -o verbose## Read commands in script, but do not execute them (syntax check)
set -o noexec## Prevent overwriting of files by redirection
set -o noclobber

双破折号 (Double dash)

Develop a habit of using (- -) without spaces in most of your commands. It is used in most bash built-in commands to signify the end of command options, after which only positional parameters are accepted. Now, eve though something looks like an option, it will be treated as a argument. This is to be safe when you don’t know what can be on the right end of the command.

养成使用大多数命令中不带空格的(--)的习惯。 它在大多数bash内置命令中用于表示命令选项的结尾,此后仅接受位置参数。 现在,即使前夕看起来像一个选项,它将被视为一个参数。 当您不知道命令右边的内容时,这样做是安全的。

This one is quite useful with the printf command when it is wrapped in another function. Here is an example.

当将它包装在另一个函数中时,它对于printf命令非常有用。 是一个例子。

陷阱和出口 (Traps and Exit)

I have already discussed the kernel signals in the previous blog. Here is how to write a handler for them in your shell script and take decisions. Using traps is important because if a SIG comes and you are in the middle of an arithematic operation or you have blocked some system resources during your run, it can result into unpredictable outcomes from your shell.

我已经在上一篇博客中讨论了内核信号。 这是在您的Shell脚本中为它们编写处理程序并做出决定的方法。 使用陷阱非常重要,因为如果SIG来了并且您正处于算术运算的中间,或者在运行期间阻塞了一些系统资源,则可能会导致Shell产生不可预测的结果。

>> function handle() {
echo "handled"
}
trap handle SIGTERM SIGINT SIGHUP

Also, you can define what you want to do for each signal and it differs from case to case basis. In most examples you will see clean up of any temp directory you created as a part of your execution, you should also consider logging out from any other session that you might have logged in during the execution.

此外,您可以定义要对每个信号执行的操作,具体情况视情况而定。 在大多数示例中,您将看到执行过程中创建的任何临时目录的清理,还应考虑从执行过程中可能登录的任何其他会话注销。

Here is a good article on it —

这是一篇很好的文章-

Now, when you decided to exit from you application, do provide an exit code. It is very helpful for the parent process to decide what happened. This is because, a lot of times you will have some Ansible instance calling your script and you might not be invoking it yourself from the terminal. If becomes meaningful for the client party to deduce the execution summary from the exit code. Important ones are —

现在,当您决定退出应用程序时,请提供退出代码。 父进程决定发生了什么非常有用。 这是因为很多时候,您将有一些Ansible实例调用您的脚本,并且您可能不会从终端本身调用它。 对于客户端从出口代码中推断执行摘要变得有意义。 重要的是-

1   -- General script errors
127 -- command not found
130 -- Script terminated by CTRL+C
126 -- command cannot execute, when you are trying to read a file you don't have access to

You may define you own error codes and communicate it to the parent process what he should expect, just beware that you are not over-riding something already well defined in Linux.

您可能会定义自己的错误代码,并将其传达给他期望的父进程,只是要注意您不会覆盖Linux中已经定义好的东西。

其他一些有用的命令 (Some other useful commands)

>> dirs    -- lists the contents of the directory stack>> eval    -- convert string into command
>> exec --
This shell builtin replaces the current process with a specified command - remember fork(), wait(), exec()??>> find -- help you do a lookup over the file directories
>> basename --
extract filename from the
>> dirname --
extract directory name from the

Finally, here is a sample shell script that includes almost everything we have covered in this blog. In this script, the production support team would be able to scale up the pods if all were initially down.

最后,这是一个示例shell脚本,其中几乎包含了我们在本博客中介绍的所有内容。 在此脚本中,如果最初将所有吊舱缩小,则生产支持团队将能够放大吊舱。

翻译自: https://medium.com/swlh/shell-scripting-2-writing-the-script-8efae61eac7e

shell脚本编写

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值