shell 脚本基础
🍇shell是一个命令解释器,他在操作系统的最外层,负责用户和系统内核的一个交互,把用户输入的命令翻一个系统,然后将输入、输出的结果反馈给系统用户
通俗来讲shell就是把Unix/Linux
下的命令放入一个文件内集中执行,它不仅是一个连接用户和Linux内核的程序,又是一个管理Linux的脚本语言的命令解释器,它是利用了shell功能写成的一个程序,将shell语句放入文件然后将结果反馈给用户,shell提供了数组、循环、条件以及逻辑判断等
我们所输入的命令计算机是不可以设别的,它需要一种程序来进行翻译成自己识别的二进制程序,同时把结果反馈给我们
编程语言的分类
🍐编程语言:
🥝解释型语言
🍋语言分类
1. 书写脚本
cat /server/scripts/shell.sh #!/bin/bash ----> 指定脚本默认使用的命令解释器 第一行 echo "hello word" #执行结果 [root@m01 /server/scripts]# sh shell.sh hello word
标注:
- 语法正确,文件有+x执行权限
- 以 # 开头,整行注释不执行
- 文件以
- source 或 . 替你在当前环境中 执行1次脚本 ,source 一般用来实现include 功能 或配置环境变量 别名后生效. /etc/init.d/functions
-
-
中间不能有空格,可以使用下划线
( _ )
。 -
不能使用标点符号。
-
不能使用bash里的关键字( 可用help帮助 )
-
等号 = 用于为变量分配值,等号两边不能有空格
-
变量存储的数据类型是整数类型和字符串类型
-
在赋值时建议用引号括起来。因为如果字符串中存在空格符号,需要使用单引号或双引号。
-
要对变量进行调用,在变量前加
$
变量的分类:
-
-
环境变量:主要保存和系统操作环境相关的数据。
-
位置参数变量:主要是用来向脚本当中传递参数或数据,变量名不能自定义,变量作用是固定的。
-
局部变量 是 shell 程序内部定义的,其作用仅限于定义它的程序,对其他程序不可见。包括:用户自定义变量、位置变量和预定义变量。
全局变量
含义 | |
PATH | 指定命令的搜索路径 |
HOME | 指定用户的主工作目录(即用户登录系统时,默认路径) |
HISTSIZE | 指保存历史命令记录的条数 |
LOGNAME | 指当前用户的登录名 |
HOSTNAME | 指主机的名称,程序如果要用到主机名,通常从这个环境取得 |
SHELL | 指当前用户时那种Shell |
LANG/LAGUGE | 和语言相关的环境变量,使用多种语言的用户可以修改此环境变量。/etc/sysconfig/i8n |
指当前用户的邮件存放目录 | |
PS1 | 命令基本提示符,root用户是#,普通用户是$ |
PS2 | 附属提示符,默认是 ">" |
UID | 记录用户的UID信息 |
HISTCONTROL |
修改和查看环境变量的命令
含义 | |
echo | 显示某个环境变量值 echo $PATH |
export | 设置一个新的环境变量 export HELLO="hello" (可以无引号) |
env | 显示所有环境变量 |
set | 显示本地定义的shell变量 |
unset | 清空环境变量 unset=HELLO |
readonly |
[root@m01 /]# VAR2=123 [root@m01 /]# echo $VAR 123
2. 例子:错误的演示,等于号之间的空格和数字开头的变量
[root@m01 /]# VAR = 123 -bash: VAR: command not found [root@m01 /]# VAR =123 -bash: VAR: command not found [root@m01 /]# VAR= 123 -bash: 123: command not found [root@m01 /]# 3VAR=123 -bash: 3VAR=123: command not found
3.
[root@m01 /]# web=nginx [root@m01 /]# echo ${web}.org nginx.org [root@m01 /]# echo www.${web}.org www.nginx.org [root@m01 /]# echo $weborg #无法显示空白
环境变量
[root@m01 ~]# VAR1=123 [root@m01 ~]# echo $VAR1 123 [root@m01 ~]# cat VAR1.sh #!/bin/bash echo $VAR1 [root@m01 ~]# echo $VAR1 123 [root@m01 ~]# bash VAR1.sh #或者sh 执行脚本时,会使用另一个bash去执行,就访问不到$VAR1的值
2. 例子:env命令查看全局变量
#用户切换时也可以用此命令判断环境变量是否切换成功
[root@m01 ~]# env | grep PATH #系统命令路径 PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
3. 例子:export
把局部变量输出为全局变量
[root@m01 ~]# export VAR1=123 [root@m01 ~]# echo $VAR1 123 [root@m01 ~]# cat VAR1.sh #!/bin/bash echo $VAR1 [root@m01 ~]# bash VAR1.sh #引用环境变量 123
4. 例子:让环境变量永久生效,可以把定义好的变量写入配置文件
当登录系统或新开启一个ssh连接启动bash进程时,一定会加载这4个配置文件:
作用 | |
/etc/profile | 系统全局环境和登录系统的一些配置 |
/etc/bashrc | shell 全局自定义配置文件,用于自定义 shell |
/root/.bashrc | 用于单独自定义某个用户的 bash |
/root/.bash_profile |
四个配置文件的先后顺序
[root@m01 ~]# echo 'echo /etc/profile ' >> /etc/profile [root@m01 ~]# echo 'echo /etc/bashrc' >> /etc/bashrc [root@m01 ~]# echo 'echo /root/.bashrc ' >> /root/.bashrc [root@m01 ~]# echo 'echo /root/.bash_profile ' >> /root/.bash_profile [root@m01 ~]# ssh root@172.16.1.61 /etc/profile /etc/bashrc /root/.bashrc /root/.bash_profile
下面开始插入永久环境变量:
#在/etc/profile
我们一般会把服务环境放在这里
[root@m01 ~]# vim /etc/profile export $VAR1=123 [root@m01 ~]# source /etc/profile #加载文件
[root@m01 ~]# vim VAR1.sh #!/bin/bash echo "本shell脚本的文件名: $0" echo "第1个参数: $1" echo "第2个参数: $2" echo "第3个参数: $3" echo "第4个参数: $4" [root@m01 ~]# chmod +x VAR1.sh [root@m01 ~]# ./VAR1.sh 11 22 33 44 本shell脚本的文件名:./VAR1.sh 第1个参数: 11 第2个参数: 22 第3个参数:33 第4个参数:44 #使用场景:服务器启动传参数 [root@m01 ~]# /etc/init.d/network restart #centos6启动方式
链接博文:https://blog.csdn.net/yangyang3_/article/details/80378733
扩展:date 命令是显示或设置系统时间与日期
-s <字符串>:根据字符串来设置日期与时间。字符串前后必须加上双引号;
[root@m01 ~]# date +"%Y-%m-%d" #今天时间,一般备份数据需要用这个 2019-12-29 [root@m01 ~]# date +"%Y-%m" #只显示年月 2019-12 [root@m01 ~]# date +"%Y-%m-%d %H:%M.%S" #日期加时间 2019-12-29 20:31.18 [root@m01 ~]# date +"%Y/%m/%d %H/%M/%S" #使用/做分隔符 2019/12/29 20/31/29 [root@m01 ~]# date +"%Y-%m-%d-%H-%M-%S" #使用-做分隔符,一般备份数据需要用这个 2019-12-29-20-31-46
注: %y 年份只显示2位, %Y年份显示4位
date命令加减操作: date +%Y%m%d #显示当天年月日 date -d "+1 day" +%Y%m%d #显示明天的日期 date -d "-1 day" +%Y%m%d #显示昨天的日期 date -d "-1 month" +%Y%m%d #显示上一月的日期 date -d "+1 month" +%Y%m%d #显示下一月的日期 date -d "-1 year" +%Y%m%d #显示前一年的日期 date -d "+1 year" +%Y%m%d #显示下一年的日期
设定时间:
date -s 01:01:01 #设置具体时间,不会对日期做更改
例2:在命令中调用date
命令
[root@m01 ~]# echo `date` #在脚本中优先执行反引号里的命令 Sun Dec 29 20:49:37 CST 2019 [root@m01 ~]# echo $(date) Sun Dec 29 20:50:31 CST 2019 [root@m01 ~]# echo `date +"%Y-%m-%d"` 2019-12-29
[root@m01 ~]# find /etc/ -type f [root@m01 ~]# VAR1=$( tar zcvfP root.tar.gz $(find /etc/ -type f )) [root@m01 ~]# echo $VAR1 #查看值, VAR1中存储着tar的标准输出
" "
在双引号中,除了$ , ' ' ,和 \ 以外所有的字符都解释成字符本身,拥有“调用变量的值”、“引用命令” 和 “转义符” 的特殊含义
[root@m01 ~]# echo $VAR1 110 [root@m01 ~]# echo \$VAR1 $VAR1
例一:给变量赋予多个单词,需要使用单引号和双引号
[root@m01 ~]# VAR1='12306' [root@m01 ~]# echo $VAR1 12306
例二:赋值时单引号和双引号的区别
[root@m01 ~]# VAR1='12306' [root@m01 ~]# VAR1="kd $VAR1" [root@m01 ~]# VAR2='kd $VAR1' [root@m01 ~]# echo $VAR1 kd 12306 [root@m01 ~]# echo $VAR2 kd $VAR1
注:单引号之间的内容原封不动赋值给变量,双引号之间的内容如有特殊符号会保留它的特殊含义
删除变量
[root@m01 ~]# unset VAR1 [root@m01 ~]# echo $VAR1
含义 | |
$* | 以一个单字符串显示所有向脚本传递的参数 |
$# | 传递到脚本的参数个数 |
$$ | 当前进程的进程号 PID |
$? | 显示最后命令的推出状态;0表示没有错误,非0代表错误 |
$! | 后台运行的最后一个进程的进程号 pid |
$0 | 脚本的名字 |
$@ |
例子:
[root@m01 ~]# vim VAR1.sh #!/bin/bash echo "$* 表示这个程序的所有参数 " echo "$# 表示这个程序的参数个数" echo "$$ 表示程序的进程ID " touch /tmp/b.txt & echo "$! 执行上一个后台指令的PID" echo "$$ 表示程序的进程ID " echo "$? 表示上一个程序执行返回结果 " [root@m01 ~]# sh VAR1.sh 11 22 33 44 55 66 11 22 33 44 55 66 表示这个程序的所有参数 6 表示这个程序的参数个数 88800 表示程序的进程ID 88801 执行上一个后台指令的PID 88800 表示程序的进程ID 0 表示上一个程序执行返回结果
当我们需要对变量的值进行处理时,我们就会用到以下方法进行处理
[root@m01 ~]# a=web [root@m01 ~]# echo $a web #直接显示变量返回值
[root@m01 ~]# a=nginx [root@m01 ~]# echo ${#a} 5 #输出变量的值长度为5(包括空格也算一个字符)
[root@m01 ~]# a=nginx [root@m01 ~]# echo ${a:0} nginx [root@m01 ~]# echo ${a:1} ginx #查看从1以后的字串,默认从0开始
[root@m01 ~]# a=nginx [root@m01 ~]# echo ${a:3:2} nx #打印从开头第三个字符之后的两个字符为nx
[root@m01 ~]# a=aaabbbcccddd [root@m01 ~]# echo ${a#a*b} bbcccddd #删除了开头到b最短的字符 [root@m01 ~]# echo ${a##aaab} bbcccddd #也可以直接输入内容,注意是要从第一个字母开始
注:这里必须从开头开始,*是代表任意
[root@m01 ~]# a=aaabbbcccddd [root@m01 ~]# echo ${a##a*b} cccddd #从开头删除匹配到b的最长字符串 [root@m01 ~]# echo ${a##aaabb} bbcccddd #也可以直接指定,这样的话就不会匹配最长值了
注:以上两个,如果指定的话,就删除指定的值,不会去匹配最长最短的值了
[root@m01 ~]# a=aaabbbcccddd [root@m01 ~]# echo ${a%c*b} aaabbbc #从变量结尾开始删除最短匹配Word的字串 [root@m01 ~]# echo ${a%ccddd} aaabbbc # 同样可以指定字符串来删除
[root@m01 ~]# a=aaabbbcccddd [root@m01 ~]# echo ${a%%c*d} aaabbb #从结尾开始匹配c到结尾d的最长的字符串并删除 [root@m01 ~]# echo ${a%%ccddd} aaabbbc #同样可以指定字符串来删除
[root@m01 ~]# a=aaabbbcccddd [root@m01 ~]# echo ${a/b/f} aaafbbccc #替换掉第一个匹配的字符
[root@m01 ~]# a=aaabbbcccddd [root@m01 ~]# echo ${a//b/f} aaafffccc #替换掉全部匹配的字符
特点:
-
在双括号结构中,所有表达式可以像C语言一样,如:a++,b-- 等 。a++等价于 a=a+1
-
在双括号中,所有变量可以不加入:”$“符号前缀
-
双括号可以进行逻辑运算,四则运算
-
双括号结构 扩展了 for ,while,if 条件测试运算
-
常用的算数运算符
含义(*表示常用) | |
---|---|
+、- | 加法(或正号)、减法(或负号)* |
*、/、% | 乘法、除法、取余(取模)* |
** | 幂运算* |
++ 、-- | 增加及减少,可前置也可以放在变量结尾,默认步长为1* |
!、&&、|| | 逻辑非(取反)、逻辑与(and)、逻辑或(or) |
==、!=、= | 比较符号(相等、不相等,对于字符串” = “也可以表示相当) |
<<、>> | 向左移位、向右移位 |
~、|、&、^ | 按位取反,按位异或、按位与、按位或 |
=、+=、-=、*=、/=、%= | 赋值运算符,例如a+=1 相当a=a+1,a-=1 相当a=a- |
?: |
[root@m01 ~]# b=$((1+2)) [root@m01 ~]# echo $b 3 [root@m01 ~]# echo $((2*3)) 6 [root@m01 ~]# echo $((b++)) 4 [root@m01 ~]# echo $((++b)) 6
-
-
用 / (反斜杠) 放在 shell 特定的字符前面;
-
> expr length “this is a test” 14
2. 抓取字串
> expr substr “this is a test” 3 5 is is
3. 抓取第一个字符数字串出现的位置
> expr index "sarasara" a 2
4. 整数运算
> expr 14 % 9 5 > expr 10 + 10 20 > expr 1000 + 900 1900 > expr 30 / 3 / 2 5 > expr 30 \* 3 (使用乘号时,必须用反斜线屏蔽其特定含义。因为shell可能会误解显示星号的意义) 90 > expr 30 * 3 expr: Syntax error
菜鸟教程:
参数 | 含义 |
-s | 一般用于密码输入,隐藏输入的信息 |
-p | 显示提示信息,参数后面用 " 内容 " |
-t | 秒数,限时输入信息 -t 3 #三秒内输入 |
-n |
[root@m01 ~]# vim VAR1.sh #写入以下内容 #!/bin/bash read -p "请输入姓名:" NAME read -p "请输入年龄:" AGE read -p "请输入性别:" SEX cat<<eof ********************* 你的基本信息如下: 姓名: $NAME 年龄:$AGE 性别:$SEX ******************** eof [root@xuegod63 ~]# sh VAR1.sh 请输入姓名:ll 请输入年龄:18 请输入性别:man ********************* 你的基本信息如下: 姓名: ll 年龄:18 性别:man
if condition then command1 command2 ... commandN fi
单分支流程图:
案例:把计算脚本 修改为 if 判断格式
vim cal-awk-if.sh #!/bin/bash num1=$1 num2=$2 #1.检查参数的个数是否正确 [ $# -ne 2 ] && { echo "Usage:$0 num1 num2" exit } #2.检查参数是否为数字 expr $num1 + $num2 &> /dev/null [ $? -ne 0 ] && { echo "Usage:$0 num1 num2" exit 2 } #3.进行计算 awk -vn1=$num1 -vn2=$num2 'BEGIN{print n1/n2}' awk -vn1=$num1 -vn2=$num2 'BEGIN{print n1*n2}' awk -vn1=$num1 -vn2=$num2 'BEGIN{print n1-n2}' awk -vn1=$num1 -vn2=$num2 'BEGIN{print n1+n2}'
修改 比较两个数字大小为 if 格式
vim compare.sh #!/bin/bash read -p "PLS INPUT num:2" n1 n2 num1=$n1 num2=$n2 if [[ "$num1" =~ ^[0-9]+$ && "$num2" =~ ^[0-9]+$ ]] then else echo "Usage:$0 num1 num2" exit 1 fi if [ $num1 -eq $num2 ] then echo "$num1 == $num2" elif [ $num1 -ge $num2 ] then echo "$num1 > $num2" else echo "$num1 < $num2" fi
if condition then command1 command2 ... commandN else command fi
双分支流程图:
案例:查看内存情况,不足发送邮件
vim chk_mem.sh #!/bin/bash mem=$(free -m |awk 'NR==2{print $NF}') if [ "$mem" -lt "10000" ] then echo "Out of Memory" | tee /root/mem.log mail -s "Out of Memory" XXXX@qq.com < /root/mem.log else echo "Memort is ok !" fi
if条件测试操作1 ; then commands elif 条件测试操作2 ; then commands elif 条件测试操作3 ; then commands ....... else commands fi
多分支流程图:
案例:判断系统用户在系统中是否存在,是否有家目录
[root@m01 ~]# vim if-4.sh #!/bin/bash read -p "input a user:" user if grep $user /etc/passwd;then echo "the user $user exists on this system" elif ls -d /home/$user ; then echo "the user $user not exists on this system" echo "$user has a home directory" else echo "the user $user not exists on this system" echo "$user not has a direcotry" fi
[root@m01 /server/scripts]# sh if-4.sh input a user:root root:x:0:0:root:/root:/bin/bash operator:x:11:0:operator:/root:/sbin/nologin the user root exists on this system
格式:test 测试文件
说明 | 示例 | |
---|---|---|
-eq | 等于则为真 | [ "$a" -eq "$b" ] |
-ne | 不等于则为真 | [ "$a" -ne "$b" ] |
-gt | 大于则为真 | [ "$a" -gt "$b" ] |
-ge | 大于等于则为真 | [ "$a" -ge "$b" ] |
-lt | 小于则为真 | [ "$a" -lt "$b" ] |
-le | 小于等于则为真 |
案例1:比较大小
[root@m01 ~]# vim test1.sh #!/bin/bash if test -eq 1 ;then echo "ok" else echo "error" fi
案例2:比较整数大小
[root@m01 ~]# vim test2.sh #!/bin/bash read -p "input var1 var2:" var1 var2 if [ $var1 -gt $var2 ];then echo "$var1 > $var2" elif [ $var1 -lt $var2 ];then echo "$var1 < $var2" else echo "$var1 = $var2" fi
说明 | 示例 | |
== | 等于则为真 | [ "$a" == "$b" ] |
!= | 不等于则为真 | [ "$a" != "$b" ] |
-z 字符串 | 字符串的长度为零则为真 | [ -z "$a" ] |
-n 字符串 | 字符串的长度不为空则为真 | [ -n "$a" ] |
str1 > str2 | str1 大于 str2 为真 | [ str1 \> str2 ] |
str1 < str2 | str1 小于 str2 为真 | [ str1 \ |
案例1:根据用户判断是否是超级管理员
[root@m01 ~]# vim test3.sh #!/bin/bash read -p "input your name:" name if [ $name == "root" ];then echo "you are super administrator" else echo "You are a general user" fi
-
大于号和小于号必须转义,否则
shell
会把它当作重定向 -
大于号和小于号他们的顺序和
sort
排序不一样 -
在
test
比较测试中,它使用的是ASCII顺序,大写字母是小于小写字母;sort 刚好相反
扩展:ASCII 美国信息交换标准代码
[root@m01 ~]# cat test-4.sh #!/bin/bash var1=test var2=Test if [ $var1 \> $var2 ];then echo "$var1 > $var2" else echo "$var1 < $var2" fi
说明 | 示例 | |
---|---|---|
-e 文件名 | 如果文件或目录存在则为真 | [ -e file ] |
-r 文件名 | 如果文件存在且可读则为真 | [ -r file ] |
-w 文件名 | 如果文件存在且可写则为真 | [ -w file ] |
-x 文件名 | 如果文件存在执行权限则为真 | [ -x file ] |
-s 文件名 | 如果文件存在且至少有一个字符则为真 | [ -s file ] |
-d 文件名 | 如果文件存在且为目录则为真 | [ -d file ] |
-f 文件名 | 如果文件存在且为普通文件则为真 | [ -f file ] |
-c 文件名 | 如果文件存在且为字符型文件则为真 | [ -c file ] |
-h 文件名 | 如果文件存在且是软连接文件则为真 | [ -h file ] |
-b 文件名 | 如果文件存在且为块特殊文件则为真 | [ -d file ] |
file1 -nt file2 | 检查file1 是否比 file2 新 | [ file1 -nt file2] |
file1 -ot file2 | 检查file1是否比 file2 旧 |
案例1:清空日志文件
[root@m01 ~]# vim clear-log.sh #!/bin/bash #clear /var/log/messages #确定当前是root用户 if [ $USER != "root" ];then echo "你必须使用root用户才能执行脚本" echo 10 #直接退出 fi #判断文件是否存在 if [ ! -f /var/log/messages ];then echo "文件存在" echo 12 fi #保留最近100行的日志文件 tail -100 /var/log/messages > /var/log/mesg.tmp #日志清理 > /var/log/messages #cat /dev/null > /var/log/messages mv /var/log/messages.tmp /var/log/messages echo "Logs clean up"
三种判断方式
if [条件判断一] && (||) [条件判断二];then 命令一 elif[条件判断三] &&(||) [条件判断四];then 命令二 else 执行其他 fi
if [条件判断一 -a (-o) 条件判断二 -a(-o)条件判断三];then elif [条件判断三 -a(-o) 条件判断四 ];then else 执行其他 fi
判断第三种
if [[条件判断一 &&(||) 条件判断二 ]];then elif [[条件判断三 &&(||) 条件判断四 ]];then else 执行其他 fi
例1:设置umask
[root@m01 ~]# vim /etc/profile 59 if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then 60 umask 002 61 else 62 umask 022 63 fi [root@m01 ~]# vim umask.sh if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then echo "umask 002" else echo "i am root :umask 022" fi [root@m01 ~]# sh umask.sh i am root:umask 022
例2:[[.......]] 和 [.......] 的区别
#判断当前用户是否是root [root@m01 ~]# if [[ $USER == r* ]] ; then echo "hello,$USER" ; else echo $USER not ; fi hello,root
[root@m01 ~]# if [ $USER == r* ] ; then echo "hello,$USER" ; else echo $USER not ; fi root not
[root@m01 ~]# if [[ $USER == [a-z]oot ]] ; then echo "hello,$USER" ; else echo $USER not ; fi
-
所有的字符串与逻辑运算符直接用 ”空格“ ,不能连在一起
-
在 [......] 表达式中,常见的 >、< 需要加转义符 \,大小比较
-
在进行逻辑运算符 &&、|| 比较时;如果用的[ ] 符号,则用在外面,如 [.....] && [....] || [.......] 如果在 [......] 里面进行逻辑与或的比较,则用 -a、-o 进项比较,如 [ x = y -a x < z -o x > m]
-
[[........]] 运算符只是 [.......] 运算符的扩充;能够支持< 、> 符号运算不需要转义符;它还是以字符串比较大小。里面支持逻辑运算符 || 、&&、,不在使用 -a、-o
-
[[.......]] 用 && 而不是 -a 表示逻辑 "与";用 || 而不是 -o 表示逻辑 "或"
-
[[........]] 可以进行算术扩展,而 [........] 不可以
-
[[........]] 能用正则,而 [.......] 不行
-
-
含义 | 实例 | |
---|---|---|
* | 匹配 0 或多个字符 | a*b a与b 之间可以有任意长度的任意字符,也可以一个也没有,如 aabcd,axyzb |
? | 匹配任意一个字符 | a?b a与b之间必须也只能有一个字符,可以是任意字符,如aab,abb,acb,a0b |
[list] | 匹配 list 中的任意单一字符 | a[xyz]b a与b之间必须也只能有一个字符,但只能是 x 或 y 或 z,如:axb ayb azb |
[!list] | 匹配除 list 中的任意单一字符 | a[!0-9]b a与b 之间必须也只能有一个字符,但不能是阿拉伯数字,如 axb,abb,a-b |
[c1-c2] | 匹配 c1-c2 中的任意单一字符 如:[0-9] [a-z] | a[0-9]b 0与9之间必须也只能有一个字符 如 a0b,a1b......a9b |
{string,string,....} | 匹配 string1 或 string2 (或更多)其一字符串 |
例:
[root@m01 ~]# ls /var/log/*.log [root@m01 ~]# ls /vat/log/????.log /var/log/boot.log [root@m01 ~]# touch /opt/a{1,2,3}.txt [root@m01 ~]# ls /opt/a[123].txt /opt/a1.txt /opt/a2.txt /opt/a3.txt [root@m01 ~]# ls /opt/a[1,2,3].txt [root@m01 ~]# ls /opt/a[13].txt /opt/a1.txt /opt/a3.txt
1. 命令行传入1个字符或数字,数字等于1则显示1,如果等于2则显示
如果既不等于1也不等于2,就显示输入不对,然后退出程序
项目分析:
-
-
数字等于1显示1退出
-
数字等于2显示2退出
-
[root@m01 ~]# cat num.sh #!/bin/bash num=$1 #判断参数个数,{ } 花括号里面的命令一块执行 [ $# -ne 1 ]&&{ echo "Usage: input number" exit 1 } #判断是否是数学 #需要填写数字 [ $num -eq 1 ]&&{ echo "input number is 1" exit 0 } [ $num -eq 2 ]&&{ echo "input number is 2" exit 0 } [ $num -ne 1 -a $num -ne 2 ]&&{ echo "Usage: input number" exit 1 }
#使用正则表达式
[root@m01 ~]# cat number.sh #!/bin/bash input=$1 [[ ! "$input" =~ ^(1|2)$ ]]&&{ echo input error exit 1 } [ "$input" -eq 1 ]&&{ echo 1 exit } [ "$input" -eq 2 ]&&{ echo 2 exit }
2. 开发shell脚本,使用传参或read实现比较2个整数大小,用条件表达式(禁用 if )进行判断并输出到屏幕方式提醒用户结果。
项目分析:read / 传参
[root@m01 ~]# cat number.sh #!/bin/bash read -p "please input num1 num2:" n1 n2 num1=$1 num2=$2 [[ "$num1" =~ ^[0-9]+$ && "$num2" =~ ^[0-9]+$ ]] || { echo "Usage: $0 num1 num2" exit 1 } [ $num1 -eq $num2 ] && { echo "$num1 == $num2" exit 0 } [ $num1 -gt $num2 ] && { echo "$num1 > $num2" exit 0 } [ $num1 -lt $num2 ] && { echo "$num1 < $num2" exit 0 } #升级版本 #!/bin/bash read -p "please input num1 num2:" n1 n2 num1=$1 num2=$2 [[ "$num1" =~ ^[0-9]+$ && "$num1" =~ ^[0-9]+$ ]] || { echo "Usage: $0 num1 num2" exit 1 } [ $num1 -eq $num2 ] && { echo "$num1 == $num2" exit 0 } [ $num1 -gt $num2 ] && { echo "$num1 > $num2" exit } [ $num1 -lt $num2 ] && { echo "$num1 < $num2" exit 0 }
它是根据变量的不同进行取值比较,然后针对不同的取值分别执行不同的命令操作
case 变量或表达式 in 变量或表达式1) 命令序列1 ;; 变量或表达式2) 命令序列2 ;; …… *) 默认命令序列 esac
case 语句执行流程控制:
执行过程:
若值与1不相匹配,则继续与值2比较,若取值相同则执行值2后的命令序列,直到遇到双引号” ::“ 后跳转至esac,表示结束分支。
依次类推,若找不到任何匹配的值,则执行默认模式 ” *)
注意事项:
-
-
匹配中的值可以是多个值,通过 ” |
service服务启动nginx服务
[root@m01 ~]# vim /etc/init.d/nginx #!/bin/bash #chkconfig: 2345 99 98 # 上面是chkconfig 默认设置(如果要chkconfig管理就要加上) 2345:启动流程 99:开机顺序 20:关机顺序 # ll /etc/rc.d/rc3.d/S* 不要和里面的服务冲突 #description: nginx-server nginx=/usr/local/nginx/sbin/nginx case $1 in start) netstat -anptu | grep nginx if [ $? -eq 0 ] then echo "nginx-server is already running" else echo "nginx-server begin start" $nginx fi ;; stop) $nginx -s stop if [ $? -eq 0 ] then echo "nginx-server is stoped" else echo "nginx-server stop fail,try again" fi ;; status) netstat -anlpt | grep nginx if [ $? -eq 0 ] then echo "nginx-server is running" else echo "nginx-server is stoped" fi ;; restart) $nginx -s reload if [ $? -eq 0 ] then echo "nginx-server is begin restart" else echo "nginx-server restart fail" fi ;; *) echo "please enter {start restart status stop}" ;; esac exit 0
for var in list do commands done 或: for var in list ; do commands done
for循环流程图:
取值列表有多种取值方式,比如:
1. 可以直接读取 in 后面的值,默认以空格分隔
[root@m01 ~]# cat for-1.sh #!/bin/bash for var in a1 b1 c1 d1 do echo "the text is $var" done
2. 列表中的复杂值,可以使用引号或转义符 ” / “ 来加以约束
[root@m01 ~]# cat for-2.sh #!/bin/bash for var in a1 "c1 d1" e2 "hello world" do echo the text is $var done [root@m01 ~]# cat for-3.sh #!/bin/bash for var in a1 b\'1 "c1 d1" e2 "hello world" |\'s a22 do echo the text is $var done
3. 从变量中取值
[root@m01 ~]# cat for-4.sh #!/bin/bash list="a1 b1 c1 d1" for i in list do echo is a $1 done
4. 从命令中取值
[root@m01 ~]# cat for-5.sh #!/bin/bash for i in `cat /etc/hosts` do echo "$i" done
5. 自定义shell 分隔符
指定单个字符做分隔符:
IFS=: #以:冒号做分隔符
可以指定多个
如 IFS='\n:;' #这样赋值会将反斜杠、n、冒号、分号和双引号作为字段分隔符
注:$'\n' 与 '\n:;' #这个赋值会将反斜杠、n、冒号、分号和双引号作为字段分隔符。
注:$'\n' 与'\n' 时的区别
IFS='\n' #将字符 \ 和字符 n 作为 IFS 的换行符。
for ((i=0;i<10;i++)) do commmmands done
例1:单个变量。输入1到10之间的数字
[root@m01 ~]# cat for-8.sh #!/bin/bash for ((i=1;i<=10;i++)) do echo num is $i done ********************************************** 互动: i++ 这一条语句在for循环体中哪个位置执行? for (( i=1 ; i<=10 ; )) #i=1 只赋值一次。然后执行 i <= 10 do echo num is $i i=$(($i+1)) # i++在这里执行。 当for循环体中所有命令执行完后,再执行i++ done
例2:多个变量。同时输入 1-9的升序和降序
[root@m01 ~]# cat for-9.sh #!/bin/bash for ((a=1,b=9;a<10;a++,b--)) do echo num is $a-$b done
例3:使用for循环在/tmp目录下通过随机小写10个字母加固定字符串wei批量创建10个html文件。
#生成随机字符串方法 [root@m01 ~]# openssl rand -base64 10 Sm8rkVboxdDl9Q== [root@m01 ~]# date +%N | md5sum 3da7567b4e32f56cf78308e37133b1e0 - [root@m01 ~]# echo $RANDOM 27267 [root@m01 ~]# echo $((RANDOM+1000000)) 1022112 [root@m01 ~]# tr -cd 'a-zA-Z0-9' </dev/urandom | head -c 10 5z3CKhbJxm[root@m01 ~]#
[root@m01 ~]# cat tpuc-random.sh #!/bin/bash [ -d /tmp/ ] || mkdir /tmp for n in {1..10} do rand=`tr -cd 'a-zA-Z' </dev/urandom | head -c10` file=${rand}_wei.html touch /tmp/$file done #第二种方法 #!/bin/bash dir=/test/ create(){ i=1 while (($i<11)) do cd $dir && touch `tr -dc "a-z"</dev/urandom | head -c 10`_wei.html i=$(($i+1)) done } check(){ if [ -d $dir ]; then create else mkdir $dir create } check #第三种方法 #!/bin/bash dir=/test/ mkdir -p /test for i in `seq 10` do file=$(date +%N%s | md5sum | head -c 10 | tr [0-9] [a-j]) touch ${dir}${file}_wei.html done #tr #-d 删除 #-c 取反 排除 #tr -cd 'z-a' <
例4:99乘法表
#!/bin/bash for i in {1..9} do for j in {1..9} do if [ $i -ge $j ];then res=$((i*j)) echo -n
while-do-done
重复测试之指令的条件,只要条件成立就反复执行对应的命令操作,直到命令不成立或为假;
语法格式如下:
while 测试命令 do 命令 done
[root@m01 ~]# cat while-1.sh #!/bin/bash var=10 while [ $var -gt 0 ] do echo $var var=$[$var-1] done
例2:输入如下两数据相乘的效果
自增操作 let var++
自减操作 let var--
[root@m01 ~]# cat while-2.sh #!/bin/bash num=1 while [$num -lt 10] do sum=$(( $num*$num )) echo "$num*$num = $sum" ((num++)) # let num++ done
2. 镶套语句
例1:批量创建用户---密码随机
编写思路:
- 脚本功能:手动输入用户名和数量,批量创建系统用户
- 关键命令:密码根据date随机产生不相同
- 使用到的语法:选择、循环、分支
[root@m01 ~]# cat while-3.sh #!/bin/bash #创建一个批量创建脚本 read -p "please input you user name|number:" name num cat<<eof ********************* user name:$name user number: $num ******************** eof for i in $(seq $num) do user=${name}${i} id $user &> /dev/null if [ $? -ne 0 ]; then useradd $user pass=`date +%s|md5sum|cut -c 1-8 ` sleep 1 echo " $user:$pass" >> /tmp/passwd.log echo $pass | passwd --stdin $user &> /dev/null echo " user $user Password creation completed !" else echo " This user already exists!" fi done
例2:批量创建 a.txt 文件中的用户
[root@m01 ~]# cat a.txt #!/bin/bash dba01 dba02 web01 web02 ssl01
编写脚本的思路
- 明确脚本作用:将文件中的用户创建完成
- 编写脚本需要的命令:useradd passwd
- 把变化的数据使用变量表示
- 选择合适的流程控制 (选择、循环、分支)
[root@m01 ~]# cat for-address.sh #!/bin/bash for name in `cat/root/a.txt` #for name in $(cat /root/a.txt) do id $name &> /dev/null if [ $? -ne 0 ];then useradd $name echo "123456" | passwd --stdin $name &> /dev/null echo "user $name created" else echo "user $name is exit" fi done
[root@m01 ~]# cat for-for.sh #!/bin/bash for i in `seq 9` do for j in `seq $i` do echo -n "$i*$j= `echo $(($i*$j))`" done echo " " done
#!/bin/bash #找到访问的IP地址在进行排序,访问失败超过100次的IP地址进行拒绝 res=/server/scripts/ip_conn.txt awk -F "[ :]+" '/^tcp/ && /ESTABLISHED/{print $6}' /server/scripts/netstat.log |sort| uniq -c| sort -nr > $res while read cnt ip do if [ $cnt -eq 172.16.1.{1..253}];then else #echo "$cont次数 $ip 地址" if [ $cnt -gt 2 -a `iptables -nL | grep -wc "$ip"` -eq 0 ] then iptables -I INPUT -s $ip -j DROP fi fi done<$res
Continue:跳过本次循环,进行下次循环
break 概述: 跳出当前整个循环或结束当前循环,在for、while 等循环语句中,用于跳出当前所在的循环体,执行循环体之后的语句,后面如果什么也不加,表示跳出当前循环等价与 break 1,也可以在后面加数字,假设 break 3 表示跳出第三层循环。
continue 概述:忽略本次循环剩余的代码,直接进行下次循环; 在 for、while 等循环语句中,用于当前坐在的循环体,执行循环体之后的语句,如果后面加的数字是1,表示忽略本次条件循环,如果是2的话,忽略下来 2次条件的循环。
[root@m01 ~]# cat break-continue.sh #!/bin/bash while true do echo "*********************" echo "Please select your operation:" echo "1 Copy" echo "2 Delete" echo "3 Backup" echo "4 Quit" echo "**********************" read op case $op in 1) continue #这里加了continue 后,后面的echo命令就不执行了 echo "your selection is Copy" ;; 2) echo "your selection is Delete" ;; 3) echo "your selection is Backup" ;; 4) echo "Exit ......" break #跳出循环体 ;; *) echo "incalide selection,please try again" esac done
例2:使用交互式批量添加用户
[root@m01 ~]# adduser.sh #!/bin/bash while : do read -p "Please enter prefix & password & num:" pre pass num printf "user information: ********************* user prefix: $pre user password: $pass user number: $num ******************** " read -p "Are you sure?[y/n] " action if [ "$action" == "y" ];then break fi done for i in $(seq $num) # 从i =1开始,取到 $num 。 seq 表示 1-$num #$(seq $num) 等于 ` seq $num ` ; $( 命令 ) ; ${ 变量 } ; [ 表达式/条件 ] do user=${pre}${i} id $user &> /dev/null if [ $? -ne 0 ];then useradd $user echo "$pass"|passwd --stdin $user &> /dev/null if [ $? -eq 0 ];then echo -e "\033[31m$user\033[0m creat" #以红色来显示用户名 fi else echo "user $user exist" fi done
#使用()直接数组 days1=(one two three four five) #或者在()中声明下标,默认从0开始 days2=([0]=’one’ [1]=’two’ [2]=’three’ [3]=’four’) #下标可以不连续 days3=([0]=’one’ [2]=’three’) #直接赋值(批量) arr=( 10.0.0.61 10.0.0.71 10.0.0.81 )
ip[0]=10.0.0.1 ip[1]=10.0.0.2 ip[2]=10.0.0.3 ip[3]=10.0.0.4 echo ${ip[*]} #数组中的所有内容取出
array=(`cat /server/scripts/ip.txt`) echo ${array[*]}
ip=(10.0.0.1 10.0.0.2 10.0.0.3 10.0.0.4) echo ${ip[0]} #取出第一个元素 echo ${ip[2]} #取出第二个元素
${数组名[@]}得到是以空格隔开的元素,可以用在数组遍历中;${数组名[*]}得到的是一整个字符串。
[root@m01 ~]# vim check_ip.sh #!/bin/bash . /etc/init.d/functions #ip_list=(10.0.0.61 10.0.0.71 10.0.0.81) ip_list=( 10.0.0.61 10.0.0.71 10.0.0.81 ) for ip in ${ip_list[*]} do { ping -c1 -W1 -i1 $ip &> /dev/null if [ $? -eq 0 ] then action "$ip is ok" /bin/true else action "$ip is failed" /bin/false fi }& usleep 500 #单位:微秒 done [root@m01 ~]# sh check_ip.sh 10.0.0.61 10.0.0.71 10.0.0.81
例2:试编写一个shell程序,该程序能接受用户从键盘输入的10个整数,然后求出其总和、平均值
#方法一 [root@m01 ~]# cat array-1.sh #!/bin/bash file=/server/files/num.txt [ -s "$file" ]&& > $file for n in {1..5} do read -p "input ${n}number:" num echo $num >> $file done awk '[sum+=$1]END{print sum,sum/NR}' $file #方法二: [root@m01 ~]# cat array-2.sh #!/bin/bash file=/server/files/num.txt [ -s "$file" ]&& > file for n in {1..5} do read -p "input ${n}number:" num echo $num >> $file done arr=(`cat $file`) sum=0 for num in ${arr[@]} do ((sum+=num)) done awk -vn=$sum 'BEGIN{print n,n/5}'
$1 <--- $2, $2 <--- $3, $3 <--- $4, 以此类推.
[root@m01 ~]# shifs.sh #!/bin/bash # 用 'shift'命令逐步存取所有的位置参数 # 给这个脚本一个命名,比如说shft, #+ 然后以一些参数来调用这个脚本,例如 # ./shft a b c def 23 skidoo until [ -z "$1" ] # 直到所有的位置参数被存取完... do echo -n "$1 " shift done echo # 换行. exit 0
例子:加法计算器
[root@m01 ~]# cat shift.sh #!/bin/bash if [ $# -le 0 ];then echo “没有足够的参数” exit fi sum=0 while [ $# -gt 0 ] ; do #sum=$(expr $sum + $1) sum=$[$sum+$1] shift # shift 2 一次移动2个参数 done echo result is $sum
测试:
[root@m01 ~]# bash a shift.sh 11 2 3 4 result is 20
function name { commands }
方法二:name 后面的括号表示你定义一个函数
name(){
commands
}
函数名 参数1 参数2
[root@m01 ~]# cat funcTest.sh #!/bin/bash parameters1=$1 #全局变量 parameters2=$2 #全局变量 function testFun() { #定义函数testFun local cc="this is cc" #定义局部变量$cc local dd="this is dd" #定义局部变量$dd echo $parameters1, $parameters2 #访问参数1和参数2 echo $cc #打印局部变量 return 0 #shell函数返回值是整形,并且在0~257之间。 } echo $dd #这里将会打印不生效,因为dd是局部变量。 testFun #使用函数testFun
[root@m01 ~]# cat fun-1.sh #!/bin/bash function fun_1 { echo "this is function" } function fun_1 { echo "this is 12306" } fun_1
[root@m01 ~]# cat fun-1.sh #!/bin/bash function fun_1 { echo "this is function" ls /etc/passwd return 3 } fun_1 [root@m01 ~]# bash fun-1.sh this is function /etc/passwd [root@m01 ~]# echo $? 3
[root@m01 ~]# cat fun-3.sh #!/bin/bash fun1(){ read -p "input a value:" va echo $[$va*5] } num=$(fun1) echo current num is $num [root@m01 ~]# sh fun-3.sh input a value: 22 current num is 110
[root@m01 ~]# cat fun-4.sh a.txt #!/bin/bash fun1(){ rm -rf $1 } fun1 $1
第二种:调用函数直接传递参数
[root@m01 ~]# touch /root/a.txt [root@m01 ~]# cat fun-4.sh #!/bin/bash fun1(){ rm -rf $1 } fun1 /root/a.txt [root@m01 ~]# bash fun-1.sh #测试 [root@m01 ~]# ls /root/a.txt ls: cannot access /root/a.txt: No such file or directory
第三种:函数中多函数传递和使用方法
[root@m01 ~]# cat fun-5.sh #!/bin/bash fun1(){ echo $[$1*5] echo $[$2*5] } fun1 5 2 #直接传两个参数 [root@m01 ~]# bash fun-1.sh #测试 25 4
[root@m01 ~]# cat fun-5.sh #!/bin/bash function fun1{ num1=$[$vsr1*2] } read -p "input a num:" var1 fun1 echo the new value is: $num1 [root@m01 ~]# bash fun-1.sh input a num:2 the new value is:4
格式1:echo -e "\033[背景颜色;文字颜色 m 要输出的字符 \033[0m"
格式2:echo -e "\e[背景颜色;文字颜色 m 要输出的字符\e[0m"
[root@m01 ~]# echo -e "\E[1;31m红色字\E[0m" 红色字 [root@m01 ~]# echo -e "\033[31m红色字 \033[0m" 红色字
注:其中 42 的位置代表底色,34 位置代表的是字的颜色,0m 是清除所有格式
-
-
文字颜色后面有个 m
-
字符串前后可以没有空格,如果有的话,输出也是同样有空格
-
控制选项:
-
-
\033[1m 设置高亮度,加粗
-
常见 shell 输出带颜色文字:3x 代表字的颜色,4x 代表背景色
echo -e "\033[30m 黑色字 \033[0m" echo -e "\033[31m 红色字 \033[0m" echo -e "\033[32m 绿色字 \033[0m" echo -e "\033[33m 黄色字 \033[0m" echo -e "\033[34m 蓝色字 \033[0m" echo -e "\033[35m 紫色字 \033[0m" echo -e "\033[36m 天蓝字 \033[0m" echo -e "\033[37m 白色字 \033[0m" echo -e "\033[40;37m 黑底白字 \033[0m" echo -e "\033[41;37m 红底白字 \033[0m" echo -e "\033[42;37m 绿底白字 \033[0m" echo -e "\033[43;37m 黄底白字 \033[0m" echo -e "\033[44;37m 蓝底白字 \033[0m" echo -e "\033[45;37m 紫底白字 \033[0m" echo -e "\033[46;37m 天蓝底白字 \033[0m" echo -e "\033[47;30m 白底黑字 \033[0m"
[root@m01 ~]# yum install -y mariadb mariadb-server -y # mariadb 是mysql的客户端命令;mariadb mariadb-server 是服务端命令 [root@m01 ~]# rpm -qf /usr/bin/mysql #查看数据库是否安装 mariadb-5.5.56-2.el7.x86_64 [root@m01 ~]# systemctl start mariadb 登录 mysql: [root@m01 ~]# mysqladmin -uroot password "123456" #给root一个登录密码,实际生产环境下密码更为复杂 [root@m01 ~]# mysql -uroot -p123455 MariaDB [(none)]> show databases; MariaDB [(none)]> create database china; #创建china数据库 MariaDB [china]> create table user (id int); #创建 user 表只有一个id字段 MariaDB [china]> insert into user values(1); #插入一条记录,id字段值1 MariaDB [china]> insert into user values(2); #插入一条记录,id字段值2 MariaDB [china]> select *from user; #查看表中的数据 +------+ | id | +------+ | 1 | | 2 | +------+
mysql 自动化备份脚本:
思路:
-
-
运行要执行的命令:备份,导入数据
-
把命令执行过程中的没有用的文件删除
-
[root@m01 ~]# cat mysql-back-auto.sh #!/bin/sh #auto backup mysql #Define PATH定义变量 BAKDIR=/data/backup/mysql/`date +%Y-%m-%d` MYSQLDB=xuegod #MYSQLDB=webapp MYSQLUSR=root #MYSQLUSR=backup MYSQLPW=123456 #MYSQLPW=backup #mysql数据库密码 #must use root user run scripts 必须使用root用户运行,$UID为系统变量 if [ $UID -ne 0 ];then echo This script must use the root user ! ! ! sleep 2 exit 0 fi #Define DIR and mkdir DIR 判断目录是否存在,不存在则新建 if [ ! -d $BAKDIR ];then mkdir -p $BAKDIR else echo This is $BAKDIR exists.... exit fi #Use mysqldump backup mysql 使用mysqldump备份数据库 /usr/bin/mysqldump -u$MYSQLUSR -p$MYSQLPW $MYSQLDB > $BAKDIR/${MYSQLDB}_db.sql cd $BAKDIR ; tar -czf ${MYSQLDB}_db.tar.gz *.sql #查找备份目录下以.sql结尾的文件并删除 find $BAKDIR -type f -name *.sql -exec rm -rf {} \; #或 #如果数据库备份成功,则打印成功,并删除备份目录30天以前的目录 [ $? -eq 0 ] && echo “This `date +%Y-%m-%d` MySQL BACKUP is SUCCESS” cd /data/backup/mysql/ && find . -type d -mtime +30 |xargs rm -rf echo "The mysql backup successfully "