一、学习资源
1.1 入门学习
菜鸟教程:Shell 教程 | 菜鸟教程
快速入门:https://www.jb51.net/article/161028.htm
1.2 实战练习
牛客网题库
脚本练习实例
shell脚本日常练习
shell脚本基础日常练习_Random_code的博客-CSDN博客_shell 脚本练习
二、主要内容
2.1 命令
awk命令详解
与grep命令的配合使用:
示例:(判断boot分区可用容量小于20M时报警,否则显示OK)
df | grep "boot" | awk '{if($4<20000) print "Alart"; else print "OK"}'
basename命令
博客:Shell basename命令_DreamWendy-CSDN博客
command命令
博客:Shell command命令_DreamWendy-CSDN博客
echo命令详解
export命令详解
参考:shell与export命令 - tooltime - 博客园
expr命令详解
参考:SHELL脚本--expr命令全解 - 骏马金龙 - 博客园
grep命令详解
博客:Shell grep命令_DreamWendy-CSDN博客
let命令的用法
在shell中,let命令用于指定算术运算,即 let expretion,示例代码如下:
#!/usr/bin/env bash
## 运行结果:
## a init is 2
## a+=1 is 3
a=2
echo "a init is $a"
let "a+=1"
echo "a+=1 is $a"
local命令的作用
作用:一般用于shell内局部变量的定义,多使用在函数内部
关于局部变量和全局变量(主要理解第3点):
(1)shell 脚本中定义的变量是global的,作用域从被定义的地方开始,一直到shell结束或者被显示删除的地方为止。
(2)shell函数定义的变量也是global的,其作用域从 函数被调用执行变量的地方 开始,到shell或结束或者显示删除为止。函数定义的变量可以是local的,其作用域局限于函数内部。但是函数的参数是local的。
(3)如果局部变量和全局变量名字相同,那么在这个函数内部,会使用局部变量。
使用实例:
#!/bin/bash
text='global var'
function Hello() {
local text='local var!!!'
#局部变量
echo $text
}
Hello
## 运行结果
$PC: ./local_com.sh
local var!!!
## 结果分析
调用Hello方法,进入后执行echo输出text变量;
当方法外部和内部都定义了同一个变量时,在函数内部优先输出local定义的局部变量
printf命令的使用
printf 命令模仿 C 程序库(library)里的 printf() 程序。printf 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好。printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n
格式:
printf format-string [arguments...]
使用实例:
printf "hello shell\n"
printf "%-10s" read $1
printf "%-10s %-8s %-4.2f\n" LZS cool 48.6543
读取输入的一个参数并打印成字符串,%-10s 指一个宽度为 10 个字符(-表示左对齐,没有则表示右对齐),任何字符都会被显示 在 10 个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。 %-4.2f 指格式化为小数,其中.2 指保留 2 位小数;
printf的转义序列:
(1)\a—警告字符,通常为ASCII的BEL字符
(2)\b—后退
(3)\c—抑制(不显示)输出结果中任何结尾的换行字符(只在%b 格式指示符控制下的
参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任
何留在格式字符串中的字符,都被忽略
(4)\f—换页
(5)\n—换行
(6)\r—回车
(7)\t水平制表符
(8)\v垂直制表符
(9)\一个字面上的反斜杠字符
(10)\ddd表示1到3位数的八进制字符,仅在格式字符串中有效
(11)\0ddd表示1到3位的八进制字符
seq命令的用法
shift命令
参考:Shell编程中Shift的用法 - image eye - 博客园
位置参数可以用shift命令左移,比如shift 3表示原来的$4现在变成$1,原来的$5现在变成$2等等,原来的$1,$2,$3丢弃,$0不移动。不带参数的shift命令相当于shift 1。
我们知道,对于位置变量或命令行参数,其个数必须是确定的,或者当shell程序不知道其个数时可以把所有参数一起赋值给变量$*。当用户要求shell在不知道位置变量个数的情况下,还能逐个的把参数一一进行处理,也就是在$1后为$2等。在shift命令执行前变量$1的值在shift命令执行后就不可用了。
shift命令测试实例:
#!/bin/bash
until [ $# -eq 0 ];do
echo "The first argument is:$1,The number of arguments is:$#"
shift
done
unset命令的用法
unset命令使用规则:unset var
unset可以删除变量,被删除后,变量不能再次被使用,如下代码:
#!/bin/bash
var="unset test"
unset var
echo $var
该段代码将无任何输出,注意,unset不能删除只读变量,for循环等子代码块中的临时变量要及时使用unset删除;
2.2 语法
case语句用法
(1)";;"相当于其他语言的"break";
(2)"*)"相当于其他语言的"default";
(3)esac是case语句的结束;
(4)"-)"、"d)"等相当于其他语言的"case '-': "、"case 'd': ";
括号的使用
参考:shell中各种括号的作用()、(())、[]、[[]]、{}_乌托邦-CSDN博客
select循环
(1)select是个无线循环,因此要记住用break命令退出循环,或用exit命令终止脚本。也可以按ctrl+c退出循环。
(2)select经常和case联合使用。
(3)与for循环类似,可以省略in list,此时使用位置变量。
使用实例如下:
#!/bin/bash
#定义PS3提示符
PS3="Please choose the menu:"
#输出菜单
select menu in yangroutang mifan hulatang jiaozi lamian huimian quit
do
# 判断选择
case $REPLY in
1|4)
echo "The price is 20"
;;
2|5)
echo "The price is 12"
;;
3|6)
echo "The price is 10"
;;
7)
break
;;
*)
echo "Choose error"
;;
esac
done
2.3 正则表达式
参考教程:正则表达式 – 语法 | 菜鸟教程
Perl正则表达式(最常用的正则表达式)
2.4 符号
glob(通配符)详解
参考:Shell之Glob和RE的区别 - ZakZhu - 博客园
shell中$0等的含义
参考:Shell脚本中$0、$?、$!、$$、$*、$#、$@等的意义以及linux命令执行返回值代表意义_helloxiaozhe的博客-CSDN博客
$*和$@的用法:
shell脚本中$* 和 $@ 区别_寰宇001的博客-CSDN博客_shell脚本$*
shell中 &>和2>&1的含义
参考:Shell中 &>/dev/null和 >/dev/null 2>&1_weixin_34274029的博客-CSDN博客
>、<、>>、<<的区别:
>: 将标准输入以覆盖的方式写入文件;
<: 将标准输出以覆盖的方式写入文件;
>>: 将标准输入以追加的方式写入文件;
<<: 将标准输出以追加的·方式写入文件;
shell中三种引号的用法及区别
参考:https://www.jb51.net/article/108330.htm
shell中 ${}, ##, %%, :-,:+, ? 的使用
参考:shell中 ${}, ##, %%, :-,:+, ? 的使用_XFH1207的博客-CSDN博客
shell 之 -f -z -e -o -a -d 等判断符
三、学习实例
3.1 语法
1. 编写脚本,提示输入正整数n的值,计算1+2+…+n的总和。
#!/bin/bash
read -p "please input a positive integer:" n
i=1
sum=0
for i in ‘seq 1 $n’;do
if [[ $n =~ (^[0-9]*[1-9][0-9]*$) ]];then #判断n是否为正整数
let sum+=i
else
echo "a wrong int" && exit
fi
let i++
done
echo "sum is $sum"
相关知识:
(1)seq命令的用法,见本文 "seq命令的用法";
(2)正则表达式,"=~"符号见本文 "Perl正则表达式",正则表达式见本文 "正则表达式";
(3)let命令:见本文"let命令";
--------------------------------------------------------------------------------------------------------------------------------
2. 编写脚本/root/bin/yesorno.sh,提示用户输入yes或no, 并判断用户输入的是yes还是no,或是其它信息。
#!/bin/bash
read -p "can i help you? yes or no " ans
case $ans in
[yY][eE][sS]|[yY])
echo "ok,come on"
;;
[nN][oO]|[nN])
echo "no,thanks"
;;
*)
echo "byebye"
;;
esac
相关知识:
(1)"case"见本文"shell case语句用法"
(2)"[yY][eE][sS]|[yY]"匹配 "yes" 或 "y",其中的三个字母可随意大小写;
---------------------------------------------------------------------------------------------------------------------------------
3. 编写脚本/root/bin/filetype.sh,判断用户输入文件路径 ,显示其文件类型(普通,目录,链接,其它文件类型)
#!/bin/bash
read -p "please input file path:" path
a='ls -l $path|grep -o "^."' #脚本里面不能使用别名,所以用ls -l
case $a in
-)
echo "common file"
;;
d)
echo "directory file"
;;
l)
echo "linked file"
;;
*)
echo "other file"
;;
esac
相关知识:
(1)ls -l表示以这样的格式列出所有文件:
total 32
drwxrwxr-x 2 xiao xiao 4096 Nov 17 10:16 count_lines
-rwxrwxrwx 1 xiao xiao 392 Nov 18 10:38 create_user.sh
(2)|表示管道,即前一个命令的结果给后一个命令做输入;
(3)grep -o "^."表示根据前面列出的第一个参数"drwxrwxr-x",列出该文件的类型,^表示行首;
(4)"-)"、"d)"等见本文"shell case语句用法"
--------------------------------------------------------------------------------------------------------------------------------
4. 在Shell中接收传入的参数有两种方式,一种是通过脚本进行参数传递,另外一种是通过read来接收传入的参数;
通过脚本来传递参数的简单示例如下:
# 通过脚本来传递,这里$0指脚本名,$1为第一个参数,$2为第二个参数
[root@host ~]# ./script.sh 1 2
Total = 3
[root@host ~]# vim script.sh
#!/bin/bash
function add() {
total=$(expr $1 + $2)
echo -e "Total = $total"
}
add $1 $2
再来看通过read来接收传入的参数,先看read的基本格式:
read [-rs] [-a ARRAY] [-d delim] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [var1 var2 var3......]
read使用实例
[root@host ~]# ./script.sh
Enter Password:
The password your input is: Test@1234\
[root@host ~]# vim script.sh
#!/bin/bash
read -n10 -t30 -r -s -d $ -p "Enter Password:" password
echo -e "\nThe password your input is:$password"
参数分析
(1)-p 提示语句,后面接输入提示信息,这里为'Enter Password: '
(2)-n 参数个数,有时候要限制密码长度,或者其他输入长度限制,比如[Y/N],只输入输入一位,-n1
(3)-s 屏蔽回显,屏幕上不显示输入内容,一般用于密码输入
(4)-t 等待时间,这里设置30秒,30秒内未输入或者输入不全,终止
(5)-d 输入界限,这里是$,输入到$,自然终止输入
(6)-r 屏蔽特殊字符\的转译功能,加了之后作为普通字符处理
相关知识:
(1)expr命令,见本文"expr命令详解";
(2)echo -e "-e"表示开启转义,见本文 "echo命令详解";
---------------------------------------------------------------------------------------------------------------------------------
5. 计算100以内所有能被3整除的整数之和;
#!/bin/bash
sum=0
i=1
for i in {1..100};do
if [ $[$i%3] -eq 0 ];then
let sum+=i
fi
let i++
done
echo $sum
相关知识:
(1)关注for循环 "{1..100}"表示从1到100;
(2)关注 "$[$i%3]",当需要将计算的结果形成变量时,需要按该形式书写;
(3)关于for,见本文"for循环列表生成方式";
---------------------------------------------------------------------------------------------------------------------------------
3.2 功能
1. 使用shell脚本检测局域网主机是否宕机;
#!/bin/bash
for i in {1..254}
#-c指定ping几次,-i指定超时
ping -c 2 -i 0.5 192.168.43.$i &> /dev/null
#$?是上一个指令的执行返回值 ,0表示没有错误,其他任何值表明有错误
#-eq equal 的缩写
if [$? -eq 0]; then
echo "192.168.43.$i is up"
else
echo "192.168.43.$i is down"
fi
done
2. 编写一个脚本,名为createscripts.sh ,当执行该脚本时,输入 createscripts.sh /path/newsh.sh 则会在指定路径生成脚本文件(涵盖注释信息),并且自动打开该文件,开始编辑,在编辑结束后,自动给该脚本加上执行权限。
#!/bin/bash
#
A=`basename $1 |grep -o "\.sh$"`
#echo $A
if [ -n "$A" ];then
echo -e "#!/bin/bash \n#" >> $1
echo "#---------------------------------------" >> $1
echo "# Filename: `basename $1`" >> $1
echo "# Revision: 1.0" >> $1
echo "# Date: `date +"%F %T"`">> $1
echo "# Author: `id -nu`" >> $1
echo "# Email: sezina_f@163.com" >> $1
echo "# Description:" >> $1
echo "#---------------------------------------" >> $1
echo "# Copyright: 2020 tzx" >> $1
echo "# License: GPL" >> $1
echo >> $1
vim +13 $1
chmod +x $1
else
echo "Usage: Please end file with .sh."
fi
相关知识:
(1)反引号(``)的用法见本文 "符号 -> shell中三种引号的用法和区别";
(2)basename主要用于去除去除文件名的目录部分和后缀部分,返回一个字符串参数的基本文件名称,具体用法见本文 "命令 -> basename命令" ;
(3)grep -o:只匹配符合正则表达式中相应规则的字符串(即无空格等空白字符);
(4)正则表达式中 "."表示 “匹配除换行符 \n 之外的任何单字符” ,这里是要匹配 ".sh",所以使用 "\."转义字符;
(5)>>是将标准输出以追加的方式写入文件,具体含义见本文 "shell中 &>和2>&1的含义 -> '>、<、>>、<<的区别' " ;
(6)vim +13 $1表示:创建该文件,并将光标定位到第13行让用户编辑(因为前12行已经有我们自动写入的内容了);
(7)chmod +x表示:给该文件赋予可执行权限;
3. 编写脚本systeminfo.sh,显示当前主机系统信息,包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小;
#!/bin/bash
echo "当前主机系统如下:"
echo "主机名: $(hostname)"
echo "IPV4地址: " "$(ifconfig|grep "inet "|grep -o "\([0-9]\{1,3\}\.\)\{3\}[1-9]\{1,3\}")"
echo "操作系统版本:" "$(cat /etc/redhat-release)"
echo "内核版本:" "$(uname -r)"
echo "CPU型号:" "$(lscpu|grep -i "model name")"
echo "内存大小" $(free -hm|head -2|tr -s " " |tail -1|cut -d" " -f2)
echo "磁盘容量" $(fdisk -l /dev/sda|head -2|tail -1|cut -d " " -f3,4)
4. 编写脚本/root/bin/disk.sh,显示当前硬盘分区中空间利用率最大的值;
echo "当前硬盘分区中空间利用率最大的值:$( df |grep -o "[0-9]\{1,3\}%" |sort -rn |head -1)"
5. 编写脚本/root/bin/sumspace.sh,传递两个文件路径作为参数给脚本,计算这两个文件中所有空白行之和;
space1=`cat $1 |grep "^[[:space:]]*$"|wc -l`
space2=`cat $2 |grep "^[[:space:]]*$"|wc -l`
echo $space1+$space2 |bc
6. 任意用户登录系统时,显示红色字体的警示提醒信息“Hi,dangerous!”
vim /etc/profile.d/env.sh
echo -e '\033[31m hi,dangerous! \033[0m'