知识点
格式:
#!/bin/bash开头
如果在编辑脚本的时候如果 #!/bin/bash 中的bin有黄色阴影的话
处理方法:在 vim 编辑器中执行:nohlsearch
变量
**输出变量基本用法:**echo $变量名 注意,echo后边要加空格
常用系统变量
H
O
M
E
、
HOME、
HOME、PWD、
S
H
E
L
L
、
SHELL、
SHELL、USER等等
显示当前shell中所有的变量:set
自定义变量
基本用法:
(1)定义变量:变量名=变量值,注意,=号前后不能有空格
(2)撤销变量:unset 变量名
(3)声明静态变量:readonly变量,注意:不能unset
将局部变量改为全局变量:
export 变量名
特殊变量
$n
基本语法:
n为数字,$0代表脚本名称,$1-
9
代表第一到第九个参数,十以上的参数需要用大括号包含,如
9代表第一到第九个参数,十以上的参数需要用大括号包含,如
9代表第一到第九个参数,十以上的参数需要用大括号包含,如{10}
例子:
1>编辑脚本
@!/bin/bash
echo '===========$n=========='
echo $0
echo $1
echo $2
2>输出脚本
./hello.sh $1的值 $2的值
$#
基本语法:
获取所有输入参数个数,常用于循环,判断参数的个数是否正确以及加强脚本的健壮性
例子:
1>编辑脚本
@!/bin/bash
echo '===========$#=========='
echo $#
2>输出脚本
./hello.sh
?
和
*和
?和@
j基本语法:
?
?
?
?
?
:代表命令行中所有的参数,
*****:代表命令行中所有的参数,
?????:代表命令行中所有的参数,*****把所有的参数看成一个整体
@
:代表命令行中所有的参数,但
@:代表命令行中所有的参数,但
@:代表命令行中所有的参数,但@把每一个参数区分对待
例子:
1>编辑脚本
@!/bin/bash
echo '===========$*=========='
echo $*
echo '===========$@=========='
echo $@
2>输出脚本
./hello.sh
$?
基本语法:
最后一次执行的命令的返回状态,如果这个变量的值为0,证明上一个命令正确执行;如果这个变量的值为非0(具体是哪个数,由命令自己决定),则证明上一个命令执行不正确、
例子:
./hello.sh
echo $?
0 正确返回0
运算符
基本用法:
"
(
(
运算式
)
)
"
或“
((运算式))"或“
((运算式))"或“[运算式]”
**注意:**如果使用的是(())的话里面可以使用数学上的符号,如”>=“等;若用的是[]的话就不能用这些符号,要用像”-lt“等符号
例子1:
计算(2+3)* 4的值
1> s=$[(2 + 3) * 4]
2> echo $s
例子2:
计算两个数的和
1>编辑脚本(hello.sh)
#!/bin/bash
sum = $[$1 + $2]
echo sum=$sum
2>输出脚本
./hello.sh $1的值 $2的值
条件判断
基本语法:
(1)test 判断的内容
(2)[ 判断的内容 ] **注意 判断的内容 前后要有空格
**注意:**条件非空即为true(0);[ adas ]返回0,[ ]返回1
常用判断条件:
(1)两个整数之间比较
-eq 等于(equal) -ne 不等于(not equal)
-lt 小于(less than) -le 小于等于(less equal)
-gt 大于(greater than) -ge 大于等于(greater equal)
**注意:**如果字符串之间的比较,用等号“=”判断相等;用“!=”判断不等
(2)按照文件权限进行判断
-r 有读的权限(read)
-w 有写的权限(write)
-x 有执行的权限(execule)
(3)按照文件类型进行判断
-e 文件存在(existence)
-w 文件存在并且是一个常规的文件(file)
-d 文件存在并且是一个目录(directory)
多条件判断:
&&表示前一条命令执行成功时,才执行后一条命令;||表示上一条命令执行失败后,才执行下一条命令
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g2hmC9GS-1658999166843)(E:\桌面\未命名文件.png)]
流程控制(重点)
if判断
基本语法:
(1)单分支
if [ 条件判断 ];then
程序
fi
或者
if [ 条件判断 ]
then
程序
fi
(2)多分支
if [ 条件判断 ]
then
程序
elif [ 条件判断 ]
then
程序
else
程序
fi
case语句
基本语法:
case $变量名 in
"值1")
如果变量的值等于值1,则执行程序1
;;
"值2")
如果变量的值等于值2,则执行程序2
;;
_省略其他分支_
*)
如果变量的值都不是以上的值,则执行此程序
;;
esac
注意事项:
(1)case行尾必须为单词”in“,每一个模式匹配必须以右边的”)“结束
(2)双分号”;;“表示命令序列结束,相当于Java中的break
(3)最后的”*)“表示默认模式,相当于Java中的default
for循环
基本语法1:
for ((初始值;循环控制条件;变量变化))
do
程序
done
**例子1:(vim for1.sh)**求0到100的和
1>编辑脚本
#!/bin/bash
sum=0
for ((i=0;i<=100;i++))
do
sum=$[ $sum + $i ]
done
echo $sum
2>输出
./for1.sh
**例子2:(vim for2.sh)**求0到$i的和
1>编辑脚本
#!/bin/bash
for ((i=0;i<=$1;i++))
do
sum=$[ $sum +$i ]
done
echo $sum
2>输出
./for2.sh $1的值
基本语法2
for 变量 in 值1 值2 值3...
do
程序
done
echo $变量
例子1:
for os in Linux Windows macos;do echo $os;done
Linux
Windows
macos
**例子2:**求1到100的和
for i in {1..100};do sum=$[ $sum + $i ];done;echo $sum
注:{ }代表一个序列;如{1…100}表示是1到100的序列,{}里面有两个…;相当于Java里面的增强for循环
while循环
基本语法:
while [ 条件判断式 ]
do
程序
done
**例子1:(vim while.sh)**求1到$1的和
#!/bin/bash
a=1
while [ $a -le 100 ]
do
sum=$[ $sum + $a ]
a=$[ $a + 1 ]
done
echo $sum
**注:**相比for,要在循环外部提前声明初始值(如:a=1)并且要在循环体内声明变量变化(如:a=$[ $a + 1 ])
例子2:
#!/bin/bash
a=1
while [ $a -le 100]
do
let sum+=a
let a++
done
echo $sum
read读取控制台输入
基本语法:
read (选项)(参数)
①选项:
-p:指定读取值时的提示符
-t:指定读取值时等待的时间(秒)如果-t不表示一直等待
②参数:
变量:指定控制台输入的变量名
例子:
1>编辑脚本
#!/bin/bash
read -t 10 -p "请输入你的名字:" name
echo "welcome $name"
2>输出
请输入你的名字:longlong
welcome longlong
函数
系统函数
basename
basename [string / pathname] [suffix]
(basename 命令会删掉所有的前缀包括最后一个(‘/’)字符,然后将字符串显示出来
basename 可以理解为取路径里的文件名称
选项:
suffix 为后缀,如果 suffix 被指定,basename 会将 pathname 或 string 中的 suffix 去掉
例子:
1>编辑脚本(vim hanshu.sh)
@!/bin/bash
echo '===========$n=========='
echo name:$(basename $0 .sh)
echo 1st:$1
echo 2nd:$2
注:$()是命令替换符,替换成除了最后一个 / 前面的内容,只输出后边的内容,加了.sh是为了去掉后缀
2>输出
./hanshu.sh $1的值 $2的值
/root/scripts/hanshu.sh $1的值 $2的值
===========$n==========
hanshu
$1的值
$2的值
dirname
基本语法:
dirname 文件绝对路径
(从给定的包含绝对路径的文件名中去除文件名(非目录的部分),然后返回剩下的路径(目录部分))
dirname 可以理解为取文件路径的绝对路径名称
dirname正好跟basename输出的内容相反
**例子1:
dirname /root/scripts/hanshu.sh
/root/scripts
例子2:
1>编写脚本
@!/bin/bash
echo '===========$n=========='
echo name:$(basename $0 .sh)
echo psth:$(cd $(dirname $0);pwd)
cd $(dirname $0)
echo path:$(pwd)
echo 1st:$1
echo 2nd:$2
2>输出
./hanshu.sh $1的值 $2的值
/root/scripts/hanshu.sh a b
===========$n==========
name:hanshu
path:/root/scripts
$1的值
$2的值
自定义函数
基本语法:
【function】funname【()】
{
? 程序
? 【return int】
}
注意:
(1)【】这里面的内容可以不要;funname为函数的名字
(2)必须在调用函数的地方,先声明函数,shell脚本是逐行运行,不会像其它语言一样先编译
(3)函数返回值,只能通过$?系统变量获得,如果要显示返回值则加:return 返回;如果不加,将以最后一条命令作为运行结果。return 后跟数值n(0-255,超出则返回不正确的值)
1>编译脚本(vim fun.sh)求两个数的和
第一种写法:
#!/bin/bash
function add(){
s=$[ $1 + $2 ]
echo "和:"$s
}
read -p"请输入第一个整数:" a
read -p"请输入第二个整数:" b
add $a $b
第二种写法:
#!/bin/bash
function add(){
s=$[ $1 + $2 ]
echo $s
}
read -p"请输入第一个整数:" a
read -p"请输入第二个整数:" b
sum=$(add $a $b)
echo "和:"$sum
错误的写法:
#!/bin/bash
function add(){
s=$[ $1 + $2 ]
return $s
}
read -p"请输入第一个整数:" a
read -p"请输入第二个整数:" b
sum=$(add $a $b)
echo "和:"$?
这个错误的原因:因为return的返回值不能超过255,超过则输出错误的返回值
#!/bin/bash
function add(){
s=$[ $1 + $2 ]
return "和:"$s
}
read -p"请输入第一个整数:" a
read -p"请输入第二个整数:" b
sum=$(add $a $b)
echo $?
这个会显示:./fun.sh: 第 4 行:return: 和:40: 需要数字参数255
综合案例1(归档文件)
实际生产应用中,往往需要对重要的数据进行备份
需求:实现一个每天对指定目录归档备份的脚本,输入一个目录名称(末尾不带/),将目录下所有文件按天归档保存,并将归档日期附加在归档文件名上,放在/root/archive下。
这里用到归档命令:tar
格式:tar 选项 生成文件名称 要归档的目录的名称
后面可以加上 -c 选项便是归档,加上 -z 选项表示同时进行压缩,得到的文件后缀名为 .tar.gz
脚本实现:(针对这一题而言)
#!/bin/bash
if[ $# -ne 1 ]
then
echo "参数的个数错误!应该输入一个参数,作为归档目录名"
echo
exit
fi
if [ -d $1 ]
then
echo
else
echo
echo "目录不存在!"
echo
exit
fi
DIR_NAME=$(basename $1)
DIR_PATH=$(cd $(dirname $1);pwd)
DATE=$(date +%y%m%d)
FILE=archive_$(DIR_NAME)_$DATE.tar.gz
DEST=/root/archive/$FILE
echo "开始归档.."
echo
tar -czf $DEST $DIR_PATH/$DIR_NAME
if [ $? -eq 0]
then
echo
echo "归档成功!"
echo "归档文件为:$DEST"
echo
else
echo "归档出现问题"
echo
fi
exit
测试:
mkdir /root/archive
./guidang.sh ../scripts
如果设计定时执行脚本可以参照下列:
比如:每天凌晨2点执行一次
#首先进行编辑
crontab -e
#进入之后,写下以下命令
0 2 * * * /root/scripts/guidang.sh /root/scripts
正则表达式入门
正则表达式使用单个字符串来描述,匹配一系列符合某个语法规则的字符串。通常被用来检索、替换那些符合某个模式的文本。Linux下,grep、sed、awk等文本处理工具都支持通过正则表达式进行模式匹配
常规匹配
一串不包含特殊字符的正则表达式匹配自己;例如
cat /etc/passwd | grep long
就会匹配所有包含long的行
常用特殊字符
1>特殊字符:^
^ 匹配一行的开头
例如:
cat etc/passwd | grep ^a
会匹配出所有以 a 开头的行
2>特殊字符:$
$ 匹配一行的结束
例如:
cat etc/passwd | grep t$
会匹配出所有以 t 结尾行
^ 和 $ 的结合
cat etc/passwd | grep ^dsat$
中间锁死了,所以只会匹配到dsat在一起的行
cat etc/passwd | grep ^$
会匹配出所有的空行
3>特殊字符:.
. 匹配一个任意的字符
cat etc/passwd | grep r..t
会匹配出所有 r 和 t 之间只有两个字符的行
4>特殊字符:*
***** 不单独使用,它和上一个字符连用,表示匹配上一个字符0次或多次
例如
cat etc/passwd | grep ro*t
会匹配到rt、rot、root、roooot等所有行
.和*的结合
表示任意字符出现任意次
例如
cat etc/passwd | grep ^a.*bash$
会匹配出以 a 开头以 bash 结尾;并且a和bash之间有任意字符出现任意多次的行
5>字符区间(中括号):[]
[ ]:表示匹配某个范围内的一个字符,例如下列
[6,8]:匹配6或者8
[0-9]:匹配一个0-9的数
[0-9]*:匹配任意长度的数字字符串
[a-z]:匹配一个a-z之间的字符
[a-z]*:匹配任意长度的字母字符串
[a-c,e-f]:匹配a-c或者e-f之间的任意字符
例子:
会匹配rt、rat、rbt、rabt、rabbt等 r 和 t 之间出现a或者b任意次的所有行
特殊字符:\
\ 表示转义,不会单独使用。要用单引号
例如:找出passwd中所有包含“$”的行
cat etc/passwd | grep '\$'
例如:找出passwd中所有包含“/$”的行
cat etc/passwd | grep '/\$'
案
问题:匹配一个合法的手机号
思路:
可以先把所有的号码写到一个文件里面,然后再通过
**cat 某个文件 | grep ^1[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$ 来匹配 **
**或者通过扩展的正则表达式,但是要加 -E **
cat 某个文件 | grep -E ^1[0-9]{10}$
小测试:(没有能文件)
echo "12345612457" | grep ^1[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$
或者
echo "12345612457" | grep -E ^1[0-9]{10}$
文本处理工具
cut
cut的工作就是“剪”,具体就是在文件中负责剪切数据用的
基本用法:
cut 【选项】文件名字
也可以cat(ls等等) xxx| cut xxx
选项:
-f:列号,提取第几列
-d:分隔符,按照指定的分割符分割列,默认是制表符"\t"
-c:按照字符进行切割,后加上 n 表示取第几列。比如 -c 1
例子1:
1>数据准备
vim cut.txt
dong sfds
sa ad
sda da
da da
2>切割cut.txt的第一列
cut -d " " -f 1 cut.txt
dong
sa
sda
da
例子2(切割ifconfig后打印的IP地址)
ifconfig ens33 | grep netmask | cut -d " " -f n(第几列开始)
awk
文本分析工具,把文件逐行的读入,以空格为默认分隔符将每片切片
基本语法:
awk 【选项参数】‘/pattern1/{action1} /pattern2/{action2}…’ filename
pattern:表示 awk 在数据中查找的内容,就是匹配模式(其实就是正则表达式)
action:在找到匹配内容时所执行的一系列命令
/pattern1/{action1} 这一部分其实就相当于 if 语句
选项参数:
-F:指定输入文件分隔符;如果是默认分隔符空格可以不写
-v:赋值一个用户定义变量
基本功能
例子:
(1)搜索 passwd 文件以 root 关键字开头的所有行,并输出该行的第7列
cat /etc/passwd |awk -F ":" '/^root/{print $7}'
/bin/bash
(2)搜索 passwd 文件以 root 关键字开头的所有行,并输出该行的第1列和第7列,中间以“,”号分开
cat /etc/passwd |awk -F ":" '/^root/{print $1","$7}'
/root,/bin/bash
(3)只显示 /etc/passwd 的第1列和第7列,以逗号分割,且在所有的行的前面添加 user,shell 在最后一行添加 long,/bin/zuishuai
cat /etc/passwd |awk -F ":" 'BEGIN{print "user,shell"}{print $1","$7} END{print "long,/bin/zuishuai"}'
BEGIN在所有数据读取之前执行;END 在所有数据执行之后执行
扩展功能
例子:
(1)J将 passwd 文件中的用户 id 增加数值 1 并输出
cat /etc/passwd | awk -F ":" '{print $3+1}'
如果print里面的数据太多上面这种就不太好了;可以用 -v 参数
cat /etc/passwd | awk -v i=1 -F ":" '{print $3+i}'
awk的内置变量:
FILENAME:文件名
NR:已读的记录数(如:行号)
NF:浏览记录的域的个数(如:切割后,列的个数)
例子:
(1)统计passwd文件名,每行的行号,每列的列数
awk -F ":" '{print "文件名:"FILENAME "行号:"NR "列数:"NF}' /etc/passwd
(2)查询ifconfig命令输出结果中的空行行号
方法1: (使用 grep)这种行号后边有”:”且前面不能加东西
ifconfig | grep -n ^$
方法2 (使用NR)后边没有”:“且可以加任何东西
ifconfig | awk '/^$/ {print "空行行号:"NR}'
(3)切割ifconfig后打印的所有的IP地址(以空格为默认分隔符,前面几个空格就是几列)
方法1: (使用 grep 和 cut 两个工具)
ifconfig | grep netmask | cut -d " " -f n(第几列开始)
方法2: (使用awk工具)默认分隔符为空格
ifconfig | awk '/netmask/ {print $2}'
综合案例2(发送消息)
可以利用Linux自带的 mesg 和 write 工具,向其它用户发送消息(不用安装qq等软件)
看发送信息的功能是否打开:
(1)mesg
(2)who -T
发送信息:
write user(用户名) 【控制台】要发送的信息
需求:实现一个向某个用户快速发送消息的脚本,输入用户名作为第一个参数,后面直接跟要发送的消息。脚本需要1、检测用户是否登录在系统中 ,2、用户是否打开消息功能 ,以及3、当前发送的消息是否为空
脚本实现:(vim send_msg.sh)
-i:忽略大小写;-m 1:如果要两个同样的用户,只要第一个;awk ‘{print $1}’:分割住第一列(因为用户名在第1列)
#!/bin/bash
login_user=$(who | grep -i -m 1 $1 | awk '{print $1}')
if [ -z $login_user ]
then
echo "用户不在线!"
echo "脚本退出..."
exit
fi
is_allowed=$(who -T | grep -i -m 1 $1 | awk '{print $2}')
if [ $is_allowed != "+" ]
then
echo "$1 没有开启消息功能"
echo "脚本推出..."
exit
fi
if [ -z $2 ]
then
echo "没有消息发送"
echo "脚本推出..."
exit
fi
whole_msg=$(echo $* | cut -d " " -f 2-)
user_terminal=$(who | grep -i -m 1 $1 | awk '{print $2}')
echo $whole_msg | write $login_user $login_terminal
if [ $? != 0 ]
then
echo "发送失败!"
else
echo "发送成功!"
fi
exit
输出格式
./send_mesg.sh user(用户名) 要发送的信息
**注意:**发送消息发送不成中文
脚本 user(用户名)【控制台(如果有两个同样的用户的时候要加上控制台)】发送的信息 发送的消息。脚本需要1、检测用户是否登录在系统中 ,2、用户是否打开消息功能 ,以及3、当前发送的消息是否为空
脚本实现:(vim send_msg.sh)
-i:忽略大小写;-m 1:如果要两个同样的用户,只要第一个;awk ‘{print $1}’:分割住第一列(因为用户名在第1列)
#!/bin/bash
login_user=$(who | grep -i -m 1 $1 | awk '{print $1}')
if [ -z $login_user ]
then
echo "用户不在线!"
echo "脚本退出..."
exit
fi
is_allowed=$(who -T | grep -i -m 1 $1 | awk '{print $2}')
if [ $is_allowed != "+" ]
then
echo "$1 没有开启消息功能"
echo "脚本推出..."
exit
fi
if [ -z $2 ]
then
echo "没有消息发送"
echo "脚本推出..."
exit
fi
whole_msg=$(echo $* | cut -d " " -f 2-)
user_terminal=$(who | grep -i -m 1 $1 | awk '{print $2}')
echo $whole_msg | write $login_user $login_terminal
if [ $? != 0 ]
then
echo "发送失败!"
else
echo "发送成功!"
fi
exit
输出格式
./send_mesg.sh user(用户名) 要发送的信息
**注意:**发送消息发送不成中文
脚本 user(用户名)【控制台(如果有两个同样的用户的时候要加上控制台)】发送的信息
|