Shell脚本

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Shell脚本相关的知识,希望对你有一定的参考价值。

Shell脚本编程

文本常用处理工具1(不修改原文件)

grep

grep过滤包含关键字的行

#语法
grep [选项] "关键字" 文件名
#常用选项
    -i  不区分大小写
    -v  查找不包含指定内容的行,反向选择
    -w  按单词搜索
    -o  只打印匹配到的关键字
    -c  统计匹配到的次数
    -n  显示包含关键字的行号
    -r  逐层遍历目录查找
    -l  只列出匹配的文件名
    -e  使用正则匹配
    -A  显示匹配行及后面多少行
    -B  显示匹配行及前面多少行
    -C  显示匹配前后多少行
    -L  列出不匹配的文件名
    -E  使用扩展正则匹配
    ^key    以关键字开头
    key$    以关键字结尾
    ^$  匹配空行
    --color=auto    给找到的关键字加注颜色

#添加别名到全局bashrc
echo "alias grep=grep --color=auto" >> /etc/bashrc
source /etc/bashrc

cut

cut是列的截取

#语法
cut 选项 文件名
#常见选项
    -c  以字符为单位进行分割、截取
    -d  自定义分隔符,默认为制表符tab键
    -f  与-d一起使用,指定截取哪个区域

#例,打印以:分隔符第1列和第7列的行
cut -d ":" -f1,7 /etc/passwd
#例,打印以:分隔符第1列到第5列的行
cut -d ":" -f1-5 /etc/passwd
#例,打印以:分隔符第2列到最后所有的列
cut -d ":" -f2- /etc/passwd

sort

sort用于排序,它将文件的每一行为一个单位,从首字母向后,依次按ASCII码值进行比较,最后将他们按升序输出

#语法
sort 选项 文件
#常用选项
    -u  去除重复行
    -r  降序排列,默认是升序
  -o    将排序结果输出到文件中,类似重定向符号>
  -n    已数字排序,默认是按字符排序
  -t    分隔符
  -k    第N列
  -b    忽略前导空格
  -R    随机排序,每次运行结果均不相同

#例
sort -n -t: -k3 /etc/passwd

uniq

uniq用于去除连续的重复行

#语法
uniq 选项 文件
#常用选项
    -i  忽略大小写
    -c  统计重复行次数
    -d  只显示重复行

tee

tee是从标准输入读取并写入到标准输出和文件,即双向覆盖重定向(从屏幕输出|文本输入)

#常用选项
-a  双向追加重定向,默认是覆盖重定向

#例
echo "hello world" | tee 1.txt

diff

diff用于逐行比较文件的不同。diff描述两个文件不同的方式是告诉我们怎样改变第一个文件之后与第二个文件匹配

#语法
diff 选项 文件1 文件2
#常用选项
    -b  不检查空格
    -B  不检查空白行
    -i  不检查大小写
    -W  忽略所有空格
    --normal    正常显示(默认)
    -c  上下文格式显示
    -u  合并格式显示

#patch工具

paste

paste用于合并文件行

#常用选项
    -d  自定义分隔符,默认是tab
    -s  串行处理,非并行

tr

tr用于字符转换,替换和删除;主要用于删除文件中的控制字符或进行字符转换,一对一的字符替换。

#语法
 tr 选项 "字符串1"   "字符串2" < 文件
 commands | tr 选项 "字符串1"    "字符串2"
#常用选项
    -d  删除字符串1中所有输入字符
    -s  删除所有重复出现字符序列,只保留1个,即将重复出现的字符串压缩为一个字符串

#匹配字符串1
a-z
A-Z
0-9
控制字符
...

文本处理处理工具2(可修改原文件)

sed

sed简称流编辑器,是用来处理文件的

sed一行一行读取文件内容,按照要求处理,把处理后的结果输出到屏幕

sed把每一行都存在临时缓冲区,对这个副本进行编辑,所以不会直接修改原文件

sed主要用来自动编辑一个或多个文件,简化对文件的反复操作,对文件进行过滤和转换操作

sed常用语法格式:1.命令行模式 2.脚本模式

sed中的正则表达式用/ /包在当中

命令行格式

