脚本
脚本就是批处理
平常一个命令不能完成的任务,写到一个文件当中,有多个命令按照一定的逻辑来进行编写
编写完后去执行这个文件,随后他就会按照流程多个命令,多个语句,达到批处理的一个任务
shell
系统分成了三层
第一层是kernel(内核)内核是操作系统的核心,主要负责1管理硬件,电脑cpu是什么样的、内存多大、硬盘多大、显卡是啥样的,这些都是由内核去管理,他管理计算机所有的硬件,包括直接连的和间接连的,直接连的比如:内存、CPU。间接连的比如:打印机。同时他还要负责驱动硬件干活,比如你要打印照片,内核会告诉打印机:去把某照片打印,打印机就去打印了。
内核是负责管理硬件和驱动硬件干活
第三层:用户user:下指令的,内核为啥要给你打照片?是因为用户告诉内核去把我的照片打一下,内核才去找打印机去打照片,用户是下指令的,下指令给内核,内核去干,内核让设备去干,那么shell的作用是啥
第三层:shell:命令解释器 用户和内核语言不同,内核识别的是二进制,1和0 内核只认识1和0。shell作为翻译官,用户下达命令,shell去解释翻译给内核,内核再去调用硬件去干活。
shell:介于系统内核与用户之间,负责解释命令行
shell脚本
是按照shell语法的规则去编写的脚本称为shell脚本(里面写的大部分都是shell命令)
1什么是Shell
Shell是命令解释器 解释用户输入的命令
Shell的分类:
面试题:Linux中默认的Shell是什么?
bash
两个概念
交互式shell:用户输入命令 bash解释用户输入的内容 执行完后 bash退出 称为交互式 和人交互的过程
非交互式shell 不和用户交互 执行脚本中的内容 执行到结尾 bash结束 称为非交互式shell
2.什么是shell脚本
把命令写入到文本中,执行这哥文件称为shell脚本,包含了变量 判断 循环 数组函数 比较表达式等等
3.语言种类
c c++ java python html ruby go php ....shell
编译型语言:
比如c语言,一次性编译终身执行,就和我开发了c语言中编译了二进制,计算机能够认识的语言,认识了第二次直接执行它,计算机还认识,就是一次编译,后面都可以运行
解释型语言
每次我执行都需要解释一遍
shell和python的区别
shell可以处理系统底层的功能 系统中的管理程序 启动 停止 运行 数据统计 数据分析 安装服务 配置 服务 管理服务
python 页面 cmdb自动化运维平台 业务程序 实现某个功能
python是自动化运维必须的!
3.创建shell脚本的规范
1.存放统一目录 方便做自动化管理 先要有规范 /server/scripts
2.脚本结尾必须以.sh结尾
3.脚本开头以#!/bin/bash 开头或者 #!/bin/sh
4.脚本中要有注释
date
author
功能:
5.注释尽量不用中文 可以使用中文
6.脚本中的符号一次性书写完成 必须英文符号
如'' "" {} ()
查看一下当前系统所支持的shell
总共就两个bash和tcsh
下图这几个都指向的bash l开头的是符号链接
nologin 是不能登录的意思
下图两个都指向的tcsh l开头的是符号链接
用户一登录就会得到一个shell,并且以下图提示符的形式让用户命令输出执行
是因为执行了bash这个命令后得到的这个环境
查看一下当前用户root 使用的shell
grep ^root /etc/passwd
下图写着/bin/bash
用户一登录就会执行这个命令他就会最后得到一个提示符相当于已经打开bash这个shell环境了,然后敲命令,他就会解释给内核,内核去驱动硬件
给用户更改shell
usermod -s /bin/csh user1
更改user1 用户的登录shell 为csh
编写第一个sehll脚本
格式 用vim编写 一般以.sh作为后缀(目的为了让其他人知道这是一个shell脚本)
vim test.sh
第一行脚本声明
#!/bin/bash
第二行注释信息
# 写上注释比如:这个脚本是干什么的啥啥啥
接下里是可执行语句
命令
保存退出
接下来 赋予可执行权限 使脚本具有可执行属性
chmod +x test.sh
然后执行脚本文件
方法一:脚本文件路径 直接写路径 绝地路径 或相对路径 需要有执行脚本权限
./test.sh #绝对路径/test.sh (需要有x权限)
方法二:sh 脚本文件路径
sh ./test.sh #或者sh 绝对路径/test.sh 或者sh test.sh (不需要有x权限)
方法三:source 脚本文件路径
source ./test.sh 或者. ./test.sh 或. 绝对路径/test.sh 或. test.sh(不需要有x权限)
./test.sh和sh test.sh 都是去子shell去执行的,执行完毕后关闭子shell
结果反馈给父shell的屏幕上 不会影响当前shell
. test.sh 是在当前shell下执行 并反馈给当前shell 会影响当前shell
切换子shell 直接敲bash 然后就去bash了 pstree 查看
一个进程相当于一个加工厂一样他有:
进料口(标准输入0(键盘))
出料口(标准输出1(显示器))
废料口(标准错误输出2(显示器))
列如执行一个命令
cat
然后输入 默认从键盘输入 他再从屏幕输出
cat前门进后门出 输入是键盘 输出是屏幕
如果我不想在屏幕上显示了
cat 1> test1
ctrl +d 提交
more test1 查看文件内容
标准输出1 对应位置是显示器 如果要改变位置就写1> 跟上文件名 这样就不在屏幕上显示了 (1> 只能保存正确输出信息)1>>追加
1> 代表的是标准输出重定向 做的比较多 一般>就代表1> 了 不用加1了
2>
比如我写错了他就会有错误提示
ls -jkl
如果错误提示我不想在屏幕上显示了,想把他保存到一个 文件当中去
ls -jkl 2> test2
标准错误输出2 对应位置显示器 如果改变位置就写2> 跟上文件名 这样就不在屏幕上显示了 (2> 只能保存错误输出信息)2>>追加
还有种写法 不管对错 都保存到一个文件中
&> 混合输出重定向
>覆盖式 清空原来文件内容 然后写新的
>>追加式 原来的文件内容不会被破坏,在后面追加上
类型 | 设备文件 | 文件描述编号 | 默认设备 |
标准输入 | /dev/stdin | 0 | 键盘 |
标准输出 | /dev/stdout | 1 | 显示器 |
标准错误输出 | /dev/stderr | 2 | 显示器 |
cat < test1
从test1中替代标准输入,交给cat是执行 标准输入是键盘替代键盘了
类型 | 操作符 | 用途 |
重定向输入 | < | 从指定的文件读取数据,而不是从键盘输入 |
重定向输出 | > | 将输出结果保存到指定的文件(覆盖原有内容) |
>> | 将输出结果追加到指定的文件 | |
标准错误输出 | 2> | 将错误信息保存到指定的文件(覆盖原有内容) |
2>> | 将错误信息追加到指定的文件中 | |
混合输出 | &> | 将标准输出、标准错误的内容保存到同一个文件中 |
管道操作符号“|”
将左侧的命令输出结果,作为右侧命令的处理对象
管道的前后都是对应的命令,每个命令对应的是个进程,如果一个命令不能完成我们需要的结果,就有可能把一个命令正常输出结果交给另外一个命令作为标准输入,然后再加下来处由图中2 的标准进程输出来 最终输出的内容是第二个进程的
还有可能还有其他进程,依次下去
比如
ls -l /etc 想要分页显示
ls -l /etc | more
awk
grep ^root /etc/passwd
比如我只想看第一段和最后一段 也就是root 和他的shell
grep ^root /etc/passwd | awk -F: '{print $1,$7}'
awk是按列或者按字段进行处理的一个工具,只要在一行中能够明确他的分隔符是谁他就按着他的分隔符去找到相应的列,然后进行处理
-F: 就是awk进行列处理的时候他的分隔符是: 如果列与列之间用空格分割这个就可以不写 默认分隔符就是空格
print 是打印的意思 就是在屏幕上输出的意思
$1代表第一列$7 代表第七列 中间用,隔开
shell变量的作用、类型
变量的作用
什么叫变量:定义变量:就是在内存中指定一个空间并且赋值
比如我定义一个变量ABC=12345678910111213141516…
变量的优点:方便来引用,如果频繁用这串数字,那么用的时候就可以$ABC
$ABC代表引用变量
变量的作用:增加脚本灵活性
变量的类型
1自定义变量:由用户自己定义、修改和使用 如ABC=123 用户自己设置的
2环境变量:由系统维护,用于设置工作环境 比如PATH 系统自己设置好了 也可以给他改变值
3预定义变量:Bash中内置的一类变量,不能直接修改 特殊的保留变量,这个名字早就固定好了,有特殊的含义,当我们执行脚本的时候这个变量就自动赋值了
位置变量:通过命令行给脚本程序传递参数,是一种预定义变量
定义一个新的变量
格式:变量名=变量值 列如ABC=123 第一个数不能是数字 可以是下划线
变量名以字母或下划线开头,区分大小写,建议全大写
查看变量的值
格式:echo $变量名 echo $ABC
自定义变量#####
变量赋值及引用时使用符号
双引号:允许通过$符号引用其他变量值
单引号:禁止引用其他变量值,$视为普通字符
反撇号:命令替换,提取命令执行后的输出结果
$():命令替换,命令嵌套是可替代反撇号
${}:引用变量,用于区分变量名与紧跟其后的字符串
双引号:允许通过$符号引用其他变量值
列如abc=123 456 空格属于特殊符号 但是系统认为前面是一个命令后面是一个命令
像这种情况需要用引号引起来让系统识别abc=“123 456”
单引号:禁止引用其他变量值,$视为普通字符
单引号也可以用来引用字符串他与双引号不同的是
双引号把$识别为变量引用
而单引号仅仅把$识别为普通字符
反撇号:命令替换,提取命令执行后的输出结果
我想看下vim的命令的目录 which vim
用变量查看:如果说我想把一个命令的结果放到什么位置这叫命令替换
可以用反撇号``
vim_weizhi=which vim
把上面的查询过程写到一块交给一个变量
vim_bao=`rpm -qf `which vim``
这样是不成的因为反撇号是朝一个方向的,他无法正确识别。不知道那个和那个是一对所以需要$()
$():命令替换,命令嵌套是可替代反撇号注意!()前面要加$
vim_bao=(which vim))
反撇号和$小括号都是进行命令替换的但是$小括号可以进行命令嵌套
#另外两种写法:只要不冲突就行
vim_bao=$(rpm -qf `which vim`)
vim_bao=rpm -qf $(which vim)
${}:引用变量,用于区分变量名与紧跟其后的字符串
abc=123
echo $abc
bcd=“$abc456”
echo $bcd 这个时候是显示不出来的$abc和456连在一块认成一个变量了
变量名和紧跟其后的字符串混淆了,
这个时候就需要加上${}了
bcd=$”{abc}456”
#有些人不管有没有东西只要是变量 都用这种语法表示
echo ${bcd}
交互赋值:一问一答
比如
read abc 回车 就会等待我们给abc赋值
我输入99999
read=读 从哪读 从键盘读
从键盘读东西出来交给abc
完整语法
read -p “please input value:” abc
自定义变量
设置变量的作用范围
格式1:export 变量名 ...
格式2: export 变量名=变量值 ...
——两种格式可以混合使用
全局变量是变量在当前shell和子shell都可以使用,父shell不可
局部变量 仅在本shell当中生效的 父shell 和子shell 都不生效
如何创建全局变量
export abc=123 在给abc赋值的时候前面加上export
如何给局部变量设置为全局变量
abc=123
export abc 这是给局部变量设置为全局变量
整数变量的运算
格式: 计算的时候运算符前后有空格 也就是加减乘除和数字前后都要有空格
expr 2 \* 3 + 1 – 4 / 2
常用运算符
加法运算:+
减法运算: -
乘法运算: \*
除法运算: /
求模(取余)运算: %
取余就是俩数相除得的余数
环境变量 维护当前系统环境的变量
这些变量往往都是系统设置好的,有可能要读,也有可能要去修改
这些变量要定义在
/etc/profile (全局的对于所有用户都生效)
~/.bash_profile (只对当前用户生效 ~代表当前宿主目录)
常见的环境变量:环境变量默认都是大写的
PWD、PATH
USER 、SHELL、HOME
查看当前用户
echo $USER
查看当前所在的目录
echo $PWD
查看命令搜索路径
echo $PATH
查看当前shell
位置变量
预定义变量
$#:命令行中位置变量的个数
$*:所有位置变量的内容
$?:上一条命令执行后返回的状态,当返回状态值为0时表示执行正常,非0值表示执行异常或出错
$0:当前执行的进程/程序名
写个脚本
vim test1.sh
chmod +x test1.sh
./test1.sh
第一次执行的时候后面没有跟任何东西 得到的结果是
第二次执行的时候 后面跟了a b c d e f 参数用空格隔开
echo $0 第一行是输出了$0的结果a给$0赋值等于 ./test1.sh $0指的脚本名
echo $1 第二行是输出的$1的结果 b给$1赋值 结果 a $1是脚本执行后 后面跟的第一个参数
echo $2 第二行是输出的$2的结果 b 给$2赋值
c 给$3赋值 因为里面没有$3 所以就不管了
echo $4 第二行是输出的$4的结果 d给$4赋值
从$1-$9 他是依靠在执行脚本是后面跟的参数的位置进行赋值 第一个参数给$1赋值第二个参数给$2赋值 一直到$9 没有$10和$11
echo # 最后看到的是参数的个数 一共6个参数 所以显示6
echo $* 是参数的内容 内容是a b c d e f
执行脚本的三种方式
第一种: sh test.sh #不需要执行权限 使用子shell执行
第二种: /server/scripts/test.sh 或者 ./test.sh #需要执行权限
第三种: source test.sh #父shell执行 登录的shell 第一个窗口就是父shell
for在命令行中使用
[root@scripts ~]# for i in 1 2 3; do echo $i;done
1
2
3
shell核心位置环境变量
$0 #表示脚本的名称 全路径执行 显示全路径的脚本名称
$n #表示脚本的底n个参数 n为数字 0已被脚本名占用 从1 开始 从$9后需要加{}引起来作为一个整体 ${}
$# #表示传参的个数 控制传参的个数
$* #表示传参的所有参数 不加双引号和$@相同 加上双引号 表示为一个参数 在脚本中相同 循环体中不同
$@ #表示传参的所有参数 不加双引号和$*相同 加上双引号 表示为正常的传参
$? #表示上一条命令执行结果 0为成功 非0为失败 返回值0-255 可以自定义返回值
$$ #表示当前脚本的pid
只要脚本中出现一次的,我们都使用变量
变量的子串
[root@scripts scripts]# name='i am qichen'
[root@scripts scripts]# echo $name
i am qichen
[root@scripts scripts]# #我要单独取出am来
[root@scripts scripts]# echo ${name:2:2}
am
[root@scripts scripts]# #取出a来
[root@scripts scripts]# echo ${name:2:1}
a
[root@scripts scripts]# #取出c来
[root@scripts scripts]# echo ${name:7:1}
c
统计变量字串的长度
awk的执行过程是一行一行执行
#第一种方式
[root@scripts scripts]# echo $name
i am qichen
[root@scripts scripts]# echo $name | wc -L
11
wc -L #统计文件中最长的行的长度,如果为一行那就统计一行的
#第二种方式
[root@scripts scripts]# echo $name
i am qichen
[root@scripts scripts]# echo ${#name}
11
#第三种方式
[root@scripts scripts]# expr length "$name"
11
#length #长度
#第四种方式
[root@scripts scripts]# echo $name | awk '{print length}'
11
统计出变量中长度小于3的值
[root@scripts scripts]# echo $name
I an fengqichen teacher I am 18
#要求算出长度小于3的值
#方法一
#写脚本bash
[root@scripts scripts]# cat for.sh
#!/bin/bash
name='I am fengqichen teacher I am 18'
for i in $name
do
b=$(echo ${#i})
if [ $b -lt 3 ]
then
echo "${i}"
fi
done
[root@scripts scripts]# sh for.sh
I
am
I
am
18
#还一个更简洁的脚本
[root@scripts scripts]# cat for.sh
#!/bin/bash
name='I am fengqichen teacher I am 18'
for i in $name
do
[ ${#i} -lt 3 ] && echo "${i}"
done
[root@scripts scripts]# sh for.sh
I
am
I
am
18
#另一种方法
[root@scripts scripts]# echo $name
I an fengqichen teacher I am 18
[root@scripts scripts]# echo $name | xargs -n1
I
an
fengqichen
teacher
I
am
18
[root@scripts scripts]# echo $name | xargs -n1 | awk '{print length}'
1
2
10
7
1
2
2
[root@scripts scripts]# echo $name | xargs -n1 | awk '{ if (length<3) print}'
I
an
I
am
18
#if判断如果length得到的长度小于3那么就print显示出来
#第三种方法
#$NF是取出最后一列的内容,NF是取出最后一列的列号,比如
[root@scripts scripts]# cat 1.txt
abc ddd ff gg hh tt
dawd eaf wada esg th
awd 23 436d ytnyt esf
[root@scripts scripts]# cat 1.txt | awk '{print $NF}'
tt
th
esf
[root@scripts scripts]# cat 1.txt | awk '{print NF}'
6
5
5
[root@scripts scripts]# echo $name | awk '{for(i=1;i<=NF;i++)if(length($i)<3)print $i}'
I
an
I
am
18
#解释:
[root@scripts scripts]# echo $name
I an fengqichen teacher I am 18
#首先看
echo $name | awk '{for (i=1;i<=NF;i++)}'
#i=1 i<=NF(NF是最后一列的列号在这里是7) i++
echo $name | awk '{for(i=1;i<=NF;i++) if(length($i)<3)print $i}'
#if判断(length ($i)<3) 如果小于那么就输出print $i}
第一次i=1 然后查看第一列 统计第一列的长度 length 1 真实的第一列是I 他的长度是1 1<3 那么输出
第二次i=2 然后查看第二列 统计第二列的长度 length 2 真实的第二列是 an 他的长度是2 2<3 那么输出
第三次i=3 然后查看第三列 统计第三列的长度 length 3 真实的第三列是 fengqichen 他的长度是10 10不<3 那么不输出
第四次i=4 然后查看第四列 统计第四列的长度 length 4 真实的第四列是 teacher 他的长度是7 7不<3 那么不输出
第五次i=5 然后查看第五列 统计第五列的长度 length 5 真实的第五列是 I 他的长度是1 1<3 那么输出
以此类推 直到i=7 结束后 中止循环