Shell脚本编程
时间:2023-02-16 20:00:01
Shell脚本编程
- 一. SHELL入门
-
- 1.1 变量
-
-
- 1.1.1 变量名规范
- 1.1.2 位置变量
- 1.1.3 环境变量
-
- 1.2 管道
- 1.3 退出状态码
-
-
- 1.3.1 退出状态码描述
- 1.3.2 更改退出状态码exit命令
-
- 二. 判断与控制
-
- 2.1 if-then 语句
- 2.2 if-then-else 语句
- 2.3 嵌套if语句
- 2.4 条件测试-数值比较
- 2.5 条件测试-字符串比较
- 2.6 条件测试-文件比较
- 2.7 符合条件测试
- 2.8 if-then使用双括号
- 2.9 if-then使用双方括号
- 2.10 case命令
- 三. 循环与控制
-
- 3.1 for循环
- 3.2 while循环
- 3.3 until命令
- 3.4 break命令
- 3.5 continue命令
- 3.6 处理循环输出
- 四. 高级变量用法
-
- 4.1 变量替换和测试
- 4.2 字符串处理
- 4.3 命令替换
- 4.4 有类型变量
- 4.5 bash数学运算
-
-
- 4.5.1 bash数学运算之expr
- 4.5.2 bash数学运算之bc
-
- 五. 函数的高级用法
-
- 5.1 定义和使用函数
- 5.2 将参数传递给函数
- 5.3 函数返回值
- 5.4 局部变量和全局变量
- 5.5 函数库
- 六. 文件查找
-
- 6.1 find命令
- 6.2 find、locate、whereis和which总结和应用场景分析
- 七. 三剑客之一的文件处理grep
-
- 7.1 grep命令
-
-
- 7.1.1 语法格式
- 7.1.2 grep参数
- 7.1.3 grep和egrep
-
- 7.2 三剑客之一的文件处理sed
-
-
- 7.2.1 sed工作模式
- 7.2.2 sed选项
- 7.2.3 sed中的pattern详解
- 7.2.4 sed详细说明编辑命令
- 7.2.5 反向引用
- 7.2.6 利用sed查找文件内容
- 7.2.7 利用sed删除文件内容
- 7.2.8 利用sed修改文件内容
- 7.2.9 利用sed添加文件内容
-
- 7.3 三剑客之一的文本处理awk
-
-
- 7.3.1 awk工作模式
- 7.3.2 awk内置变量
- 7.3.3 awk格式输出printf
- 7.3.4 awk模式匹配的两种用法
- 7.3.5 awk表达式用法
- 7.3.6 awk动作中的条件和循环句子
- 7.3.7 awk中间的字符串函数
- 7.3.8 awk的常用选项
- 7.3.9 awk使用中数组
- 7.3.10 实例
-
- 八. Shell实战脚本操作数据库
-
- 8.1 导入测试数据
-
-
- 8.1.1 载入数据
- 8.1.2 赋予用户数据库权限
-
- 8.2 Shell脚本与MySQL数据库交互
-
-
- 8.2.1 mysql详细说明命令参数:
- 8.2.2 实例
-
- 8.3 利用Shell脚本导入文本数据MySQL中
- 8.4 备份MySQL并通过数据FTP将其传输到远端主机
-
-
- 8.4.1 mysqldump常用参数详解:
- 8.4.2 FTP常用指令
- 8.4.3 需求
-
- 九. 大型脚本工具开发实战
-
- 9.1 需求描述:
- 9.2 功能函数代码
- 9.3 程序主流程设计和代码实现
一. SHELL入门
1.1 变量
- 变量是由任何字母、数字和下划线组成的字符串,不能从数字开始。
- 区分字母大小写
- 任何空间都不能出现在变量、等号和值之间
实例
注:$ var1 $ var2不能实现相加,$ var1,$ var默认情况下,2作为字符串处理
在执行命令或脚本时,我们可以使用位置参数变量来表示这些参数。
位置参数变量 | 描述 |
---|---|
$n | n 为数字,$ 0 代表Shell本身的文件名,$ 1?$ 9 代表第 1 ~ 9 个参数,10 大括号应包含上述参数, 如 $ {10} |
$* | 命令行的所有参数都被视为一个整体 |
$@ | 命令行中的所有参数都不同,但每个参数都不同 |
$# | 命令行中所有参数的数量 |
实例
$ @和$ *都表示命令行所有参数,但是$ *将命令行的所有参数看成一个整体,而$ @则区分各个参数
环境变量是在操作系统中一个具有特定名字的对象,它包含了一个或多个应用程序将使用到的信息。Linux是一个多用户的操作系统,每个用户登录系统时都会有一个专用的运行环境,通常情况下每个用户的默认的环境都是相同的。这个默认环境就是一组环境变量的定义。每个用户都可以通过修改环境变量的方式对自己的运行环境进行配置。
分类
-
对所有用户生效的环境变量 /etc/profile
-
对特定用户生效的环境变量 ~ /.bashrc或者~/.bash_profile
-
临时有效的环境变量 export
常用环境变量
环境变量 | 含义 |
---|---|
PATH | 命令搜索的路径 |
HOME | 用户家目录的路径 |
LOGNAME | 用户登录名 |
PWD | 当前所在路径 |
HISTFILE | 历史命令的保存文件 |
HISTSIZE | 历史命令保存的最大行数 |
HOSTNAME | 主机名 |
SHELL | 用户当前使用的SHELL |
PS1 | 一级命令提示符 |
TMOUT | 用户和系统交互过程的超时值 |
IFS | 系统输入分隔符 |
OFS | 系统输出分隔符 |
实例
1.2 管道
将一个命令的输出作为另一个命令的输入
实例
- 列出所有安装过的软件包
rpm -qa
rpm命令是RPM软件包的管理工具
- 1列出与python相关的软件包
rpm -qa | grep python
Linux grep 命令用于查找文件里符合条件的字符串。
-a :将 binary 文件以 text 文件的方式搜寻数据
-c :打印匹配次数
-f 两个文件相同行 如,grep -f file1 file2
-h 不显示匹配行所在文件名
-i :忽略大小写的不同,所以大小写视为相同
-l 只显示匹配到的文件名
-n :顺便输出行号
-o 只显示匹配的字符
-v :只显示不匹配的行。
--color=auto :可以将找到的关键词部分加上颜色的显示喔!
- 显示与python相关的软件包的行数
rpm -qa | grep python | wc -l
Linux wc命令用于计算字数
-c或--bytes或--chars 只显示Bytes数。
-l或--lines 只显示行数。
-w或--words 只显示字数。
--help 在线帮助。
--version 显示版本信息。
- 1
1.3 退出状态码
所有的shell命令都使用退出状态吗来告知shell它已执行完毕
退出状态码是一个0~255的整数值
Linux提供了一个$?来捕获退出状态码的值,所以,可以通过echo语句测试命令是否运行成功。
一般来讲,退出状态 0 表示正常执行,没有遭遇任何问题。其他非零的状态码均表示失败。
状态码 | 描述 |
---|---|
0 | 命令成功结束 |
1 | 一般性未知错误 |
2 | 不适合的shell 命令 |
126 | 找到了该命令但无法执行 |
127 | 没找到命令 |
128 | 无效的退出参数 |
128+x | 与linux信号x相关的严重错误 |
130 | 通过ctrl+C终止的命令 |
255 | 正常范围之外的退出状态码 |
实例
退出状态码是以上一条指令的返回结果为准
使用exit命令改变退出状态码
exit $exit_code
exit 48 或者 exit 125 或者exit 0
- 1
二. 判断与控制
2.1 if-then 语句
语法:
if command | condition
then
commands
fi
- 1
实例
#!/bin/bash
if pwd
then
echo "It works"
fi
- 1
2.2 if-then-else 语句
语法:
if command | condition
then
commands
else
commands
fi
- 1实例
#!/bin/bash
if pwd
then
echo "It works"
fi
2.3 嵌套if语句
语法:
if command | condition
then
commands
elif command | condition
then
commands
else
commands
fi
- 1
2.4 条件测试-数值比较
语法:
if condition
then
commands
fi
或者
if condition;then
commands
fi
- 1
数值比较
数值比较 | 含义 |
---|---|
n1 -eq n2 | n1和n2相等,则返回true;否则返回false |
n1 -ne n2 | n1和n2不相等,则返回true;否则返回false |
n1 -gt n2 | n1大于n2,则返回true;否则返回false |
n1 -ge n2 | n1大于等于n2,则返回true;否则返回false |
n1 -lt n2 | n1小于n2,则返回true;否则返回false |
n1 -le n2 | n1小于等于n2,则返回true;否则返回false |
实例
#!/bin/bash
if [ $1 -eq $2 ];then
echo "$1 = $2"
elif [ $1 -gt $2 ];then
echo "$1 > $2"
elif [ $1 -lt $2 ];then
echo "$1 < $2"
fi
- 1
2.5 条件测试-字符串比较
字符串比较
字符串比较 | 含义 |
---|---|
str1 = str2 | 相等比较 |
str1 != str2 | 不相等比较 |
str1 < str2 | str1小于str2为true |
str1 > str2 | str1大于str2为true |
-n str1 | str1长度不是0则为true |
-z str1 | str1长度是0则为true |
实例
#!/bin/bash
var1='hello'
var2='world'
if [ $var1 = $var2 ];then
echo "equal"
else
echo "not equal"
fi
- 1
#!/bin/bash
var1='hello'
var2='world'
if [ $var1 < $var2 ];then
echo "<"
else
echo ">"
fi
- 1
<被当作了重定向,需要进行转义
#!/bin/bash
var1='hello'var2='world'
if [ $var1 \< $var2 ];then
echo "<"
else
echo ">"
fi
- 1
#!/bin/bash
var1=''
var2='world'
if [ -n "$var1" ];then
echo "var1 is not null"
else
echo "var1 is null"
fi
- 1
2.6 条件测试-文件比较
文件比较
文件比较 | 含义 |
---|---|
-d file | file是否为目录 |
-f file | file是否为文件 |
-e file | file是否存在 |
-r file | file是否可读 |
-w file | file是否可写 |
-x file | file是否可执行 |
-s file | file存在且非空 |
file1 -nt file2 | file1比file2新为true |
file1 -ot file2 | file1比file2旧为true |
实例
#!/bin/bash
if [ -d /usr/local ];then
echo "yes"
else
echo "no"
fi
- 1
#!/bin/bash
if [ /usr/local/test1 -nt /usr/local/test2 ];then
echo "new"
else
echo "old"
fi
- 1
2.7 符合条件测试
语法:
语法1 “与”
if condition1 && condition2
then
commands
fi
语法2 “或”
if condition1 || condition2
then
commands
fi
- 1
实例
#!/bin/bash
var1=56
var2=34
var3=89
if [ $var1 -gt $var2 ] && [ $var2 -lt $var3 ];then
echo " $var1 > $var2 并且 $var2 < $var3"
fi
- 1
2.8 if-then中使用双括号
使用双括号进行算术运算
语法:
if ((expression))
then
commands
fi
- 1
注:
-
双括号结构中,变量名引用可以加$,也可以不加
-
运算符前后可以有空格,也可以没有
-
可以用于if,for,while等循环控制结构中
-
多个运算符使用逗号分隔
可用运算符
运算符 | 含义 |
---|---|
value++ | 后增 |
value– | 后减 |
++value | 先增 |
–value | 先减 |
! | 逻辑求反 |
== | 相等 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
&& | 逻辑与 |
实例
#!/bin/bash
a=10
((a+=10))
echo $a
- 1
2.9 if-then中使用双方括号
单方括号语法
if [ $n1 -gt $n2 ] && [ $n2 -lt $n3 ]
then
commands
fi
改为双方括号语法
if [ [$n1 -gt $n2 && $n2 -lt $n3 ]]
then
commands
fi
注:
双括号结构中,变量名引用必须加$
[[后面必须要空格,]]前面必须有空格
- 1
2.10 case命令
语法:
case $var in
pattern1)
commands
;;
pattern2)
commands;
;;
esac
- 1
实例:
#!/bin/bash
case $1 in
jack)
echo "hello,jack"
;;
mike)
echo "hello,mike"
;;
*)
echo "hello everyone"
;;
esac
- 1
三. 循环与控制
3.1 for循环
循环遍历一系列特定值,然后在结构体中针对每个特定值做处理
语法:
for var in list
do
commands
done
- 1
实例:
for循环读取列表值:
#!/bin/bash
for i in Beijing Shanghai Nanjing Guangzhou Zhengzhou
do
echo "Province is $i"
done
for i in {1..20}
do
echo "Number is $i"
done
- 1
for循环读取变量值:
#!/bin/bash
list="Zhangsan Lisi Mike Tom"
for i in $list
do
echo "Name is $i"
done
#!/bin/bash
IFS=$":" #分隔符,默认为空格或tab
list="Zhangsan:Lisi:Mike:Tom"
for i in $list
do
echo "Name is $i"
done
- 1
for循环从命令执行结果读取值
#!/bin/bash
FILE=$(ls /opt/)
for i in $FILE
do
echo "file is $i"
done
- 1
#!/bin/bash
for i in $(ls /opt/)
do
if [ -d /opt/$i ]
then
echo "$i is DIR"
elif [ -f /opt/$i ]
then
echo "$i is FILE"
fi
done
- 1
C语言风格的for循环
#!/bin/bash
for ((i=10;i<20;i++))
do
echo "Next Number is $i"
done
- 1
#!/bin/bash
sum=0
for (( i=1;i<101;i++))
do
(( sum+=$i ))
done
echo "sum is $sum"
- 1
3.2 while循环
语法:
while command
do
commands
done
实例:
#!/bin/bash
num=10
while [ $num -lt 20 ]
do
echo "number is $num"
((num++))
done
3.3 until命令
语法:
until command
do
commands
done
- 1
实例:
#!/bin/bash
num=10
until [ $num -eq 0 ]
do
echo "number is $num"
((num--))
done
3.4 break命令
实例:
#!/bin/bash
num=10
for ((i=1;i<=10;i++))
do
if ((i==5))
then
break
else
echo "number is $i"
fi
done
- 1
当有两层循环的时候,"break 1"跳出内循环; " break 2"跳出外循环
#!/bin/bash
num=10
for ((i=1;i<=10;i++))
do
for((j=1;j<=5;j++))
do
if ((j==3))
then
break 1
else
echo "$i $j"
fi
done
done
- 1
#!/bin/bash
num=10
for ((i=1;i<=10;i++))
do
for((j=1;j<=5;j++))
do
if ((j==3))
then
break 2
else
echo "$i $j"
fi
done
done
3.5 continue命令
实例:
#!/bin/bash
num=10
for ((i=1;i<=30;i++))
do
if ((i>15 && i<25))
then
continue
else
echo "Number is $i"
fi
done
- 1
3.6 处理循环的输出
实例
#!/bin/bash
num=10
for ((i=1;i<=100;i++))
do
echo "Number is $i"
done > result.txt
- 1
#!/bin/bash
num=10
for ((i=1;i<=100;i++))
do
echo "Number is $i"
done | grep "5"
- 1
四. 变量的高级用法
4.1 变量替换和测试
变量替换
语法 | 含义 |
---|---|
${变量名#匹配规则} | 从变量开头进行规则匹配,将符合最短的数据删除 |
${变量名##匹配规则} | 从变量开头进行规则匹配,将符合最长的数据删除 |
${变量名%匹配规则} | 从变量尾部进行规则匹配,将符合最短的数据删除 |
${变量名%%匹配规则} | 从变量尾部进行规则匹配,将符合最长的数据删除 |
${变量名/旧字符串/新字符串} | 变量内容符合旧字符串规则,则第一个旧字符串会被新字符串取代 |
${变量名//旧字符串/新字符串} | 变量内容符合旧字符串规则,则全部的旧字符串会被新字符串取代 |
实例
变量测试
实例
4.2 字符串处理
计算字符串长度
方法一:
${#string}
方法二:
expr length "$string" #string如果有空格,则必须加双引号
- 1
获取子串在字符串中的索引位置
语法
expr index $string $substring
- 1
$substring会被切割成字符,比如orl被切割成o,r,l,分别查找字符,哪个字符最先找到就会返回那个字符的位置,l最靠前,位置为3,返回3
计算子串长度
expr match $string substr
- 1
$string会从头开始匹配子串,不能从中间匹配
抽取子串
方法一:
${string:position} #从string中的position开始
方法二:
${string:position:length} #从position开始,匹配长度为length
方法三:
${string:-(position)} #从右边开始匹配
方法四:
${string:(position)} #从左边开始匹配
方法五:
expr substr $string $position $length #从position开始,匹配长度为length
- 1
练习
需求描述:
变量string=“Bigdata process framework is Hadoop,Hadoop is an open source project” 执行脚本后,打印输出string字符串变量,并给出用户以下选项:
- 打印string长度
- 删除字符串中所有的Hadoop
- 替换第一个Hadoop为Mapreduce
- 替换全部Hadoop为Mapreduce
用户输入数字1|2|3|4,可以执行对应项的功能;输入qlQ则退出交互模式
脚本:
read命令用于从标准输入中读取输入单行
-a:将分裂后的字段依次存储到指定的数组中,存储的起始位置从数组的index=0开始。
-d:指定读取行的结束符号。默认结束符号为换行符。
-n:限制读取N个字符就自动结束读取,如果没有读满N个字符就按下回车或遇到换行符,则也会结束读取。
-N:严格要求读满N个字符才自动结束读取,即使中途按下了回车或遇到了换行符也不结束。其中换行符或回车算一个字符。
-p:给出提示符。默认不支持"\n"换行,要换行需要特殊处理,见下文示例。例如,"-p 请输入密码:"
-r:禁止反斜线的转义功能。这意味着"\"会变成文本的一部分。
-s:静默模式。输入的内容不会回显在屏幕上。
-t:给出超时时间,在达到超时时间时,read退出并返回错误。也就是说不会读取任何内容,即使已经输入了一部分。
-u:从给定文件描述符(fd=N)中读取数据。
#!/usr/bin/env bash
string="Bigdata process framework is Hadoop,Hadoop is an open source project"
function tips_info
{
echo "******************************************"
echo "*** (1) 打印string长度"
echo "*** (2) 在整个字符串中删除Hadoop"
echo "*** (3) 替换第一个Hadoop为Mapreduce"
echo "*** (4) 替换全部Hadoop为Mapreduce"
echo "******************************************"
}
function print_len
{
if [ -z "$string" ];then
echo "Error,string is null"
exit 1
else
echo "${#string}"
fi
}
function del_hadoop
{
if [ -z "$string" ];then
echo "Error,string is null"
exit 1
else
echo "${string//Hadoop/}"
fi
}
function rep_hadoop_mapreduce_first
{
if [ -z "$string" ];then
echo "Error,string is null"
exit 1
else
echo "${string/Hadoop/Mapreduce}"
fi
}
function rep_hadoop_mapreduce_all
{
if [ -z "$string" ];then
echo "Error,string is null"
exit 1
else
echo "${string//Hadoop/Mapreduce}"
fi
}
while true
do
echo "【string=\"$string\"】"
tips_info
read -p "Pls input your choice(1|2|3|4|q|Q):" choice
case "$choice" in
1)
echo
echo "Length Of String is: `print_len`"
echo
continue
;;
2)
echo
echo "删除Hadoop后的字符串为:`del_hadoop`"
echo
;;
3)
echo
echo "替换第一个Hadoop的字符串为:`rep_hadoop_mapreduce_first`"
echo
;;
4)
echo
echo "替换第一个Hadoop的字符串为:`rep_hadoop_mapreduce_all`"
echo
;;
q|Q)
exit 0
;;
*)
echo "error,unlegal input,legal input only in { 1|2|3|4|q|Q }"
continue
;;
esac
done
- 1
- 40
4.3 命令替换
语法格式
方法一
`command`
方法二
$(command)
$(())两个括号用来进行,包括加减乘除数据运算,引用变量前面可以加$,也可以不加$
案例
获取系统的所有用户并输出
cut 命令从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段写至标准输出。 如果不指定 File 参数,cut 命令将读取标准输入。必须指定 -b、-c 或 -f 标志之一。 参数: -b :以字节为单位进行分割。这些字节位置将忽略多字节字符边界,除非也指定了 -n 标志。 -c :以字符为单位进行分割。 -d :
自定义分隔符,默认为制表符。 -f :与-d一起使用,指定显示哪个区域。 -n :取消分割多字节字符。仅和 -b 标志一起使用。如果字符的最后一个字节落在由 -b 标志的 List 参数指示的范围之内,该字符将被写出;否则,该字符将被排除
#!/bin/bash
index=1
for user in `cat /etc/passwd | cut -d ":" -f 1`
do
echo "This is $index user: $user"
index=$(($index + 1))
done
根据系统时间计算今年或明年
Linux date命令可以用来显示或设定系统的日期与时间
时间方面:
% : 印出 %
%n : 下一行
%t : 跳格
%H : 小时(00..23)
%I : 小时(01..12)
%k : 小时(0..23)
%l : 小时(1..12)
%M : 分钟(00..59)
%p : 显示本地 AM 或 PM
%r : 直接显示时间 (12 小时制,格式为 hh:mm:ss [AP]M)
%s : 从 1970 年 1 月 1 日 00:00:00 UTC 到目前为止的秒数
%S : 秒(00..61)
%T : 直接显示时间 (24 小时制)
%X : 相当于 %H:%M:%S
%Z : 显示时区
日期方面:
%a : 星期几 (Sun..Sat)
%A : 星期几 (Sunday..Saturday)
%b : 月份 (Jan..Dec)
%B : 月份 (January..December)
%c : 直接显示日期与时间
%d : 日 (01..31)
%D : 直接显示日期 (mm/dd/yy)
%h : 同 %b
%j : 一年中的第几天 (001..366)
%m : 月份 (01..12)
%U : 一年中的第几周 (00..53) (以 Sunday 为一周的第一天的情形)
%w : 一周中的第几天 (0..6)
%W : 一年中的第几周 (00..53) (以 Monday 为一周的第一天的情形)
%x : 直接显示日期 (mm/dd/yy)
%y : 年份的最后两位数字 (00.99)
%Y : 完整年份 (0000..9999)
参数说明:
-d datestr : 显示 datestr 中所设定的时间 (非系统时间)
--help : 显示辅助讯息
-s datestr : 将系统时间设为 datestr 中所设定的时间
-u : 显示目前的格林威治时间
--version : 显示版本编号
根据系统时间获取今年还剩下多少星期,已经过了多少星期
判定nginx进程是否存在,若不存在则自动拉起该进程
#!/bin/bash
nginx_process_num=$(ps -ef | grep nginx | grep -v grep | wc -l)
if [ $nginx_process_num -eq 0 ];then
systemctl start nginx
fi
4.4 有类型变量
declare和typeset命令
- declare命令和typeset命令两者等价
- declare、typeset命令都是用来定义变量类型的
declare命令参数表
参数 | 含义 |
---|---|
-r | 将变量设为只读 |
-i | 将变量设为整数 |
-a | 将变量定义为数组 |
-f | 显示此脚本前定义过的所有函数及内容 |
-F | 仅显示此脚本前定义过的函数名 |
-x | 将变量声明为环境变量 |
输出数组内容:
echo $array[@]} 输出全部内容
echo ${array[1]} 输出下标索引为1的内容
获取数组长度:
echo ${#array} 数组内元素个数
echo ${#array[2]} 数组内下标索引为2的元素长度
给数组某个下标赋值:
array[0]="lily" 给数组下标索引为1的元素赋值为lily
array[20]="hanmeimei" 在数组尾部添加一个新元素
删除元素:
unset array[2] 清除元素
unset array 清空整个数组
分片访问:
${array[@]:1:4} 显示数组下标索引从1开始到3的3个元素,不显内容替换:
${array[@]/an/AN}将数组中所有元素内包含an的子串替换为AN
数组遍历:
for v in ${array[@]}
do
echo $v
done
- 1
4.5 bash数学运算
语法格式
方法一 expr $numl operator $num2
方法二 $(($num1 operator $num2))
expr操作符对照表
操作符 | 含义 |
---|---|
num1|num2 | num1不为空且非0,返回num1;否则返回num2 |
num1&num2 | num1不为空且非0,返回num1;否则返回0 |
num1num1小于num2,返回1;否则返回0 |
|
num1<=num2 | num1小于等于num2,返回1;否则返回0 |
num1=num2 | num1等于num2,返回1;否则返回0 |
num1!=num2 | num1不等于num2,返回1;否则返回0 |
num1>num2 | num1大于num2,返回1;否则返回0 |
num1>=num2 | num1大于等于num2,返回1;否则返回0 |
num1+num2 | 求和 |
num1-num2 | 求差 |
num1*hum2 | 求积 |
num1/num2 | 求商 |
num1%num2 | 求余 |
在使用的时候需要进行转义
expr $numl \| $num2
expr $numl \& $num2
expr $numl \< $num2
expr $num1 \< $num2r
expr $numl \<=$num2
expr $num1 \>$num2
expr $numl \>=$num2
练习例子
提示用户输入一个正整数num,然后计算1+2+3+…+num的值;必须对num是否为正整数做判断,不符合应当再次允许输入
#!/bin/bash
#
while true
do
read -p "pls input a positive number: " num
expr $num + 1 &> /dev/null
if [ $? -eq 0 ];then
if [ `expr $num \> 0` -eq 1 ];then
for((i=1;i<=$num;i++))
do
sum=`expr $sum + $i`
done
echo "1+2+3+....+$num = $sum"
exit
fi
fi
echo "error,input enlegal"
continue
done
bc介绍
- bc是bash内建的运算器,支持浮点数运算
- 内建变量scale可以设置,默认为0
bc操作符对照表
操作符 | 含义 |
---|---|
num1+num2 | 求和 |
num1-num2 | 求差 |
num1*num2 | 求积 |
num1/num2 | 求商 |
num1%num2 | 求余 |
num1^num2 | 指数运算 |
五. 函数的高级用法
5.1 函数定义和使用
函数介绍
- Linux Shell中的函数和大多数编程语言中的函数一样
- 将相似的任务或代码封装到函数中,供其他地方调用
语法格式
第一种格式
name()
{
command1
command2
......
commandn
}
第二种格式
function name
{
command1
command2
......
commandn
}
如何调用函数
- 直接使用函数名调用,可以将其想象成Shell中的一条命令
- 函数内部可以直接使用参数$1、$2...$n
- 调用函数:function name $1 $2
实例
需求描述:写一个监控nginx的脚本;如果Nginx服务宕掉,则该脚本可以检测到并将进程启动,如果正常运行,则不做任何处理
#!/bin/bash
this_pid=$$
while true
do
ps -ef | grep nginx | grep -v grep | grep -v $this_pid &> /dev/null
if [ $? -eq 0 ];then
echo "Nginx is running well"
sleep 3
else
systemctl start nginx
echo "Nginx is down,Start it...."
fi
done
5.2 向函数传递参数
function name
{
echo "Hello $1"
echo "Hello $2"
}
函数调用:
name Lily Allen
实例
需求描述:
写一个脚本,该脚本可以实现计算器的功能,可以进行+ - * / 四种计算。
#!/bin/bash
function calcu
{
case $2 in
+)
echo "`expr $1 + $3`"
;;
-)
echo "`expr $1 - $3`"
;;
\\*)
echo "`expr $1 \* $3`"
;;
/)
echo "`expr $1 / $3`"
;;
esac
}
calcu $1 $2 $3
5.3 函数返回值
返回值的方式
方法一:
return
方法二:
echo
使用return返回值
- 使用return返回值,只能返回1-255的整数
- 函数使用return返回值,通常只是用来供其他地方调用获取状态,因此通常仅返回0或1;0表示成功,1表示失败
实例
#!/bin/bash
this_pid=$$
function is_nginx_running
{
ps -ef | grep nginx | grep -v grep | grep -v $this_pid &> /dev/null
if [ $? -eq 0 ];then
return
else
return 1
fi
}
is_nginx_running && echo "Nginx is running" || echo "Nginx is stoped"
使用echo返回值
- 使用echo可以返回任何字符串结果
- 通常用于返回数据,比如一个字符串值或者列表值
cut 命令从文件的每一行剪切字节、字符和字段并将这些字节、字符和字段写至标准输出。
-b :以字节为单位进行分割。这些字节位置将忽略多字节字符边界,除非也指定了 -n 标志。
-c :以字符为单位进行分割。
-d :自定义分隔符,默认为制表符。
-f :与-d一起使用,指定显示哪个区域。
-n :取消分割多字节字符。仅和 -b 标志一起使用。如果字符的最后一个字节落在由 -b 标志的 List 参数指示的范围之内,该字符将被写出;否则,该字符将被排除
#!/bin/bash
function get_users
{
users=`cat /etc/passwd | cut -d: -f1`
echo $users
}
user_list=`get_users`
index=1
for u in $user_list
do
echo "The $index user is : $u"
index=$(($index+1))
done
5.4 局部变量和全局变量
全局变量
- 不做特殊声明,Shell中变量都是全局变量
- Tips:大型脚本程序中函数中慎用全局变量
局部变量
- 定义变量时,使用local关键字
- 函数内和外若存在同名变量,则函数内部变量覆盖外部变量
#!/bin/bash
var1="Hello world"
function test
{
var2=87
}
echo $var1
echo $var2
test
echo $var1
echo $var2
#!/bin/bash
var1="Hello world"
function test
{
local var2=87
}
test
echo $var1
echo $var2
5.5 函数库
为什么要定义函数库
- 经常使用的重复代码封装成函数文件
- 一般不直接执行,而是由其他脚本调用
实例
定义一个函数库,该函数库实现以下几个函数
1、加法函数 add
2、减法函数 reduce
3、乘法函数 multiple
4、除法函数 divide
5、打印系统运行情况的函数sys_load,该函数可以显示内存情况
base_function
function add
{
echo "`expr $1 + $2`"
}
function reduce
{
echo "`expr $1 - $2`"
}
function multiple
{
echo "`expr $1 \* $2`"
}
function divide
{
echo "`expr $1 / $2`"
}
function sys_load
{
echo "Memory Info"
echo
free -m
echo
echo "Disk Usage"
echo
df -h
echo
}
test1.sh
#! /bin/bash
. /root/base_function
add 12 23
reduce 90 30
multiple 12 12
divide 12 2
sys_load
- 1
总结
- 库文件的后缀是任意的,但一般使用.lib
- 库文件通常没有可执行选项
- 库文件无需和脚本在同级目录,只需在脚本中引用时指定
- 第一行一般使用#!/bin/echo,输出警告信息,避免用户执行
六. 文件查找
6.1 find命令
语法格式
find [路径] [选项] [操作]
- 1
选项参数对照表
选项 | 含义 |
---|---|
-name | 根据文件名查找 |
-perm | 根据文件权限查找 |
-prune | 通常和-path一起使用,用于将特定目录排除在搜索条件之外 该选项可以排除某些查找目录 |
-user | 根据文件属主查找 |
-group | 根据文件属组查找 |
-mtime | 根据文件更改时间查找 |
-nogroup | 查找无有效属组的文件 |
-nouser | 查找无有效属主的文件 |
-newer file1 !file2 | 查找更改时间比file1新但比file2旧IDE文件 |
-type | 按文件类型查找 |
-size | 按文件大小查找 |
-mindepth n | 从n级子目录开始搜索 |
-maxdepth n | 最多搜索到n级子目录 |
-exec | 对搜索到的文件执行特定的操作,格式为-exec command {} \; |
注:
1、{} 和 \ 之间有空格
2、\ 和 ; 之间没有空格!
实例
-type
f 文件 find . -type f
d 目录 find . -type d
c 字符设备文件 find . -type c
b 块设备文件 find . -type b
l 链接文件 find . -type l
p 管道文件 find .-type p
目录下以conf结尾的文件
find /etc -name '*conf' #-name不区分大小写
查找当前目录下文件名为aa的文件,不区分大小写
find . -iname aa
查找文件属主为hdfs的所有文件
find . -user hdfs
查找文件属组为yarn的所有文件
find . -group yarn
查找/etc目录下小于10000字节的文件
find /etc -size -10000c
查找/etc目录下大于1M的文件
find /etc -size +1M
-mtime
-n n天以内修改的文件
+n n天以外修改的文件
n 正好n天修改的文件
查找/etc目录下5天之内修改且以conf结尾的文件
find /etc -mtime -5 -name1 '*.conf'
查找/etc目录下10天之前修改且属主为root的文件
find /etc -mtime +10 -user root
查找/etc目录下30分钟之前修改的文件
find /etc -mmin +30
查找/etc目录下30分钟之内修改的目录
find /etc -mmin -30 -type d
在/etc下的3级子目录开始搜索
find /etc -mindepth 3
在/etc下搜索符合条件的文件,但最多搜索到2级子目录
find /etc -maxdepth 2 -type f -name '*.conf'
查找没有属主的用户
find . -type f -nouser
查找没有属组的用户
find .-type f -nogroup
-perm例子:
find.-perm 664
查找当前目录下所有普通文件,但排除test目录
find . -path ./etc -prune -o -type f
查找当前目录下所有普通文件,但排除etc和opt目录
find . -path . /etc -prune -o -path ./opt -prune -o -type f
查找当前目录下所有普通文件,但排除etc和opt目录,但属主为hdfs
find . -path ./etc -prune -o -path ./opt -prune -o -type f -a -user hdfs
查找当前目录下所有普通文件,但排除etc和opt目录,但属主为hdfs,且文件大小必须大于500字节
find . -path ./etc -prune -o -path ./opt -prune -o -type f -a -user hdfs -a -size +500c
搜索/etc下的文件(非目录),文件名以conf结尾,且大于10k,然后将其删除
find . /etc/ -type f -name '*.conf' -size +10k -exec rm -f {} \;
将/var/1og/目录下以1og结尾的文件,且更改时间在7天以上的删除
find /var/log/ -name '*.log' -mtime +7 -exec rm -rf{} \;
搜索/etc下的文件(非目录),文件名以conf结尾,且大于10k,将其复制到/root/conf目录下
find . /etc/ -size +10k -type f -name '*.conf' -exec cp {} /root/conf/ \;
-ok 和exec功能一样,只是每次操作都会给用户提示
逻辑运算符:
-a 与
-o 或
-not | ! 非
查找当前目录下,属主不是hdfs的所有文件
find . -not -user hdfs | find . ! -user hdfs
查找当前目录下,属主属于hdfs,且大小大于300字节的文件
find . -type f -a -user hdfs -a -size +300c
查找当前目录下的属主为hdfs或者以xm1结尾的普通文件
find . -type f -a \( -user hdfs -o -name '*.xml' \)
6.2 find、locate、whereis和which总结及适用场景分析
locate命令
-
文件查找命令,所属软件包mlocate
-
不同于find命令是在整块磁盘中搜索,locate命令在数据库文件中查找
-
find是默认全部匹配,locate则是默认部分匹配
updatedb命令
-
用户更新/var/lib/mlocate/mlocate.db
-
所使用配置文件/etc/updatedb.conf
-
该命令在后台cron计划任务中定期执行
whereis命令
选项 | 含义 |
---|---|
-b | 只返回二进制文件 |
-m | 只返回帮助文档文件 |
-s | 只返回源代码文件 |
which命令
仅查找二进制程序文件
选项 | 含义 |
---|---|
-b | 只返回二进制文件 |
各命令使用场景推荐
命令 | 适用场景 | 优缺点 |
---|---|---|
find | 查找某一类文件,比如文件名部分一致 | 功能强大,速度慢 |
locate | 只能查找单个文件 | 功能单一,速度快 |
whereis | 查找程序的可执行文件、帮助文档等 | 不常用 |
which | 只查找程序的可执行文件 | 常用于查找程序的绝对路径 |
七. 文件处理三剑客之grep
7.1 grep命令
grep 命令用于查找文件里符合条件的字符串。
语法1:
grep [option] [ pattern] [file1, file2..]
语法2:
command | grep [option] [pattern]
选项 | 含义 |
---|---|
-v | 不显示匹配行信息 |
-i | 搜索时忽略大小写 |
-n | 显示行号 |
-r | 递归搜索 |
-E | 支持扩展正则表达式 |
-F | 不按正则表达式匹配,按照字符串字面意思匹配 |
-c | 只显示匹配行总数 |
-w | 匹配整词 |
-x | 匹配整行 |
-l | 只显示文件名,不显示内容 |
-s | 不显示错误信息 |
- grep默认不支持扩展正则表达式,只支持基础正则表达式
- 使用grep-E可以支持扩展正则表达式使用
- egrep可以支持扩展正则表达式,与grep-E等价
7.2 文件处理三剑客之sed
sed(Stream Editor),流编辑器。对标准输出或文件逐行进行处理
语法格式
语法一:
stdout |sed [option] "pattern command"
语法二:
sed [option] "pattern command" file
选项 | 含义 |
---|---|
-n | 只打印模式匹配行 |
-e | 直接在命令行进行sed编辑,默认选项 |
-f | 编辑动作保存在文件中,指定文件执行 |
-r | 支持扩展正则表达式 |
-i | 直接修改文件内容 |
edit.sed文件写入:
/python/p
- 1
pattern用法表
匹配模式 | 含义 |
---|---|
10command | 匹配到第10行 |
10,20command | 匹配从第10行开始,到第20行结束10, |
10,+5command | 匹配从第10行开始,到第16行结束 |
/pattern1/command | 匹配到pattern1的行 |
/pattern1/,/pattern2/command | 匹配到pattern1的行开始,到匹配到patern2的行结束 |
10,/pattern1/command | 匹配从第10行开始,到匹配到pettern1的行结束 |
/pattern1/,10command | 匹配到pattern1的行开始,到第10行匹配结束 |
实例
打印file文件的第17行
sed -n "17p" file
打印文件的10到20行
sed -n "10,20p" file
打印file文件中从第10行开始,往后面加5行
sed -n "10,+5p" file
打印file文件中以root开头的行
sed -n "/^root/p" file
打印file文件中第一个匹配到以ftp开头的行,到mail开头的行结束
sed -n "/^ftp/,/^mail/p" file
打印file文件中从第4行开始匹配,直到以hdfs开头的行结束
sed -n "4,/^hdfs/p" file
打印file文件中匹配root的行,直到第10行结束
sed -n "/root/,10p"file
编辑命令对照表
类别 | 编辑命令 | 含义 |
---|---|---|
查询 | p | 打印 |
= | 只显示行号 | |
增加 | a | 行后追加 |
i | 行前追加 | |
r | 外部文件读入,行后追加 | |
w | 匹配行写入外部文件 | |
删除 | d | 删除 |
修改 | s/old/new | 将行内第一个old替换为new |
s/old/new/g | 将行内全部的old替换为new | |
s/old/new/2g | 将行内从第2个old开始到剩下所有的old替换为new | |
s/old/new/ig | 将行内全部的old替换为new,忽略大小写 |
实例
先看一下例子
&和\1引用模式匹配到的整个串
两者区别在于只能表示匹配到的完整字符串,只能引用整个字符串;而\1可以使用()匹配到的字符
sed中引用变量时注意事项:
(1)、匹配模式中存在变量,则建议使用双引号
(2)、sed中需要引入自定义变量时,如果外面使用单引号,则自定义变量也必须使用单引号
#!/bin/bash
old_str=hadoop
new_str=HADOOP
sed -i "s/$old_str"/"$new_str/g" abc.txt
需求描述:
处理一个类似MySQL配置文件my.cnf的文本,示例如下;编写脚本实现以下功能:输出文件有几个段,并且针对每个段可以统计配置参数总个数
my.cnf
# this is read by the standalone daemon and embedded servers
[client]
port=3306
socket=/tmp/mysql.socket
#ThisSegmentForse