#语法格式
sed [选项] ‘处理动作‘ 文件名
#选项
-e      进行多次编辑
-n      取消默认输出
-r      使用扩展正则表达式
-i      修改原文件
-f      指定sed脚本的文件名

#处理动作
‘p‘     打印缓冲空间和原文件
‘i‘     在指定行之前插入内容
‘a‘     在指定行之后插入内容
‘c‘     替换指定行所有内容,整行替换
‘d‘     删除指定行
‘s‘     搜索
‘r‘     从另一个文件读取内容
‘w‘     保存到另一个文件
‘^‘     表示行首
‘$‘     表示最后一行

#例
#打印
sed -n ‘2p‘ a.txt           打印第2行
sed -n ‘2,5p‘ a.txt     打印第2到5行
sed -n ‘$p‘ a.txt           打印第最后1行

#增加
sed ‘2ihello‘              在第2行前插入hello

sed ‘3i>hello>world>11223>235‘ a.txt                 在第3行前插入多行

sed ‘ihello‘               在每行前都插入hello

sed ‘2,4atext‘     处理2到4行

#替换
sed ‘$chello world‘ 替换最后一行为hello world

#删除
sed ‘4d‘                        删除第4行
sed ‘1,4d‘                  删除1到4行

#搜索s
sed -n ‘s/搜索内容/替换的内容/动作‘
动作一般为p打印,或者g全局替换

sed -n ‘s/^#//gp‘       替换以#开头的#为空并打印

sed -n ‘1,5s/^/#/gp‘ 在1到5行行首添加#

sed -n ‘s/(10.1.1.)1/1254/gp‘ 替换10.1.1.1为10.1.1.254,\之间的内容会当作一个整体,1代表\之间的内容

脚本格式

# 每行末尾不能有任何空格,制表符tab,或其他文本
# 如果一行存在多个命令,用分号;分隔
# 不可用引号‘‘保护命令
# #开头的为注释
#!/bin/sed -f
1,5d
s/root/hello/g
3i44
5ava
a233
p

awk

awk是一种编程语言,主要用于在linux/unix下对文本和数据的处理,数据可以来自标准输入、一个或多个文件、或其他命令的输出

awk处理文本和数据的方式:逐行扫描文件

awk可以用来统计数据,例如网站访问量,访问量的IP量等

awk支持条件判断和循环语句

#语法结构
awk 选项 ‘命令‘ 文件名
#引用shell变量需要用双引号""引起来,sed也一样的用法。
#选项
-F      定义分割符,默认分割符是空格
-v      定义变量并赋值
#命令
#1.正则表达式,地址定位

#2.{awk语句1;awk语句2;...}

#3.BEGIN...END...

#awk内部变量
$0                                      当前处理行的所有记录
$1,$2,$3,...$n              文件中每行以分隔符号分隔不同的字段
NF                                      当前记录的字段数
$NF                                     最后一列
FNR/NR                              行号
FS                                      定义间隔符
OFS                                     定义输出字段分割符,默认空格
RS                                      输入记录分割符,默认换行
ORS                                     输出记录分割符,默认换行
FILENAME                            当前输入的文件名

#例
awk ‘NR==1,NR==5{print $0}‘      打印1到5行的内容
awk ‘NR==1 || NR==5{print $0}‘ 打印第1行或第5行的内容
awk ‘NR==1 && NR==5{print $0}‘ 打印第1行和第5行的内容

awk -F: ‘{print $1,$(NF-1)}‘    打印冒号:分隔的第1列和倒数第2列

awk -F: ‘BEGIN{OFS="@@"};{print $1,$NF}‘    输出时以@@分隔

awk -F: ‘BEGIN{FS=""};{print $1"****"$NF}‘  以****分隔

#格式化输出print和printf
#print函数类似echo换行输出
awk -F: ‘{print "username is: " $1 "	 uid is: "$3}‘ /etc/passwd
#printf函数类似echo -n不换行输出

bash特性

通配符

* 匹配0或多个任意字符

? 匹配任意单个字符

[list] 匹配list中的任意单个字符

[!list] 匹配除list中的任意单个字符

{string1,string2,...} 匹配string1,string2或更多字符串

{1..5}

{a..e}

bash中的引号

双引号" " 会把引号中的内容当作整体来看待,允许通过$符号引用其他变量值

