1.shell变量
shell变量没有类型的概念,全是字符串
(1)自定义变量
变量作用:用一个特定的字符串表示一不固定的内容
定义变量:变量名=值 变量名必须以字母或下划线开头(显示赋值)
引用变量:$变量名或者 ${变量名}
取消变量:uset 变量名
${#变量名} 显示变量值的长度;加{}可以防止歧义
例子:
#!/bin/bash ip=www.baidu.com if ping -c1 $ip &> /dev/null;then echo "$ip is ok" else echo "$ip is down" fi
then是单独的命令,因此前面要加分号。fi是if的倒置,表示结束
或者:
#!/bin/bash ip=www.baidu.com ping -c1 $ip &>/dev/null if [ $? -eq 0 ];then echo "$ip is ok" else echo "$ip is down" fi
注:[]是用来做条件测试的。$?是上个命令的返回值。$? -eq 0表示上个命令的返回值是0.注意前面和后面有空格。因为[是命令,命令和选项参数之间有空格。[是命令,]是参数,和[搭配使用。
隐式赋值read:
从键盘读入
在脚本中写入 read 变量名
然后在命令行执行时从键盘读入赋值。
read -p “please input a ip: ” 变量名 #给用户提示
read可以同时定义多个变量 read name sex age #在输入时以空格分隔
$1 表示脚本后面的第一个参数。位置变量(在脚本后的位置)
可以将一个变量的值赋给另一个变量 a=”$b is you” #注意是弱引用(双引号)
使用反引号进行命令替换 a=`cat a.txt` 执行一遍再赋值给a。
上面最后都是一个字符串
(2)环境变量
环境变量和自定义变量作用范围不一样。自定义变量在当前shell有效,在子shell和其他都无效。环境变量在全局都有效
定义环境变量:方法1:export 变量名=值
方法2:export 自定义变量名 将自定义变量转成环境变量
引用环境变量: $变量名 或者${变量名}
查看环境变量:echo $变量名 或者 env 查看所有
例子:
在某个脚本publish.sh有自定义变量ip1,在其他脚本如1.sh想要用publish.sh里的自定义变量,只需要在1.sh中执行publish.sh就能在当前shell中引入,方法为. publish.sh。写项目时公共变量可以定义在公共的脚本中,不需要定义成环境变量。
/etc/profile里有系统定义好的环境变量。echo $PATH
如果想修改PATH,即新添shell的搜索路径,可以在profile文件后新加:
PATH=$PATH:/new/bin
export PATH 在当前shell和子shell就都可以用环境变量
source /etc/profile 在当前shell执行之前修改的文件,导入环境变量(不写该命令就要新开shell才能实现加载)
系统定义好的环境变量有:PATH、USER、UID、MAIL、SHELL、HOME等。可以在脚本直接拿来用。$环境变量
(3)位置变量
$1、$2、....、根据执行脚本时所带的位置的参数来确定的变量。在脚本中可以直接用。
(4)预定义变量
$?:上个命令的返回值,0表示成功
$!:上一个后台进程的PID
$$:当前进程的PID
$#:参数的个数
$@或者$*:所有的参数
$0:脚本名
$*和$#的区别:
当$*和$@没有被引用的时候,它们确实没有什么区别,都会把位置参数当成一个个体。
"$*"会把所有位置参数当成一个整体(或者说当成一个单词),如果没有位置参数,则"$*"为空,如果有两个位置参数并且IFS为空格时,"$*"相当于"$1 $2"
"$@" 会把所有位置参数当成一个单独的字段,如果没有位置参数($#为0),则"$@"展开为空(不是空字符串,而是空列表),如果存在一个位置参数,则"$@"相当于"$1",如果有两个参数,则"$@"相当于"$1" 和"$2"等等
引号
”$1”和’$1’的区别:”$1”会引用这个值,而’$1’会直接显示$1(echo ”$1” 和echo ’$1’)
‘’单引号中没有变量(强引用).双引号是弱引用。
``反引号,命令替换,里面内容先被shell执行,在替换进去。比如touch `date +%F`_file.txt
$()与反引号相同
例子:
disk_free=$(df -h | grep ‘/$’ | awk ‘{print $4}’)
#df -h显示挂载信息 /$ 以/结尾的
脚本例子
basename、dirname。 $0是带路径的文件名。但前面加个basename就只有文件名。
反引号的作用就是将反引号内的Linux命令先执行,然后将执行结果替换。
if [ ! -f $1] 条件测试中,判断$1是否是个文件。加个!表示不是文件会执行
#!/bin/bash
#如果用户没有加参数
if [ $# -eq 0 ];then echo "usage:`basename $0` file" exit fi
(5)变量的运算
整数运算
方式1:let
let sum = 1+2;echo $sum
方式2:$((表达式))
num1=10 num2=20 echo $(($num1+$num2))
+ - * / 括号里的$可以省略
方式3:$[表达式]
方式4:expr
num1=10 num2=20 expr $num1 + $num2
#为30 。直接echo就是10+20
但要把结果赋给某个变量就要加反引号
sum=`expr $num1 + $num2` *要转义下 \* + - \* /
例子1:内存的使用率
mem_used=$(free -m | grep ‘^Mem’ | awk ‘{print $3}’) #awk后面必须是单引号 mem_total=$(free -m | grep ‘^Mem’ | awk ‘{print $2}’) mem_percent=$((mem_used*100/mem_total)) echo “当前内存使用的百分比:$mem_percent”
bash -vx 脚本名 用调试的方式去执行
例子2:
#!/bin/bash ip=www.baidu.com i=1 while [ $i -le 5 ] do ping -c1 $ip &> /dev/null if [ $? -eq 0 ];then echo "$ip is up" fi let i++ done
方法5:bc
bc是个交互式的计算器
echo “2*4”|bc 将字符串创给bc
小数运算:
echo “scale=2;6/4”|bc awk ‘BEGIN{print 1/2}’ echo ‘5.0/2’|python
(6)变量“内容”的删除和替换
内容删除 #注意:删除并没有改变变量,只是这一次输出改变了
url=www.sina.com.cn
#是从前往后删
echo ${url}
echo ${#url}
echo ${url#www.} #删除了www.
echo ${url#*si} #删除了si之前(包括si)的内容
echo ${url#*.} #删除了第一个.前(包括.)的所有内容 #最短匹配
echo ${url##*.} #删掉最后一个.之前的所有内容 #贪婪匹配
%是从后往前删
echo ${url%.cn}
echo ${url%.*} #从后往前删,删到第一个点
echo ${url%%.*} #从后往前删,删到最后一个点
索引和切片
echo ${url:0:5} 0~5的位置
echo ${url:5} 从第5个位置,要后面全部
内容替换
echo ${url/sina/baidu} #将sina换成baidu 非贪婪匹配,只换1个
echo ${url//n/N} #将所有的n换成N。不加双斜线就换第1个。 贪婪匹配
变量的替代
${变量名-新的变量的值}
如果变量没有被赋值,会用新的变量值替代。(没有被赋值是指被unset了的变量)
变量有被赋值(包括空值:被定义过但是空值),不会被替代
echo ${var1-aaa}
${变量名:- 新的变量的值}
没有值或者空值会被替代;有值不会被替代
(7)变量i++和++i
对变量值i的影响:没有任何区别
i=1; let i++ ; echo $i #结果是2
j=1;let ++j ; echo $j #结果是2
对表达式值的影响: 不同
i=1; let x=i++ ; echo $x #先赋值,再运算 1
j=1 ; let y = ++i ; echo $y #先运算,再赋值 2
上面i和j的值还都是2。
user[i++] user[0] user[1] ....
user[++i] user[1] user[2] ....
不去定义变量初始值,一般都是0。
let i++ ; echo $i 是1
if [ ! -f $1 ];then echo "error file" exit fi echo 'ping......' for ip in `cat $1` #执行单引号里的命令,即显示ip,并且遍历 do ping -c1 $ip &> /dev/null if [ $? -eq 0 ];then echo "$ip is up" else echo "$ip is down" fi done
2.条件测试
(1)概述
条件测试分为三类:文件测试(测试文件是常规文件还是设备文件、是不是有写、执行权限等、判断文件是否存在);数值比较;字符串比较
test -d /home 判断/home是否是目录。不会显示任何结果,但是的话返回值为0
例子:
#!/bin/bash back_dir=/var/mysql_back if ! test -d $back_dir;then #如果不是目录,就创建 # if [ ! -d $back_dir ];then mkdir -p $back_dir fi echo "开始备份..."
[ 和test命令一样
[ -d /home ] # [是命令,-d是选项,/home和]是参数,之间必须有空格。
例子2:数值比较;root用户才能安装httpd
if [ ! $UID -eq 0 ];then # if [ $UID -ne 0 ];then #if [ $USER ! = “root” ];then #字符串比较 echo “你没有权限” exit fi yum -y install httpd
测试格式有三种:
test 表达式
[ 条件表达式 ]
[[ 条件表达式 ]] #可以用正则~
man test看帮助文档
(2)操作
文件测试
语法: [ 操作符 文件或目录 ]
[ -e dir/file ] #判断目录或者文件是否存在,存在就为真
[ -d dir ] #是否存在,并且是目录
[ -f file ] #是否存在,并且是文件
[ -r file ] #当前用户对该文件是否有读权限
[ -x file ] #执行
[ -w file ] #写
[ -L file ] #是否是链接
[ -b file ] #是否是设备文件
数值比较
[ 1 -gt 10 ] #大于
[ 1 -lt 10 ] #小于
-eq 等于
-ne 不等于
-ge 大于等于
-le 小于等于
输入用户名,判断用户是否存在,创建用户。
#!/bin/bash read -p "please input a user name: " user_name id $user_name &>/dev/null #查看用户id,用户存在会执行成功 if [ $? -eq 0 ];then #if id $user_name &>/dev/null;then echo "用户存在" exit fi useradd $user_name if [ $? -eq 0 ];then echo "用户创建成功" fi
例子:磁盘根分区的使用量>90%就报警
df -TH 查看磁盘的使用量
df -TH | grep ‘/$’ | awk ‘{print $(NF-1)}’ #NF是倒数第一列,NF-1是倒数第二列 #9%
df -TH | grep ‘/$’ | awk ‘{print $(NF-1)}’ | awk -F”%” ‘{print $1}’ #-F”%” 按%分割,取前面数字 # 9
disk_use = $(df -TH | grep ‘/$’ | awk ‘{print $(NF-1)}’ | awk -F”%” ‘{print $1}’) #将执行结果赋给变量。由于是命令,需要变量替换 $()或者反引号。
if [ $disk_use -ge 90 ];then
echo “`date +F%-%H` disk : ${disk_use}%” | mail -s “disk war....” $mail_user
fi
计划任务crontab -e 打开一配置文件,里面写任务。
格式:
*/5 * * * * /bin/bash /root/scripts/disk_use.sh
*/5 * * * * 代表每隔5分钟
/bin/bash 代表解释器
/root/scripts/disk_use.sh 为执行的脚本
可以定时执行脚本,输出内容到指定日志中。
字符串比较:
提示:使用双引号(防止变量没有报错;也可以不使用双引号,但不好)
[ “$USER” == “root” ] 判断当前用户是不是root用户 ==或者=都行
!=
[ -z “$BBB” ] 判断BBB变量的值的长度是0
[ -n “$BBB” ] 判断BBB变量的值的长度不是0
变量为空或者未定义,长度都是0.
逻辑运算
[ 表达式1 -a 表达式2 ] 两个都成立
[ 表达式1 -o 表达式2 ] 或者
[[ 表达式1 && 表达式2 ]] 双括号的并
[[ 表达式1 || 表达式2 ]] 或者
例子:
批量生成用户
seq 10 生成1-10的序列 seq 3 2 10 生成3-10的数,步长为2。
#!/bin/bash #useradd #v1.0 by yq 12/2/2020 read -p "please input number: " num #if [ “$num” = ^[0-9]+$ ];then #这个是错误的,会直接比较后面的字符,单个方括号没有正则 if [[ ! ”$num”=~^[0-9]+$ ]];then # ~代表匹配的意思.后面不能加双引号。 echo “请输入数字” exit fi read -p "please input prefix: " prefix if [ -z “$prefix” ];then echo “长度不能为0” exit fi for i in `seq $num` #{1..$num}不行,借助seq命令生成序列 do user=$prefix$i useradd $user echo "123" |passwd --stdin $user &>/dev/null if [ $? -eq 0];then echo "$user is created" fi done
while循环直到输入数字
read -p “请输入数字” num while true do if [[ “$num”=~^[0-9]+$ ]];then break else read -p “不是数字,请输入数字” num fi done
符号小结:
() 在子shell中执行
(( )) c风格的数值比较 ((1<2))
$() 命令替换,与反引号相同。先执行命令,再替换
$(()) 整数运算 $((1+2))
{} 集合{1..3}
${} 变量的引用
[] 条件测试
[[]] 条件测试,多了按正则比较的方式(=~)
$[] 整数运算
执行脚本:
./01.sh 需要执行权限 在子shell中执行
bash 01.sh 不需要执行权限 在子shell执行
. 01.sh 不需要执行权限,在当前shell执行
source 01.sh 不需要执行权限,在当前shell执行
调试脚本:
sh -n 02.sh 仅调试语法错误
sh -vx 02.sh 以调试的方式执行,查询整个执行过程