1.Shell的概念
1.1 什么是shell
Shell脚本语言是实现Linux/UNIX系统管理及自W动化运维所必备的重要工具, Linux/UNIX系统的底层及基础应用软件的核心大都涉及Shell脚本的内容。Shell是一种编程语言, 它像其它编程语言如: C, Java, Python等一样也有变量/函数/运算符/if语句/循环控制/... 但在开始之前, 我想先理清Shell语言与Shell之间的关系。
当命令不在命令行中执行,而是从一个文件中执行时,该文件就是shell脚本。
Shell是一种解释型编程语言,不需要编译,执行时也是按行执行。
Shell脚本是由解释器解释执行的,常见的解释器有:bash dash ash ksh sh等
特点:
shell脚本是普通的文本文件,由流程控制逻辑和命令构成。
shell脚本通常以.sh作为后缀名,但不是必须的。
我们现阶段学习的主要是bash dash.
个人理解: Shell与我们的JS都是解释性语言,不需要进行编译,拿java举例子,java在编译的时候需要编译才可以运行,而我们的shell是不需要编译的
1.2 什么是Shell脚本
命令、变量和流程控制语句等有机的结合起来
shell脚本擅长处理纯文本类型的数据,而linux中,几乎所有的配置文件,日志,都是纯文本类型文件,所以我们需要使用Shell脚本来进行处理文字类型的数据
1.3 脚本语言的种类
1、编译型语言
定义:指用专用的编译器,针对特定的操作平台(操作系统)将某种高级语言源代码一次性翻译成可被硬件平台直接运行的二进制机器码(具有操作数,指令、及相应的格式),这个过程叫做编译(./configure make makeinstall );编译好的可执行性文件(.exe),可在相对应的平台上运行(移植性差,但运行效率高)。。典型的编译型语言有, C语言、C++等。
另外,Java语言是一门很特殊的语言,Java程序需要进行编译步骤,但并不会生成特定平台的二进制机器码,它编译后生成的是一种与平台无关的字节码文件(*.class)(移植性好的原因),这种字节码自然不能被平台直接执行,运行时需要由解释器解释成相应平台的二进制机器码文件;大多数人认为Java是一种编译型语言,但我们说Java即是编译型语言,也是解释型语言也并没有错。
2、解释型语言
定义:指用专门解释器对源程序逐行解释成特定平台的机器码并立即执行的语言;相当于把编译型语言的编译链接过程混到一起同时完成的。
解释型语言执行效率较低,且不能脱离解释器运行,但它的跨平台型比较容易,只需提供特定解释器即可。
常见的解释型语言有, Python(同时是脚本语言)与Ruby等。
3、脚本语言
定义:为了缩短传统的编写-编译-链接-运行(edit-compile-link-run)过程而创建的计算机编程语言。
特点:程序代码即是最终的执行文件,只是这个过程需要解释器的参与,所以说脚本语言与解释型语言有很大的联系。脚本语言通常是被解释执行的,而且程序是文本文件。
典型的脚本语言有,JavaScript,Python,shell等。
4、其他常用的脚本语句种类
shell脚本的优势在于处理操作系统底层的业务 (linux系统内部的应用都是shell脚本完成)因为有大量的linux系统命令为它做支撑。2000多个命令都是shell脚本编程的有力支撑,特别是grep、awk、sed等。例如:一键软件安装、优化、监控报警脚本,常规的业务应用,shell开发更简单快速,符合运维的简单、易用、高效原则.
1.4 Shell常见种类
-
**Bsh:**由贝尔实验室编写。Bsh是产生较早的UNIX Shell程序,实现了最基本的命令解释器的功能,同时也可以作为脚本编程语言。
-
**Csh:**是因使用C语言的语法风格而得名,在用户的命令行交互界面上进行了很多改进,并增加了历史,别名,文件名替换,作业掏等功能,相比Bsh ,Csh在更加适用为用户提供命令交互操作。
-
**Ksh:**在Bsh和Csh之后出现的,结合了两都的功能优势,兼具Bsh的语法和Csh的交互特性。
-
**Bash:**从名称可以看出是Bsh的升级版本,是著名的开源软件项目,目前大多数的Linux版本(包括Red Hat公司的Linux系统)都使用Bash 作为默认的Shell程序当运行Shell程序时,实际运行的是Bash程序。
-
**Zsh:**更多地基于交互式操作进行设计的Shell程序,集成了Bash,Ksh等多种Shell程序的优点。
==Linux默认shell是Bourne Again shell(bash)==
2.Shell的基本使用
2.1 echo参数说明
参数 | 参数说明 |
---|---|
-n | 不要追加换行 |
-e | 启用下列反斜杠转义的解释 |
-E | 显式地抑制对于反斜杠转义的解释 |
\n | 换行 |
\r | 回车 |
\t | 横向制表符 |
\b | 退格 |
\v | 纵向制表符 |
\c | 抑制更多的输出 |
echo是默认换行的,与java的println是一样的, echo -n 与java的print是一样的,不换行
创建一个shell脚本,按照规范最好在文件后面加入.sh 代表是一个shell脚本
在shell脚本中 需要在第一行加入 #!/bin/bash
! /bin/sh是shell脚本的一个标志,声明这个script使用的shell。第一行的#!是一个约定标记, 它告诉脚本这段脚本需要什么解释器来执行. 第二行的echo命令则负责向屏幕上输出一句话。
使用sh命令 执行这个shell脚本,echo打印
我们也可以不使用sh来执行, ./文件名.sh也可以执行,但是我们会发现权限不够,是因为只有读和写,我们没有执行的权限
给这个文件的当前所有者加入一个执行权限再进行执行
2.2 Shell的变量
变量可以分为三类:环境变量(全局变量)、普通变量(局部变量)、 特殊变量
-
**环境变量:**也可称为全局变量,可以在创建他们的Shell及其派生出来的任意子进程shell中使用,环境变量又可分为自定义环境变量和Bash内置的环境变量
-
**普通变量:**也可称为局部变量,只能在创建他们的Shell函数或Shell脚本中使用。普通变量一般是由开发者用户开发脚本程序时创建的。
-
**特殊变量:**脚本内置的具有特殊用途的变量
变量就是js中的变量,也是java中的变量,意思没有变,但是声明变量的格式变了
2.1.1 环境变量
如何查看现有的环境变量呢? 请输入 env,这里是系统自带的环境变量
我们可以取出来使用,因为环境变量是全局的,就是在整个linux系统中,是都可以使用的,我们在shell脚本中的操作,是可以用在命令行中的,这里的意思是我们取出了USER的变量值,并且进行了打印
${变量名} :获取变量的值
*注意:$用来获取变量,=前后不加空格*
2.1.2普通变量
本地变量在用户当前的Shell生存期的脚本中使用。例如,本地变量OLDBOY取值为bingbing,这个值在用户当前Shell生存期中有意义。如果在Shell中启动另一个进程或退出,本地变量值将无效。只能用在shell当前的脚本中,在shell脚本中赋值就是普通变量
我们在命令行是拿不到这个变量的,这就是普通变量
2.1.3 特殊变量
位置变量
位置变量 | 作用说明 |
---|---|
$0 | 获取当前执行的shell脚本的文件名,如果执行脚本带路径那么就包括脚本路径。 |
$n | 获取当前执行的shell脚本的第n个参数值,n=1..9,当n为0时表示脚本的文件名,如果n大于9用大括号括起来{10},参数以空格隔开。 |
$# | 获取当前执行的shell脚本后面接的参数的总个数 |
$* | 获取当前shell的所有传参的参数,不加引号同如果给加上双引号,例如: "$",则表示将所有的参数视为单个字符串,相当于"112$3"。 |
$@ | 获取当前shell的所有传参的参数,不加引号同如果给@加上双引号,例如: "则表示将所有参数视为不同的独立字符串,相当于1" "3" "......",这是将参数传递给其他程序的最佳方式,因为他会保留所有内嵌在每个参数里的任何空白。 |
当"和@"都加双引号时,两者有区别,都不加双引号时,两者无区别。 |
$n获取当前执行的shell的参数,并进行输出
进程状态变量
位置变量 | 作用说明 |
---|---|
$? | 获取执行上一个指令的执行状态返回值(0为成功,非零为失败),这个变量最常用 |
$$ | 获取当前执行的Shell脚本的进程号(PID),这个变量不常用,了解即可 |
$! | 获取上一个在后台工作的进程的进程号(PID),这个变量不常用,了解即可 |
$_ | 获取在此之前执行的命令或脚本的最后一个参数,这个变量不常用,了解即可 |
成功案例
失败案例
需要知道的是${变量名} == $变量名 缩写,如果要打印命令的话,需要加上``这个参数,不加的话认为是失败的,他不是变量
2.3定义变量的方式
-
直接赋值:name=zhangsan
-
传参 (传递参数):name=$1
-
交互式设置变量,使用read命令,输入hhhhhhh,给str赋值
在脚本中使用
2.4 变量的子串
2.4.1 变量子串说明
表达式 | 说明 |
---|---|
${parameter} | 返回变量$parameter的内容 |
${#parameter} | 返回变内容的长度(按字符),也适用于特殊变量 |
${parameteri:offset} | 在变量${parameter}中,从位置offset之后开始提取子串到结尾 |
${parameter:offset:length} | 在变量${parameter}中,从位置offset之后开始提取长度为length的子串 |
${parameter#word} | 从变量${parameter}开头开始删除最短匹配的word子串 |
${parameter##word} | 从变量${parameter}开头开始删除最长匹配的word子串 |
${parameter%word} | 从变量${parameter}结尾开始删除最短匹配的word子串 |
${parameter%%word} | 从变量${parameter}结尾开始删除最长匹配的word子串 |
${parameter/pattem/string} | 使用string代替第一个匹配的pattern |
${parameter//pattem/string} | 使用string代替所有匹配的pattern |
2.4.2特殊扩展变量说明
表达式 | 说明 |
---|---|
${parameter:-word} | 如果parameter的变量值为空或未赋值,则会返回word字符串并替代变量的值用途.如果变量未定义,则返回备用的值,防止变量为空值或因未定义而导致异常 |
${parameter:=word} | 如果parameter的变量值为空或未赋值,则设置这个变量值为word,并返回其值。位置变量和特殊变量不适用用途:基本同上一个${parameter>word},但该变量又额外给parameter变量赋值了 |
${parameter:?word} | 如果parameter变量值为空或未赋值,那么word字符串将被作为标准错误输出,否则输出变量的值。用途:用于捕捉由于变量未定义而导致的错误,并退出程序 |
${parameter:+word} | 如果parameter变量值为空或未赋值,则什么都不做,否则word字符串将替代变量的值 |
2.5 变量的数值计算
仅支持整数的运算
注意:1.空格 2.括号:(),(()),[],[[]]
$((数学运算表达式))
打印字符串
$[]运算符
let命令
let 命令是 BASH 中用于计算的工具,用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量。如果表达式中包含了空格或其他特殊字符,则必须引起来。
问题:我们如果想要给当前 i 加一的话要怎么办呢?原始的方法 (非常的绕口)
使用我们的let
expr 命令
expr命令是一个手工命令行计数器,用于在UNIX/LINUX下求表达式变量的值,一般用于整数值,也可用于字符串
运算符与参数之间需要有空格
1)整数计算
2)比较操作符 0失败 1成功
3)字符串表达式
-
'STRING : REGEX':执行模式匹配
-
'match STRING REGEX':等价于'STRING : REGEX'。
-
'substr STRING POSITION LENGTH':返回STRING字符串中从POSITION开始,长度最大为LENGTH的子串。
-
'index STRING CHARSET':CHARSET中任意单个字符在STRING中最前面的字符位置。如果在STRING中完全不存在CHARSET中的字符,则返回0。
-
'length STRING':返回STRING的字符长度。
4)逻辑连接符号"&"与"|":判断是否为0
"&"表示如果两个参数都非0,则返回第一个参数,否则返回0。但任意一个参数为空,则expr报错。除非空字符串使用引号包围,则处理方法和0一样。
"|"表示如果第一个参数非0,则返回第一个参数的值,否则返回第二个参数。但如果任意一个参数为空,则expr报错。除非空字符串使用引号包围,则处理方法和0一样。
bc 命令
bc 命令是任意精度计算器语言,通常在linux下当计算器用。它类似基本的计算器, 使用这个计算器可以做基本的数学运算。
安装bc依赖于base源 yum -y install bc
scale=5 保留五位小数
3.管道符
管道是由两个或多个命令组成的,前一个命令的输出作为后一个命令的输入
Linux管道命令是 " | ",其作用是用来连接多条指令,前一条指令的输出流会作为后一条指令的操作对象,其命令格式为"指令1 | 指令2 | ...",该命令的后一条指令,必须能够接收标准输入流命令才能执行。
它只能处理由前面一条指令传出的正确输出信息,对错误信息是没有直接处理能力的。然后,传递给下一条指令,作为操作对象。
解释:命令1 | 命令2 | 命令3 命令2是在命令1的基础上做筛选,命令3是对命令1和命令2的基础上做筛选
基本格式:
指令1 | 指令2 | …
注意:
1、管道命令只能处理前一条指令的正确输出,不能处理错误输出;
2、管道命令的后一条指令,必须能够接收标准输入流命令才能执行。
seq start end 输出从开始到结束的字段
3.1 分类
管道可以分为以下几种类型:
标准管道(stdin、stdout、stderr):通过文件描述符进行通信,通常使用|
表示。
命名管道(named pipe):也称为FIFO(First In First Out),可以通过文件名进行通信。
匿名管道(anonymous pipe):也称为PTY(Process Terminal),用于进程间通信
3.2 常用管道命令
在Linux中,常用的管道命令有:
-
选取命令:cut、grep
-
排序命令:sort wc uniq
-
双向重定向:tee
-
字符转换命令: tr, col, join, paste, expand
-
切割命令:split
-
参数代号:xargs
选取命令:cut、grep
选取就是将一段数据经过分析后,获取需要的数据
-
cut:以行为单位,将一段信息的某一片段信息切出来
-
grep:分析所需信息,取得所需信息所在
cut
cut -d '分割字符' -f fields
cut -c 字符区间
参数:
参数 | 说明 |
---|---|
-b | 以字节为单位进行分割 ,仅显示行中指定字节范围的内容 |
-c | 以字符为单位进行分割 , 仅显示行中指定范围的字符 |
-d | 自定义分隔符,默认为制表符"TAB" |
-f | 显示指定字段的内容 , 与-d一起使用 |
-n | 取消分割多字节字符 |
--complement | 补足被选择的字节、字符或字段 |
--out-delimiter | 指定输出内容是的字段分割符 |
显示所有用户的第一个字
显示从第二个字节后面的字
打印/etc/passwd文件中以:为分隔符的第1个字段和第6个字段分别表示用户名和其家目录
cut /etc/passwd -d ':' -f 1,6
grep
分析所需信息,取得所需信息的所在行
grep [-parameter] [-color=auto] '查找字符' filename
参数:
参数 | 说明 |
---|---|
-i | 搜索时,忽略大小写 |
-c | 只输出匹配行的数量 |
-l | 只列出符合匹配的文件名,不列出具体的匹配行 |
-n | 列出所有的匹配行,显示行号 |
-h | 查询多文件时不显示文件名 |
-s | 不显示不存在、没有匹配文本的错误信息 |
-v | 显示不包含匹配文本的所有行 |
-w | 匹配整词 |
-x | 匹配整行 |
-r | 递归搜索 |
-q | 禁止输出任何结果,已退出状态表示搜索是否成功 |
-b | 打印匹配行距文件头部的偏移量,以字节为单位 |
-o | 与-b结合使用,打印匹配的词据文件头部的偏移量,以字节为单位 |
示例
排序命令
sort
在Shell中,可以使用sort
命令对数据进行排序。
sort [-parameter] [file or stdin]
参数:
参数 | 说明 |
---|---|
-b | 忽略每行前面开始出的空格字符 |
-c | 检查文件是否已经按照顺序排序 |
-d | 排序时,处理英文字母、数字及空格字符外,忽略其他的字符 |
-f | 排序时,将小写字母视为大写字母 |
-i | 排序时,除了040至176之间的ASCII字符外,忽略其他的字符 |
-m | 将几个排序号的文件进行合并 |
-M | 将前面3个字母依照月份的缩写进行排序 |
-n | 依照数值的大小排序 |
-o <输出文件> | 将排序后的结果存入制定的文件 |
-r | 以相反的顺序来排序 |
-t <分隔字符> | 指定排序时所用的栏位分隔字 |
-k | 指定需要排序的栏位 |
示例:
创建test.txt
的文件,内容如下:
只记住这个命令就可以 ==> 排序之前先根据 :截取,指定需要给第二个截取的排序,并且按照数值
wc
统计指定文件中的字节数、字数、行数,并将统计结果显示输出
wc [-parameter]
参数:
参数 | 说明 |
---|---|
-w | 统计字数,或--words:只显示字数。一个字被定义为由空白、跳格或换行字符分隔的字符串 |
-c | 统计字节数,或--bytes或--chars:只显示Bytes数 |
-l | 统计行数,或--lines:只显示列数 |
-m | 统计字符数 |
-L | 打印最长行的长度 |
--help | 显示帮助信息 |
--version | 显示版本信息 |
**示例:**
双向重定向:tee
tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件
tee [-parameter] file
参数:
参数 | 说明 |
---|---|
-a | 附加到既有文件的后面,而非覆盖它 |
-i | 忽略中断信号 |
--- help | 查看帮助信息 |
--- version | 显示版本信息 |
将test.txt的内容放到test1.txt中,不加任何参数的话,每次放到test1.txt时都会先被清空内容再放入,
加入-a之后会附加到文件后面