单引号‘ ‘ 会把引号中的内容当作整体来看待,禁止引用其他变量值,shell中特殊符号都被视为普通字符

反撇号`` 反撇号与$()作用一样,引号或括号里的命令会优先执行,如果存在嵌套,反撇号不能用,只能用$()

shell编程

shell基本写法

  • 脚本第一行

    #!/bin/env bash

  • 脚本第二行

    # 注释说明

  • 脚本第三行

    shell脚本

脚本执行方法
  • 标准脚本执行方法

    给shell增加执行权限,使用绝对路径执行

  • 非标准脚本执行方法

    1.直接在解释器执行

    ? -x 用于排错,查看脚本执行过程

    ? -n 用来查看脚本语法是否有问题

    2.使用source命令读取脚本文件,执行文件里的代码

变量的定义

变量名=变量值

unset 变量名               #取消变量

变量定义规则

1.变量名区分大小写(一般使用大写定义变量名)

2.变量名不能包含特殊符号

3.变量名不能以数字开头

4.等号两边不能有任何空格

5.变量名尽量做到见名知意

变量定义方式

基本方式

直接赋值给变量名

#例
A=123456
echo $A
echo $(A:2:3)               #表示从变量A中从第3个字符开始截取,截取3个字符

#说明
$变量名和${变量名}
相同点:都可以调用变量
不同点:${变量名}可以只截取变量的一部分,而$变量名不可以
将命令执行结果赋值给变量
#例
B=`date +%F`
echo $B
C=$(uname -r)
echo $C
交互式定义变量(read)

让用户自己给变量赋值,比较灵活

#语法
read 选项 变量名

#常用选项
    -p  定义提示用户的信息
    -n  定义字符数(限制变量值的长度)
    -s  不显示(不显示用户输入的内容)
    -t  定义超时时间,默认单位为秒(限制用户输入变量值的超时时间)

#例
#让用户自己定义变量
read -p "提示信息" 变量名
#变量值来自文件,不会提示输入变量
read -p "提示信息" 变量名 < 文件
定义有类型的变量(declare)

给变量做一些限制,固定变量类型,例如:整型、只读

#语法
declare 选型 变量名=变量值
#常用选项
    -i  将变量当作整数
    -r  定义只读变量,unset不能取消,可以退出当前终端,重新进入
    -a  定义普通数组;查看普通数组
    -A  定义关联数组;查看关联数组
    -x  将变量通过环境导出
变量的分类

本地变量

本地变量:当前用户自定义的变量。当前进程中有效,其他进程及当前进程的子进程无效。

环境变量

环境变量:当前进程有效,并且能够被子进程调用

? env 查看当前用户环境变量

? set 查询当前用户的所有变量(临时变量和环境变量)

? export 变量名=变量值 或 变量名=变量值;export 变量名

全局变量

全局变量:所有的用户和程序都能调用且继承,新建的用户默认也能调用

配置文件

文件名 说明 作用
$HOME/.bashrc 当前用户bash信息,用户登陆时读取 定义别名、函数、umask等
$HOME/.bash_profile 当前用户环境变量信息,用户登陆时读取
$HOME/.bash_logout 当前用户退出shell时最后读取 定义用户退出时执行的程序等
/etc/bashrc 全局bash信息,所有用户都生效
/etc/profile 全局环境变量信息 系统和所有用户都生效

以上文件修改后,都需重新source或注销重新登录使其生效

系统变量

#变量说明
$?  上条命令执行后返回的状态;0为正常,非0为执行异常或错误
$0  当前执行的程序或脚本名
$#  显示脚本后面接的参数的个数
$*  脚本后面所有的参数,当成一个整体输出
$@  脚本后面所有参数,参数是独立的,也是全部输出
$1-$9   脚本后面的位置参数,$1表示第1个参数,依此类推
${10}/${n}  扩展位置参数,第10个位置变量必须用{}括起来,两位数字以上的括起来
$$  当前所在进程的进程号,如echo $$
$!  后台运行的最后一个进程号(当前终端)
!$  调用最后1条命令历史的参数
其他变量定义
#取出一个目录下的目录和文件:dirname和basename
#例
A=/tmp/shell/file1.txt
dirname $A
/tmp/shell/
basename $A
file1.txt

