shell

知识点

格式:

#!/bin/bash开头

如果在编辑脚本的时候如果 #!/bin/bash 中的bin有黄色阴影的话

处理方法:在 vim 编辑器中执行:nohlsearch

变量

**输出变量基本用法:**echo $变量名 注意,echo后边要加空格

常用系统变量

H O M E 、 HOME、 HOMEPWD、 S H E L L 、 SHELL、 SHELLUSER等等

显示当前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  ##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)  ##.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)  ##.sh前有空格
echo psth:$(cd $(dirname $0);pwd)  ##path为路径
###也可以改为下列这样
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
# 首先判断输入的参数个数是否为1
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)    #获取的年月日

#定义生成的归档文件名称;前面不一定非要是archive
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

测试:

#首先要先在root下创建archive目录
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

会匹配出所有 rt 之间只有两个字符的行

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等 rt 之间出现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

#查看用户是否登录($1 是 who 查看到的登录用户的信息;awk默认分隔符是空格,第一列是user,第二列终端)
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

#从参数中获取要发送的消息($*是获取所有参数;因为就第一个$1不是,所以要从第2列开始)
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

#查看用户是否登录($1 是 who 查看到的登录用户的信息;awk默认分隔符是空格,第一列是user,第二列终端)
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

#从参数中获取要发送的消息($*是获取所有参数;因为就第一个$1不是,所以要从第2列开始)
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(用户名)【控制台(如果有两个同样的用户的时候要加上控制台)】发送的信息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值