Shell(1):基础
0、常用
规则
1)运算符和变量之间需要有空格
2)if、中括号、表达式间均需要空格
3)开头:#!/bin/bash,标注你的shell类型
4)路径写绝对路径,不然很容易混淆
5)与写在sh脚本中的bash语句相比,如果将指令直接写在命令行中,那么行与行之间用分号区别(例如for循环那一句都要用分号);
6)如果用到了 管道 |,那么需要把前半句话用括号包括(相当于管道的数据来源是个整体),不然可能导致预期之外的输出;
算数运算:
整数
- val = $[$a 运算符 $b](个人觉得最好用)
- val =` expr $a 运算符 $b`
- val = $(expr $a 运算符 $b)
val=$[2 + 2] val=$(expr 2 + 2) val=`expr 2 + 2`
自增/减:let
n=1 let n++ echo $n 2 let n-- echo $n 1
小数:bc
echo " 2 * 0.5 " | bc 1.0 #设置精度(小数点后的位数) echo " scale=2; 1.74 / 3 "| bc .58 echo " scale=3; 1.74 / 3 "| bc .580
在语句中传入表达式运算
- ` 表达式 `
- $(表达式)
例如把某个文件作为for循环的循环体:
for i in `cat test.txt`
for i in $(cat test.txt)
do
xxx
done
数组
A=(A B C D) #输出全部 echo ${A[@]} echo ${A[*]} #输出长度 echo ${#A[@]} echo ${#A[*]} #输出单个元素,下标从0开始 echo ${A[1]} #B #for循环 for i in ${A[@]} do echo $i done
for循环
#循环输出当前目录下所有文件的行数、单词数、字符数 for i in $(ls) do wc $i done 5795 72282 447054 redis_6379.log 38 485 3849 sentinel.log
#配合{}实现自定义循环内容
#这里构造了两个前半截相同,最后一节为11和12的IP地址进行循环输出
for i in 192.168.1.1{1,2}
do
echo $i
done
192.168.1.11
192.168.1.12
while死循环(注意while后的冒号)
while : do 循环体 #每60s执行一次 sleep 60 done
if
#判断某个指令的执行结果是否为空 if [ `grep abc test.txt`] then echo "OK" fi #如果grep结果中包含空格,则可以写为 if [ `grep abc test.txt | awk '{print $1}'` ] then echo "OK" fi
- wc:统计
wc [选项] 文件名 选项: -l:只统计行数 -w:只统计单词数 -m:只统计字符数
-L:最长行的长度
- 将一个文件内容清空
true > 1.txt
变量运算
val =$[2 + 2] val =$[$val1+$val2]
重启nginx
if [ 条件 ] then /usr/local/nginx/sbin/nginx -s reload 2> /dev/null
fi
将某个需要执行的指令用 2> /dev/null即可,见/dev/null
2)一些特殊字符
符号 |
说明 |
用法 |
; | 多条命令顺序执行,命令间不互相干扰 | 命令1 ; 命令2 |
| |
①正则表达式中表示或; ②用于命令间,表示用前边命令的输出作为后边命令的输入 |
①ab(c|d) ②命令1 | 命令2 |
& |
①把命令放在后台执行 ②标准输出和标准错误输出,具体见输入输出重定向一节 |
①java -jar xxx.jar & |
&& | 前边指令执行成功才执行后边指令 |
1)如果用在if条件中,可以当做或、与使用 if [ $a -eq 00 ] || [ $a -eq 12 ] 2)单独使用时,可以实现if的逻辑(||代表if [ 公式 ] false,&&代表if [公式] true) #输入一个数字 #1)如果比当前最小值小,就把当前最小值更改为该数字; #2)如果比当前最大值大,就把当前最大值更改为该数字。 MIN=0 MAX=100 read -p "输入一个数字:" INT [$MIN -lt $INT] || MIN=$INT [$MAX -gt $INT] || MAX=$INT
|
|| |
前边指令执行失败才执行后边指令 |
|
# |
①注释 ② $#:命令行参数个数 ③${#变量}:变量(字符串)长度 ④${#数组[@]}:数组长度 |
|
$ |
变量: ①$a:变量的值 命令行: $0:当前脚本程序名称 $1-$n:第n个命令行参数 $*:所有命令行参数,整体输出 $@:所有命令行参数,分别输出 $#:命令行所有参数的个数 进程 $?:最后一次执行状态 $$:当前进程的进程号pid $!:后台运行的最后一个进程的进程号 awk $0:某一行的全部字段数据 $1-$n:某一行的第n个字段数据 |
|
~ |
字符串是否包含某个子串 |
awk '$2 ~ /Sc/' 第二字段中是否包含"Sc"字符 |
{} |
正则:表示范围 循环:指定循环范围 匿名函数 命令:{}中的命令,是在当前shell执行 |
192.168.1.1{1,2}代表192.168.1.11和192.168.1.12 |
:= |
${var:="defaultvalue"} 当变量var为空(未定义时),为它赋一个默认值 |
event=${event:="on"} 如果event存在,就用存在的值 如果event不存在,就用"on"当它的值 |
|
||
#* | 匹配最左侧的符号 |
裁剪字符
学习自:shell中 #*, ##*, %*, %%*的含义及用法_shell #*-CSDN博客
用法:变量名|裁剪指令|匹配符号
这里的分隔符|,只是为了方便区分,实际上三者之间并无任何分隔符。
1)#*、##*
说明
#*:最小匹配,并移除匹配位置左边的所有字符
##*:最大匹配,并移除匹配位置左边的所有字符
例子
1 2 3 4 5 6 7 8 | VAR= "abc/opt/software/hadoop/a.txt" VAR=${VAR #*/}#最短匹配,匹配第一个斜杠/,即abc/opt,此时abc/都会被删除 echo $VAR<br><br>opt/…… /a .txt VAR= "abc/opt/software/hadoop/a.txt" VAR=${VAR ##*/} #最长匹配,匹配最后一个斜杠/,即……doop/a.txt,此时……doop/都会被删除 echo $VAR<br> a.txt |
2)%*、%%*
说明
%*:最小匹配,从右起匹配第一个字符并移除该字符右侧所有字符
%%*:最大匹配,从右往左匹配最后一个字符并移除该字符右侧所有字符
例子
1 2 3 4 5 6 7 8 9 10 11 12 | VAR= "/opt/software/hadoop/a.txt" VAR=${VAR%*/} #从右往左找到第一个斜杠,并删除它及它左边所有字符 echo $VAR /opt/ …… /hadoop VAR= "opt/software/hadoop/a.txt" VAR=${VAR%%*/} #从右往左找到最后一个斜杠,并删除它及它左边所有字符 echo $VAR opt |
$
变量 |
作用 |
$n |
n为数字 $0:当前脚本程序名称 $1-9:第1-9个参数 ${10}:10以上的参数需要用大括号包含 |
$* | 命令行中的所有参数,以一个整体输出 |
$@ | 命令行中的所有参数,不同参数用@加以区分 |
$# | 命令行中所有参数的个数 |
预定义变量 |
作用 |
$? |
最后一次执行的命令的返回状态: 0:正确执行 非0:不正确 |
$$ | 当前进程的进程号PID |
$! | 后台运行的最后一个进程的进程号 |
1、什么是Shell?
Shell是用C编写的程序,它是用户使用Linux的桥梁,Shell既是一种命令语言,也是一种程序设计语言。
Shell为提供了一个界面,用户通过这个界面访问操作系统内核的服务。
2、Shell分类
在Linux中输入指令:
cat /etc/shells #查看全部SHELL
echo $SHELL #默认SHELL
Linux中默认的Shell是/bash/bash,流行的有ash、bash、ksh、csh、zsh。
3、Shell编写规范
注意Shell脚本与Shell指令的区别:
Shell指令:其实就是Linux指令。
Shell脚本:按照一定语法规则编写的Shell文件。
shell脚本文件执行
首先编写一个Shell脚本
[... ~]$vim test.sh
#!/bin/bash
echo "hello world"
两种执行方式:
①给文件增加执行权限
[root@localhost ~]$chmod u+x test.sh
[root@localhost ~]$ ./test.sh #绝对路径或者相对路径执行
②通过bash调用脚本
bash test.sh
或者
bash "test.sh"
1)文件头
#!/bin/bash [告知系统该脚本所使用的的shell解释器]
shell指令
2)命名规范
以.sh结尾,.sh是Linux下bash shell的默认后缀。
3)Bash常用快捷键(在Linux命令行模式下使用)
快捷键 |
命令 |
CTRL+A |
将光标移动至命令行开头 |
CTRL+E | 将光标移动至命令行结尾 |
CTRL+C | 强制终止当前命令 |
CTRL+L | 清屏,相当于clear |
CTRL+U | 删除/剪切光标之前的所有字符 |
CTRL+K | 删除/剪切光标之后的所有字符 |
CTRL+Y | 粘贴 |
CTRL+R |
在历史命令中搜索,按下CTRL+R就会出现搜索页面。 在其中输入搜索内容就可以从历史命令中搜索。 |
CTRL+D | 退出终端 |
CTRL+Z | 暂停,并放入后台 |
CTRL+S |
暂停输出 |
CTRL+Q | 恢复输出 |
4)输入输出重定向
①标准输入输出
设备 | 设备名 | 文件描述符 | 类型 |
---|---|---|---|
键盘 | /dev/stdin | 0 | 标准输入 |
显示器 | /dev/stdout | 1 | 标准输出 |
显示器 | /dev/stderr | 2 | 标准错误输出 |
②输入重定向
- 输入重定向:不使用标准输入端口(也就是①中所说的键盘)输入文件,而是使用指定的文件作为标准输入设备。
- 修改:使用"<"符来修改标准输入设备。
类型 | 符号(语法) | 功能 |
---|---|---|
标准输入 | 命令<文件1 | 命令把文件1的内容作为标准输入设备 |
标识符限定输入 | 命令<<标识符 | 命令把标准输入中读入内容,直到遇到“标识符”分解符为止 |
输入输出重定向(同时使用) | 命令< 文件1 >文件2 | 命令把文件1的内容作为标准输入,把文件2作为标准输出。 |
③输出重定向
- 输出重定向:把输出信息写入到文件中,而非控制台(显示屏)。如果没有重定向,那么默认的输出设备是控制台。
类型 |
指令 |
效果 |
标准输出重定向 | 命令 > 文件 | 覆盖,把命令的正确输出内容输出到指定的文件 |
命令 >> 文件 | 追加,把命令的正确输出内容输出到指定的文件 | |
标准错误输出重定向 | 错误命令2 > 文件 | 覆盖,把命令的错误输出内容输出到指定的文件 |
错误命令2 >> 文件 | 追加,把命令的错误输出内容输出到指定的文件 | |
正确输出和错误输出同时保存 | 命令 > 文件 2>&1 | 覆盖,把正确输出和错误输出都保存到同一个文件 |
命令 >> 文件 2>&1 | 追加,把正确输出和错误输出都保存到同一个文件 | |
命令 &> 文件 | 覆盖,把正确输出和错误输出都保存到同一个文件 | |
命令 &>> 文件 | 追加,把正确输出和错误输出都保存到同一个文件 | |
命令 >> 文件1 2>>文件2 | 追加,正确输出追加到文件1中,错误输出追加到文件2 |
用法
command > file 将输出重定向到 file。 command < file 将输入重定向到 file。 command >> file 将输出以追加的方式重定向到 file。 n > file 将文件描述符为 n 的文件重定向到 file。 n >> file 将文件描述符为 n 的文件以追加的方式重定向到 file。 n >& m 将输出文件 m 和 n 合并。 n <& m 将输入文件 m 和 n 合并。 << tag 将开始标记 tag 和结束标记 tag 之间的内容作为输入。
例子
echo "hello world" > test7.sh
5)指令执行流程
标志 |
用法 |
效果 |
; | 命令1;命令2 | 顺序执行 |
&& | 命令1&&命令2 | 只有当命令1正确执行,才会执行命令2 |
|| | 命令1||命令2 | 只有当命令1不正确执行,才会执行命令2 |
6)全局规则
set +/-[选项]
其中 -代表设定某选项,+代表取消该选项,单set可以查看所有变量。
选项有:
- u:调用未声明变量是否报错(默认无反应);
- x:命令执行前,先把命令输出一次(即输出命令和命令结果)
[root@localhost ~]$ set -u #未定义变量报错
[root@localhost ~]$ echo $file
-bash: file: unbound variable
[root@localhost ~]$ set -x #指令重现
[root@localhost ~]$ ls
+ls --color=auto
test.sh
四、shell变量
1)何为shell变量
在一个脚本周期内,其值可以发生改变的量就是变量。
shell变量在其定义之前就使用,比如在100行用赋值号创建了某个变量,但是你可以在第10行就用echo把这个变量输出出来(这一点和Java、C、Python语言都大不相同)。
另外,如果使用了一个错误的或者未定义的变量,那么实际sh执行过程中不会报错,但是由于该变量不存在,所以会把该$变量自动替换为空(空字符串)。
2)变量命名
- 赋值号左右两侧不能有空格;变量的值如果有空格,那么该值要用单引号或双引号包括。例如test=“hello world!”。
- 环境变量名大写;
- 如果要进行占位赋值,可以用"$变量名"、"${变量名}"、${变量名}、$变量名
带双引号输出的作用:安全,如果输出的内容中包含空格,使用双引号会正确识别空格并完整输出。
test=123 test="$test"456 test=${test}456 #以上两种方式分开执行的话 #结果都是123456
示例
name=sc #①单引号 echo '$name' $name #②双引号 echo "${name}" sc echo "$name" sc #③其他一些输出 echo ${name} echo $name sc
在输出单个变量时,加不加双引号效果一样,这点跟Python一样灵活,相当于$就是通知了一个占位符。
echo
-
用法:类似python的print,可以把内容输出到屏幕上
以下代码的输出结果相同
echo "${name}" echo "$name" echo ${name} echo $name
-
关于单双引号
- 双引号能①识别变量,实现②转义功能(类似于转义斜杠“\”)
- 单引号不能识别变量,也不能转义,只会原样输出。
- 如果反引号要执行某段指令,那么其中的变量$i必须用双引号包括,才能够正确解析出来,例如:
echo `grep "$i" /tmp/test.txt`
当i=/data/123.txt时,上文会执行指令grep /data/123.txt /tmp/test.txt并将结果输出,
如果用单引号:
echo `grep '$i' /tmp/test.txt`
则是执行指令grep '/data/123.txt' /tmp/test.txt,会把这个单引号也引入grep中进行执行。
所以反引号中的变量,要用双引号包括。
如果只是用于显示无疑义的字符串,那么在用echo输出时,加不加引号的效果是一样的:
echo "hello world" echo hello world #效果相同
-
转义
echo "\"hello world\"" "hello world"#两个""被转义并被正常输出
-
换行
echo -e "hello\n" #-e开启转义
hello
#多出来一个空行
-
不换行
echo -e "abc! \c" #\c 不换行 abc! #不换行 接下来的命令将继续在"abc!"这一行输入
特殊符号
符号 |
效果 |
单引号 '' | 单引号内的所有字符都视为普通字符 |
双引号 "" |
双引号内的部分字符视为普通字符,除了$、反引号`、\ $:调用变量的值(类似C语言中的&) `:引用命令 \:转义 |
反引号 `` |
反引号内代表系统命令(可以视为执行引号内的指令返回的内容),而不是普通变量。比如`date`就代表系统时间 但不常用,通常为了避免眼花,常用$()代替: echo `date` echo $(date) |
$() |
1、效果同反引号,用来引用系统命令(常用) 2、如果要在shell脚本中使用Linux指令,需要用$()包括 |
() | 用于一串命令执行,()中的命令会在子shell中运行 |
{} |
1、用于一串命令执行,{}中的命令会在当前shell中执行; 2、利用${变量}或$变量实现变量占位输出 |
[ ] |
1、用于变量测试 2、返回true或false的运算 |
# | 注释 |
$ | 获取变量值,用法${变量}或$变量 |
\ | 转义字符,跟在其后的字符将变为普通字符 |
3)变量的类别
- 用户自定义变量:我们自定义的变量,最常见;
- 环境变量:和系统环境相关的变量;分为用户自定义环境变量和系统自带环境变量。
- 位置参数变量:用于向脚本中传递参数或数据,变量名和作用都是固定的;
- 预定义变量:Bash中定义好的变量,变量名和作用都是固定的;
①用户自定义变量
-
声明:变量名=变量值
-
调用:echo $name或echo ${name}
- 变量删除:unset 变量名
-
规则设置:set +/-[选项]
其中 -代表设定某选项,+代表取消该选项,单set可以查看所有变量。
选项有:
-
- u:调用未声明变量是否报错(默认无反应);
- x:命令执行前,先把命令输出一次
[root@localhost ~]$ set -u #未定义变量报错 [root@localhost ~]$ echo $file -bash: file: unbound variable [root@localhost ~]$ set -x #指令重现 [root@localhost ~]$ ls +ls --color=auto test.sh
只读变量
-
声明:
变量名=变量值 readonly 变量 #不写变量名会默认上一个
-
作用:接下来这个变量就无法修改了(再赋值)
②环境变量
- 声明: export 变量名=变量值
- 查询:env [变量名](set可以查看所有变量,env只能查看环境变量,不加变量名可以查看所有环境变量)
- 删除:unset 变量名
[root@localhost ~]$ env HOSTNAME=localhost.localdomain #主机名 SHELL=/bin/bash #当前的shell TERM=linux #终端环境 HISTSIZE=1000 #历史命令条数 SSH_CLIENT=192.168.4.1594824 22 #当前操作环境是用ssh连接的,这里记录客户端ip SSH_TTY=/dev/pts/1 #ssh连接的终端时pts/1 USER=root #当前登录的用户
- 用法:$变量名
和常规变量的用法一样,只是无需声明,可以直接在shell脚本中用$环境变量名输出。
echo $NGINX_VERSION 1.16.1
③位置参数/命令行参数
变量名、用法、作用都是固定的
变量 |
作用 |
$n |
n为数字 $0:当前脚本程序名称 $1-9:第1-9个参数 ${10}:10以上的参数需要用大括号包含 |
$* | 命令行中的所有参数,以一个整体输出 |
$@ | 命令行中的所有参数,不同参数用@加以区分 |
$# | 命令行中所有参数的个数 |
命令行参数如何设置和传入呢?
在用bash xxx.sh调用bash文件时,通过罗列传入:
bash Test.sh p1 p2
这样,$0就代表Test.sh,$1代表第一个参数,即p1,$2代表第二个参数,即p2。
$*与$@的区别
二者都是输出命令行所有参数,但是$*将这些参数作为一个整体输出,而$@则分别输出这些参数。
[root@localhost sh]$ vi parameter2.sh #!/bin/bash for i in"$*" #$*中的所有参数是一个整体,所以这个for循环只会循环一次 do echo "The parameters is: $i" done x=1 for y in"$@" #$@中的每个参数都是独立的,所以“$@”中有几个参数,就会循环几次 do echo "The parameter$x is: $y" #输出变量y的值 x=$(( $x +1 )) #然变量x每次循环都加1,为了输出时看的更清楚 done
④预定义变量
名称固定,作用固定
预定义变量 |
作用 |
$? |
最后一次执行的命令的返回状态: 0:正确执行 非0:不正确 |
$$ | 当前进程的进程号PID |
$! | 后台运行的最后一个进程的进程号 |
用法
$?
[root@localhost sh]$ ls count.sh hello.sh parameter2.sh parameter.sh #ls命令正确执行 [root@localhost sh]$ echo $? 0 #预定义变量“$?”的值是0,证明上一个命令执行正确 [root@localhost sh]$ ls install.log ls:无法访问install.log:没有那个文件或目录 #当前目录中没有install.log文件,所以ls命令报错了 [root@localhost sh]$ echo $? 2 #变量“$?”返回一个非0的值,证明上一个命令没有正确执行 #至于错误的返回值到底是多少,是在编写ls命令时定义好的,如果碰到文件不存在就返回数值2
$$与$!
[root@localhost sh]$ vi variable.sh #!/bin/bash echo "The current process is $$" #输出当前进程的PID. #这个PID就是variable.sh这个脚本执行时,生成的进程的PID find /root -name hello.sh & #使用find命令在root目录下查找hello.sh文件 #符号&的意思是把命令放入后台执行,工作管理我们在系统管理章节会详细介绍 echo "The last one Daemon process is $!" #输出这个后台执行命令的进程的PID,也就是输出find命令的PID号
5)从键盘输入:read
用法:read [选项名 选项值] [变量名]
-
选项:
- -a:后跟一个变量,该变量会被视为一个数组,然后为其赋值,默认以空格为分隔符;
- -p:提示信息:在等待输入时,给出提示信息;
- -t:秒数:最多等待指定时间;
- -n:数字:输入指定字符数量后执行;
- -s:隐藏输入数据(就像Linux登录时输入密码那样)
- -d:开启标志符,当输入该字符后结束。
- -e:开启命令补全功能
-
变量名
- 变量名自定义,如果没有则默认为REPLY
- 如果只有单个变量,则把整行输入赋予该变量;
- 如果有一个以上的变量,则输入行会进行分割,一个个赋予,最后一个变量会获得剩余全部字符
例子:
vi read.sh #!/bin/bash read -t 30 -p "Please input your name: " name #①提示“Please ……” #②等待30 秒 #③存入变量name echo "Name is $name" read -s -t 30 -p "Please enter your age: " age #与上一个相比,隐藏输入 echo "Age is $age" read -n 1 -t 30 -p "Please select your gender[M/F]:" gender #与上一个相比,加入了选项 -n 1,代表只接收一个输入字符就会执行(都不用输入回车) echo "Sex is $gender"
五、运算符
1)算术运算符
bash不支持使用+-*/直接进行运算,相关操作要依赖指令awk、expr实现,其中expr最常用。
例子
用一个算术表达式给变量赋值:
val=$(expr 2 + 2) 或 val=`expr 2 + 2`
或
val=$[2 + 2]
需要注意的是数字和运算符之间需要空格,2 + 2而非2+2。
运算符
乘的符号为*,有的版本的shell为\*
运算 |
运算符 |
说明 |
加 | + |
用法:$(expr $a 运算符 $b)或`expr $a 运算符 $b` 注意运算符与数字间的空格 val=$(expr 2 + 2) 或 val=`expr 2 + 2`
|
减 | - | |
乘 | * | |
除 | / | |
取余 | % | |
赋值 | = | |
相等 | == |
1、条件判断要放在[ ]之内 if [ $a == $b ] then echo 'a等于b' else echo 'a不等于b' fi 2、返回true或false |
不等 | != |
2)关系运算符
关系运算符只支持数字和纯数字字符串,不支持其他字符串。
用法:
[$a 运算符 $b]
运算符之前还有-
运算符:
运算 |
运算符 |
说明 |
== | -eq |
真:true 假:false |
!= | -ne | |
> | -gt | |
< | -lt | |
≥ | -ge | |
≤ | -le |
可以通过$?查询最后一次运算结果:
- 0:true
- 非0:false
例子
判断当前输入的用户是否存在。如果存在则提示“用户存在”,否则提示“用户不存在”。
注:如果要在shell脚本使用linux命令,可以使用$()或者反引号
包裹命令;
[root@localhost ~]$ vim demo.sh #!/bin/bash #接受用户的输入 read -p '请输入需要查询的用户名:' username #获取指定用户名在passwd文件中出现的次数 count=$(cat /etc/passwd | grep $username | wc -l) #判断出现的次数,如果次数=0则用户不存在,反之存在 if [ $count == 0 ] then echo '用户不存在' else echo '用户存在' fi
3)逻辑运算符(或与非)
运算 |
运算符 |
说明 |
用法注意[ ]与左右表达式之间的空格 |
或 | -o |
o代表or [表达式1 -o 表达式2] 1 2其中有一个为true才返回true |
[ $a -lt 20 -o $b -gt 100 ] |
与 | -a |
a代表and [表达式1 -a 表达式2] 1 2都为true才返回true |
[ $a -lt 20 -a $b -gt 100 ] |
非 | ! | [! 逻辑运算] | [ ! false ]返回true |
4)字符串运算符
运算符 |
说明 |
例子注意①[ ]与左右表达式之间的空格 ②表达式 与 运算符之间的空格 |
= | 两个字符串是否相等,是则返回true | [ $a = $b ] |
!= | 两个字符串是否相等,否则返回true | [ $a != $b ] |
-z | 字符串长是否为0,是则返回true | [ -z $a ] |
-n | 字符串长是否为0,否则返回true | [ -n $a ] |
str (无运算符) |
字符串是否为空,不为空则返回true | [ $a ] |
5)文件测试运算符
用于检测Unix/Linux文件的各种属性
运算符用法都是:test 运算符 file |
说明都是检测是否是某种文件 |
-b | 块设备文件 |
-c | 字符设备文件 |
-d | 目录 |
-f | 普通文件(既非目录,亦非设备) |
-g | SGID |
-k | 粘着位 |
-p | 有名管道 |
-u | SUID |
-r | 可读 |
-w | 可写 |
-x | 可执行 |
-s | 空(文件大小是否大于0) |
-e | 存在 |
用法
if test -e ./test1.sh then echo '文件已存在!' else echo '文件不存在!' fi
六、流程语句
下文所有的条件表达式中的运算符,在第五节运算符中有说明
1)if条件
用法
①
if [ 条件 ] then 表达式1 else 表达式2 fi ② if [ 条件1 ] then 表达式1 elif[ 条件2 ] then 表达式2 ... else 所有条件都不成立时,执行该表达式 fi
注意:
1、if和中括号、中括号和内部条件之间均有不能省略的空格!!
if [ 表达式 ];then 表达式省略空格会导致
-
错误的替换/bad subsitution
-
未预期的符号 `then' 附近有语法错误
2、then之后的表达式不能省略,即不能写一个空if语句,不然会报错
-
未预期的符号 `fi' 附近有语法错误
例子
①统计根分区使用率
[root@localhost ~]$ vi sh/if1.sh #!/bin/bash #统计根分区使用率 rate=$(df -h | grep "/dev/sda2" | awk '{print $5}’| cut -d "%"-f1) #把根分区使用率作为变量值赋予变量rate if [ $rate -ge 80 ] #判断rate的值如果大于等于80,则执行then程序 then echo "Warning!/dev/sda3 is fu11!!" #打印警告信息。在实际工作中,也可以向管理员发送邮件。 fi
②创建目录
[root@localhost ~]$ vi sh/add_dir.sh #!/bin/bash #创建目录,判断是否存在,存在就结束,反之创建 echo "当前脚本名称为$0" DIR="/media/cdrom" if [ ! -e $DIR ] then mkdir -p $DIR fi echo "$DIR 创建成功"
③备份mysql数据库
[root@localhost ~]$ vi sh/bakmysql.sh #!/bin/bash #备份mysql数据库。 ntpdate asia.pool.ntp.org &>/dev/null #同步系统时间 date=$(date +%y%m%d) #把当前系统时间按照“年月日”格式赋子变量date size=$(du -sh/var/lib/mysql) #统计mysql数据库的大小,并把大小赋予size变量 if [ -d /tmp/dbbak ] #判断备份目录是否存在,是否为目录 then #如果判断为真,执行以下脚本 echo "Date : $date!" > /tmp/dbbak/dbinfo.txt #把当前日期写入临时文件 echo "Data size : $size" >> /tmp/dbbak/dbinfo.txt #把数据库大小写入临时文件 cd/tmp/dbbak #进入备份目录 tar -zcf mysql-lib-$date.tar.gz /var/lib/mysql dbinfo.txt &> /dev/null #打包压缩数据库与临时文件,把所有输出丢入垃圾箱(不想看到任何输出) rm -rf /tmp/dbbak/dbinfo.txt #删除临时文件 else mkdir /tmp/dbbak #如果判断为假,则建立备份目录 echo "Date : $date!" > /tmp/dbbak/dbinfo.txt echo "Data size : $size" >> /tmp/dbbak/dbinfo.txt #把日期和数据库大小保存如临时文件 cd /tmp/dbbak tar -zcf mysql-lib-$date.tar. gz dbinfo.txt /var/lib/mysql &> /dev/null #压缩备份数据库与临时文件 rm -rf/tmp/dbbak/dbinfo.txt #删除临时文件 fi
④判断apache是否启动,如果没有启动则自动启动
[root@localhost ~]$ vi sh/autostart.sh #!/bin/bash #判断apache是否启动,如果没有启动则自动启动 port=$(nmap -sT 192.168.4.210 | grep tcp | grep http | awk '{print $2}’) #使用nmap命令扫描服务器,并截取 apache服务的状态,赋予变量port #只要状态是open,就证明正常启动 if [ "$port" == "open"] #如果变量port的值是“open” then echo "$(date) httpd is ok!” >> /tmp/autostart-acc.log #则证明apache 正常启动,在正常日志中写入一句话即可 else /etc/rc.d/init.d/httpd start &>/dev/null #否则证明apache没有启动,自动启动apache echo "$(date) restart httpd !!" >> /tmp/autostart-err.log #并在错误日志中记录自动启动apche 的时间 fi
⑤判断用户输入的是什么文件
[root@localhost ~]$ vi sh/if-elif.sh #!/bin/bash #判断用户输入的是什么文件 read -p "Please input a filename: " file #接收键盘的输入,并赋予变量file if [ -z "$file” ] #判断file变量是否为空 then echo "Error, please input a filename" #如果为空,执行程序1,也就是输出报错信息 exit 1 #退出程序,并返回值为Ⅰ(把返回值赋予变量$P) elif [ ! -e "$file” ] #判断file的值是否存在 then echo "Your input is not a file!" #如1果不存在,则执行程序2 exit 2 #退出程序,把并定义返回值为2 elif [ -f "$file” ] #判断file的值是否为普通文件 then echo "$file is a regulare file!” #如果是普通文件,则执行程序3 elif [ -d "$file” ] #到断file的值是否为目录文件 then echo "$file is a directory!" #如果是目录文件,网执行程序4 else echo "$file is an other file!” #如果以上判断都不是,则执行程序5 fi
2)case条件(类似C中的switch case)
用法
case $变量名 in 值1|值2|值3) 程序1;; 值n) 程序2;; #其他分支... *) 若果以上分支都没命中,则执行该程序(相当于default);; esac
注意事项:
- 分支程序末尾的两个分号;;
- in后边判断值时,只有右半括号),没有左半括号;
- 以case开始,以esac结束。
- 多个并列的值用 | 分隔。
3)for循环
用法
①直接把每次循环时的值都给出了(类似python中的for i in ...)
for 变量 in 值1 值2 值3 ... do 程序 done
for 变量 in { 起始值1..终值n }
do
程序
done
注意:
- 值与值间用空格分隔;
- 以上多个值可以用一个List或者文件等具有多个值的对象代替;
例子
- 打印一些值
for time in morning noon afternoon evening do echo "This time is $time!" done
- 打印从1到100
for i in {1..100} do echo $i done
- 批量解压缩脚本
for i in $(cat ls.log) ` #或者这样写for i in `cat ls.log` #读取ls.log文件的内容,文件中有多少个值,就会循环多少次,每次循环把文件名赋予变量i do tar -zxf $i &>/dev/nulldone rm -rf /lamp/ls.log
②自增式循环(类似C中for(i=1;i<=100;i++))
for (( 初始值;循环控制条件;变化 )) do 程序 done
例子
- 从1加到100
s=0 for (( i=1;i<=100;i=i+1 )) do s=$(( $s+$i )) done echo "The sum of 1+2+...+100 is : $s"
注意
- for后边跟两个括号(())
- 值、括号、运算符之间两两空格(这一项还不确定,因为我发下现没空格也能运行)
③用for循环读取文件中的值
学习自:bash如何使用for循环按行读入文本文件 - 简书
以空格为分隔符
for i in $`cat t.txt` 或 for i in $(cat t.txt) do echo $i done
以换行符为空格符
IFS=$'\n' for i in $''`cat t.txt` do echo $i done
4)while循环
用法
while [ 条件1 ] do 程序 done
例子
- 从1加到100
[root@localhost ~]$ vi sh/addnum.sh #!/bin/bash #从1加到100 i=1 s=0 #给变量i和变量s赋值 while [ $i -le 100 ] #如果变量i的值小于等于100,则执行循环 do s=$(( $s+$i )) i=$(( $i+1 )) done echo "The sum is: $s"
5)until循环
与while的区别在于until是在条件不成立时才进行循环
用法
until [ 条件1 ] do 程序 done
例子
- 从1加到100
[root@localhost ~]$ vi sh/until.sh #!/bin/bash #从1加到100 i=1 s=0 #t给变量i和变量s赋值 until [ $i -gt 100 ] #循环直到变量i的值大于100,就停止循环 do s=$(( $s+$i )) i=$(( $i+1 )) done echo "The sum is: $s"
6)流程控制(跳出循环):break与continue,
用途和其他语言的中的相同,break——跳出循环,continue——继续下次循环
[root@localhost ~]$ vi sh/break.sh #!/bin/bash #演示continue for (( i=1;i<=10;i=i+1 )) #循环十次 do if ["$i" -eq 4 ] #如果变量i的值等于4 then continue #退出换成continue fi echo $i #输出变量i的值 done
[root@localhost ~]$ vi sh/continue.sh #!/bin/bash #演示continue for (( i=1;i<=10;i=i+1 )) #循环十次 do if ["$i" -eq 4 ] #如果变量i的值等于4 then continue #退出换成continue fi echo $i #输出变量i的值 done
7)函数
用法
#定义 function 函数名 () { 程序 } #调用 函数名 $参数1 $参数2
例子
接收用户输入的数字,然后从1加到这个数字
1 [root@localhost ~]$ vi sh/function.sh 2 #!/bin/bash 3 #接收用户输入的数字,然后从1加到这个数字 4 5 function sum () { 6 #定义函数sum 7 s=0 8 for (( i=0; i<=$num;i=i+1 )) 9 #循环直到i大于$1为止。$1是函数sum 的第一个参数 10 #在函数中也可以使用位置参数变量,不过这里的$1指的是函数的第一个参数 11 do 12 s=$(( $i+$s )) 13 done 14 echo "The sum of 1+2+3...+$1 is :$s" 15 #输出1加到$1的和 16 } 17 18 read -p "Please input a number: " -t 30 num 19 #接收用户输入的数字,并把值赋予变量num 20 y=$(echo $num | sed 's/[0-9]//g') 21 #把变量num的值替换为空,并赋予变量y 22 23 if [ -z "$y"] 24 #判断变量y是否为空,以确定变量num中是否为数字 25 then 26 sum $num 27 #调用sum函数,并把变量num的值作为第一个参数传递给sum函数 28 else 29 echo "Error!! Please input a number!" 30 #如果变量num 的值不是数字,则输出报错信息 31 fi
注意
- 8行的$num,这个num是外部已经命名的变量num,参数是没有名字的,只以$1、$2……加以分辨;
- 14行的$1代表的才是传入参数,$数字的具体用法参照预定义变量一节。
七、字符串
1)正则表达式
元字符 |
说明 |
例子 |
\ | 转义符,将特殊字符转为一般字符 | a\.b 只能匹配 a.b |
^ | 匹配开头 | ^tux 匹配以tux开头的行 |
$ | 匹配结尾 | tux$ 匹配以tux结尾的行 |
. | 除换行符\n外的任意单个字符 | ab.匹配任意ab+单字符 |
[ ] | 匹配[ ]中的任意单字符 |
coo[kl]匹配cook和cool |
[^] | 匹配除了[^ ]列出的单字符 | 123[^45]匹配123x,x是除了4和5外的其他任意字符 |
[-] | 匹配某个范围内的字符(必须递增) | [0-9]可以匹配0-9的任意单个数字 |
? | 匹配之前的项0或1次 | colou?r可以匹配0或1次u,即color或colour |
+ | 匹配之前的项1或多次 | sa-6+可以匹配多个6,即sa-6、sa-666 |
* | 匹配之前的项人一次 | co*l可以匹配任意个o,即col、cool、coool |
() | 匹配某个子串 | ma(trix)?可以匹配matrix和ma |
{n} | 匹配之前的项n次 | [0-9]{3}可以匹配任意三位数 |
{n,} | 至少匹配之前的项n次 | [0-9]{2,}匹配一个两位或以上的数 |
+n,m} | 最少匹配n次,最多匹配m次 | [0-9]{2,5}匹配一个2至5位数 |
| | 或 | ab(c|d)匹配c或d,即abc和abd均可 |
2)字符截取、替换
①cut——列提取
用法
cut [选项] 文件名
选项
选项 |
说明 |
-f |
列号;提取第几列 多列用逗号,分隔,如cut -f 2,3 |
-d | 分隔符;以指定分隔符分割列 |
-n | 取消分割多字节字符 |
-c |
字符范围();不依赖分隔符来区分列,而是通过字符范围(从第几个字符开始分割)进行字符提取: n-:从第n个字符到行尾; n-m:从第n到第m个字符; -m:从第一个到第m个字符; |
--complement | 补全被选择的字符或字段 |
--out-delimiter | 输出内容为字段分隔符 |
说明:
- 默认分隔符为制表符,即TAB。
例子
-
创建文件
[root@localhost ~]$ vi student.txt id name gender mark 1 liming m 86 2 sc m 67 3 tg n 90
-
分割
[root@localhost ~]$ vi student.txt id name gender mark 1 liming m 86 2 sc m 67 3 tg n 90 [root@localhost ~]$ cut -f 2 student.txt #提取第二列内容 [root@localhost ~]$ cut -f 2,3 student.txt #提取第2和3列的内容
[root@localhost ~]$ cut -c 8- student.txt #每行从第八个字符开始提取到行尾,好像很乱啊,那是因为每行的字符个数不相等啊
8-:提取到行尾
8:提取第8个
-8:从行首提取到第8个 [root@localhost ~]$ cut -d ":" -f 1,3 /etc/passwd #以“:”作为分隔符,提取/etc/passwd_文件的第一列和第三列
十、字符处理命令
1)sort排序命令
-
用法
sort [选项] 文件名
-
选项
选项
说明
-f 忽略大小写 -b 忽略每行前边的空白部分 -n 以数值型排序,默认用字符串型排序 -r 反向排序 -u 删除重复行。同uniq命令 -t 指定分隔符,默认TAB -k n[,m] 按照指定的字段范围排序。从第n字段开始,m字段结束(默认到行尾) - 例子
默认是用每行开头第一个字符来进行排序的:[root@localhost~]$ sort /etc/passwd #排序用户信息文件
反向排序用-r选项:
[root@localhost~]$ sort -r/etc/passwd #反向排序
如果要指定排序的字段,需要用"-t"选项指定分割符,并使用"-k"选项指定字段号。假设我们要用UID字段排序/etc/passwd文件:
[root@localhost~]$ sort -t ":" -k 3,3 /etc/passwd #指定分隔符是“:”,用第三字段开头,第三字段结尾排序,就是只用第三字段排序
如果写为-k 3,代表从第三字段到行尾排序。
2)uniq取消重复行
-
用法
[root@localhost~]$ uniq [选项] 文件名 选项: -i:忽略大小写
3)wc统计命令
wc [选项] 文件名 选项: -l:只统计行数 -w:只统计单词数 -m:只统计字符数
十一、一些函数
1、eval
学习自:shell 中的 eval_shell eval-CSDN博客
用法:eval 指令字符串(不加用引号)
说明:执行eval后的指令字符串,相当于我们直接在控制台输入指令字符串并按回车,
通常配合$变量来实现灵活、批量对指令中的某个字段的替换例子
1)指令字符串中只有一个指令关键字,相当于直接执行后边的指令
1 2 | echo $NAME eval echo $NAME |
此时二者是等价的。
2)指令字符串中有两个指令关键字,相当于先执行外层指令,再执行外层指令执行完毕后输出的指令
myfile="cat test.txt" eval echo $myfile #先执行echo $myfile,输出cat test.txt #再执行eval cat test.txt,输出test.txt的内容
3)允许嵌套变量,此时在要嵌套的变量之前加/$
#输出最后一个参数 echo "Last argument is $(eval echo \$$#)"
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
2022-07-17 Springboot:@Service注解作用 以及 当有多个Impl时如何知道注入的是哪个Impl