#变量内容删除和替换
一个"%"代表从右往左去掉一个/key/
两个"%%"代表从右往左最大去掉/key/
一个"#"代表从左往右去掉一个/key/
两个"##"代表从左往右最大去掉/key/
#例
url=www.taobao.com
echo ${#url}                #获取变量长度
14

echo ${url#*.}
www.taobao

echo ${url##*.}
www

echo ${url%.*}
taobao.com

echo ${url%%.*}
com

四则运算

默认情况下,shell只支持简单的整数运算

运算内容:加(+)、减(-)、乘(*)、除(/)、求余数(%)

四则运算符号

表达式 举例
$(( )) echo $((1+1))(加法);echo $((1-1))(减法)
$[ ] echo $[10-5](减法);echo $[5*6](乘法)
expr expr 10/5(求商);expr 10%8(取余)
let n=1; let n+=1 等价于 let n=n+1

bc 计算小数

echo 1+1.5 |bc

了解i++和++i

对变量值的影响

对表达式的值的影响

i++ 先赋值在运算

++i 先运算在赋值

条件判断语法

语法格式

  • 格式1:test条件表达式
  • 格式2:[ 条件表达式 ]
  • 格式3:[ 条件表达式 ] 支持正则 = -

<u>[ 条件表达式两边有空格 ]</u>

#文件类型
-e  判断文件是否存在
-f  判断文件是否存在并且是一个普通文件
-d  判断目录是否存在
-L  判断软连接文件是否存在
-b  判断块设备文件见是否存在
-S  判断套接字文件是否存在
-c  判断字符设备文件是否存在
-p  判断命名管道文件是否存在

# !取假值

#文件权限
-r  
-w
-x
-u  判断是否有suid
-g  判断是否有sgid
-k  判断是否有t为,高级权限粘滞位

#比较文件新旧
file1 -nt file2 比较file1是否比file2新
file1   -ot file2   比较file1是否比file2旧
file1 -ef   file2   比较是否为同一个文件,或者用于判断硬连接,是否指向同一个inode

#判断整数
-eq     等于
-ne     不等于
-gt     大于
-lt     小于
-ge     大于等于
-le     小于等于

#判断字符串
-z  判断是否为空
-n  判断是否为非空
=       判断字符串是否相等
!=  判断字符串是否不相等

#多重条件判断
-a和&&       逻辑与     两边条件都满足才为真,否则为假
-o和||       逻辑或     两边条件只要有一个为真,就为真

#类C风格的数值比较
#在(( ))中,=表示赋值;==表示判断
#例
((1==2));echo $?
((1<2));echo $?
((a=123));echo $a
((a==123));echo $a

#字符串判断
#双引号引起来,看作一个整体;=和==在[ 字符串 ]比较中都表示判断
#例
[ "abc" = "cde" ];echo $?
[ "abc" == "abc" ];echo $?

#[]与[[]]比较,当判断一个字符串为空时,[[]]内字符串可以不加""

流程控制语句

基本语法结构

if结构

只要为真就一直执行

F:表示false,为假

T:表示true,为真

#语法
if [ 条件判断 ];then
        command执行命令
        command执行命令
fi

if test 条件判断;then
        command执行命令
        command执行命令
fi

...

if...else结构

分叉路口二选一

if [ 条件判断 ];then
        command1执行命令
    else
        command2执行命令
fi

if test 条件判断;then
        command1执行命令
    else
        command2执行命令
fi

...

if...elif...else结构

如果...否则...否则...否则

if [ 条件判断 ];then
        command执行命令
    elif [ 条件判断 ];then
        command执行命令
    elif [ 条件判断 ];then
        command执行命令
    elif
    ...
    else [ 条件判断 ];then
        command执行命令
fi

...

层层嵌套结构

if [ 条件判断 ];then
        command执行命令
        if [ 条件判断 ];then
        command执行命令
        fi
    else [ 条件判断 ];then
        command执行命令
        if [ 条件判断 ];then
                command执行命令
            elif [ 条件判断 ];then
                command执行命令 
            elif
            ...
            else [ 条件判断 ];then
                command执行命令
        fi
fi
for循环语句

列表for循环:用于将一组命令执行已知的次数,次数由列表决定

#for语法格式
for 变量名 in {列表}
        do
            command
            command
done

#例
for i in {1..5}
        do
            echo $i
done
[root@server1 ~]# sh for1.sh 
1
2
3
4
5

#例打印偶数
#!/bin/bash
for i in {0..20..2}  #..2为分隔几位数
    do
        echo $i
done

[root@server1 ~]# seq 5   #打印出1到5的所有数字

[root@server1 ~]# seq 10 -1 1
[root@server1 ~]# seq 10 -1 1
10
9
8
7
6
5
4
3
2
1

不带列表的for循环,执行时由用户指定参数和参数个数

#语法格式
for 变量名
        do
            command
            command
done

[root@server1 ~]# sh for2.sh a b c

类C风格的for循环

#语法格式
for (( 表达式1;表达式2;表达式3 ))
        do
            command
            command
            ...
done

表达式1:定义变量并赋初始值
表达式2:定义是否进行循环(条件)
表达式3:决定循环变量如何改变,决定什么时候退出

#例
for (( i=1;i<=5;i++ ))
        do
            echo $i
done

[root@server1 ~]# for (( i=1;i<=5;i++ )); do echo $i; done 
1
2
3
4
5

#循环控制语句
continue    #跳过本次循环,继续执行循环体
break           #打断循环,跳出循环,执行当前所在循环体后的命令
exit            #打断循环,直接退出程序或脚本
shift           #使脚本后的参数向左移动,默认1位,可使用shift 2

#质数(素数):只能被1和它本身整除的数叫质数

shell脚本并发执行

#并行执行
{程序}&       #表示将程序放到后台执行,如果需要等待程序执行完毕再进行下面内容,需要在循环体外加wait
while循环语句

while循环:条件为真就进入循环,条件为假就退出循环

#语法结构
while 表达式
        do
            command
done
until循环

until循环:条件为假就进入循环,条件为真就退出循环

#语法结构
until 表达式   
        do
            command
            command
            ...
done
case语句

case语句位多重匹配语句,如果匹配成功,执行相匹配的命令

语法结构

case 变量名 in
    pattern 1)                  #pattern用|分隔多个模式,相当于or
        command1
        ;;
    pattern 2)
        command2
        ;;
   ...
                    *)                  #默认不满足以上条件,匹配此模式
        command
        ;;
esac

#脚本交互式输入
#例
cat <<-EOF
    文本1
    文本2
    ...
EOF
函数

shell中允许将一组命令集合或语句形成一段可用代码,这些代码成为shell函数

给这段代码起个名字称之为函数名,后续可以直接调用该代码的功能

#定义函数/函数语法
#方法一
函数名()
{
    函数体(一堆命令的集合,来实现某个功能)
}

#方法二
function 函数名()
{
    函数体(一堆命令的集合,来实现某个功能)
}

#return说明
1.return可以结束一个函数。类似循环语句中的break,结束当前循环,执行循环体之后的代码
2.return默认返回函数中最后一个命令的状态值,也可以给定参数值,范围时0~256之间
3.如果没有return命令,函数将返回最后一个指令的退出状态值

#函数调用
1.临时调用,当前命令行直接执行函数名调用
2.将函数添加到用户环境变量/全局变量中
3.脚本直接调用函数名

#屏蔽信号,例如contrl+c、contrl+z等,可用kill -l查看对应的数字
trap :  1 2 3 19 20
随机数

系统变量RANDOM,默认产生0-32767的随机整数

#查看系统上一次生成的随机数
set | grep RANDOM

#产生0~1之间的随机数
echo $[$RANDOM%2]       #取余数
#产生0~2之间的随机数
echo $[$RANDOM%3]           #取余数
#产生0~3之间的随机数
echo $[$RANDOM%4]           #取余数
...
#产生0~100之间的随机数
echo $[$RANDOM%101]     #取余数

#产生50~100之间的随机数
echo $[$RANDOM%51+50]   #取余数+50,$RANDOM%51产生0-50的随机数再+50,变为50-100的随机数

#产生三位数的随机数(100~999)
echo $[$RANDOM%900+100]
expect自动应答

expect自动应答,tcl语言,需安装软件包。

#语法

#环境
#!/usr/bin/env expect
#开启一个程序
spawn ssh root@10.1.1.1
#捕获相关内容
expect {
        "(yes/no)?" { send "yes/r";exp_continue }
        "password:" { send "123456
" }
}
interact        #交互

#定义变量
set 变量名 变量值
普通数组

赋值

一次赋予一个值,索引下标位从0开始,从左到右

数组名[索引下标]=值

array[0]=v1

array[1]=v2

一次赋予多个值

数组名=(值1 值2 值3 ...)

array=(var1 var2 var3 var4 ...)

取值

${数组名[索引下标]}

declare -a #查看所有普通数组

echo ${array[2]} #查看此数组的单个元素

echo ${array[*]} #查看此数组所有元素

echo ${array[*]:1:3} #查看此数组第1-3个元素

echo ${#array[*]} #查看此数组的所有元素个数

echo ${!array[@]} #查看此数组的索引下标

关联数组

首先声明关联数组

declare -A asso_array1

declare -A asso_array2

赋值

一次赋予一个值,索引名/下标名可自定义

数组名[索引/下标]=变量值

asso_array1[linux]=one

asso_array2[java]=two

一次赋予多个值

asso_array2=([索引名linux]=harry [索引名java]=jack ...)

取值

declare -A #查看所有关联数组

echo ${asso_array1[*]} #查看此数组所有元素

...

正则表达式

正则表达式(Regular Expression、regex或regexp,缩写为RE),也译为正规表示法、常规表示法,是一种字符模式,用于在查找过程中匹配指定的字符

支持正则表达式的程序:locate|find|vim|grep|sed|awk

正则用处:

匹配邮箱、身份证号、手机号等

匹配特定字符串、特定处理等

元字符:指那些在正则表达式中具有特殊意义的专用字符,例如:点.星号*问号?等

前导字符:位于元字符前面的字符,例如abc*中的c,aoo.中的o属于前导字符

第1类正则表达式

#正则中普通的常用元字符
.               匹配除了换行符以外的任意单个字符
*               前导字符出现0次或连续多次
.*          任意长度字符
^               以...开头
$               以...结尾
^$          空行
[]          匹配括号里任意单个字符或一组单个字符
[^]         匹配不包含括号里任意单个字符或一组单个字符
^[]         匹配以括号里任意单个字符或一组单个字符开头
^[^]        匹配不以括号里任意单个字符或一组单个字符开头

#正则中其他常用元字符
<              取单词的头
>              取单词的尾
< >           精确匹配
{n}           匹配前导字符连续出现n次
{n,}      匹配前导字符至少出现n次
{n,m}     匹配前导字符出现n次与m次之间
( )           保存被匹配的字符
d              匹配数字(grep -P)
w              匹配字母数字下划线(grep -P)
s              匹配空格、制表符、换页符(grep -P)

#扩展正则常用元字符
+                   匹配一个或多个前导字符
?                   匹配零个或一个前导字符
|                   或
()              组字符(看成整体)
{n}             前导字符重复n次
{n,}            前导字符重复至少n次
{n,m}           前导字符重复n次到m次

第2类正则表达式

#tr帮助命令也可以找到
#和其他正则连用需加[],例如[[:alnum:]]?
[:alnum:]       字母与数字字符
[:alpha:]       字母字符(包括大小写字母)
[:blank:]       空格与制表符
[:digit:]       数字
[:lower:]       小写字母
[:upper:]       大写字母
[:punct:]       标点符号
[:space:]       包括换行符,回车符等在内的所有空白

正则表达式总结

1.找什么?

数字 [0-9]

字母 [a-zA-Z]

标点符号 [[:punct:]]

2.怎么找?

以什么开头 ^key

以什么结尾 key$

包含什么或不包含什么 [abc] ^[abc] [^abc] ^[^abc]

3.找多少?

前导字符出现0次或连续多次 ab*

任意单个字符 ab.

任意字符 ab.*

前导字符连续出现n次 {n} {n,m} {n,}

前导字符出现一次或多次 ab+

前导字符出现零次或一次 ab?

以上是关于Shell脚本的主要内容,如果未能解决你的问题,请参考以下文章

用于确保在任何给定时间仅运行一个 shell 脚本的 shell 片段 [重复]

常用python日期日志获取内容循环的代码片段

shell脚本引用expect

Shell脚本切割日志

Eclipse 中的通用代码片段或模板

Python如何调用别人写好的脚本