shell编程(二):变量、条件测试

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是单独的命令,因此前面要加分号。fiif的倒置,表示结束

 或者:

#!/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和其他都无效。环境变量在全局都有效

定义环境变量:方法1export 变量名=

方法2export 自定义变量名   将自定义变量转成环境变量

引用环境变量: $变量名  或者${变量名}

查看环境变量: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才能实现加载)

系统定义好的环境变量有:PATHUSERUIDMAILSHELLHOME等。可以在脚本直接拿来用。$环境变量

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显示挂载信息  /$ /结尾的

脚本例子

basenamedirname$0是带路径的文件名。但前面加个basename就只有文件名。

反引号的作用就是将反引号内的Linux命令先执行,然后将执行结果替换

if [ ! -f $1] 条件测试中,判断$1是否是个文件。加个!表示不是文件会执行


 #!/bin/bash

#如果用户没有加参数
if [ $# -eq 0 ];then
    echo "usage:`basename $0` file"
    exit
fi

  (5)变量的运算

整数运算

 方式1let

let sum = 1+2;echo $sum

 方式2$((表达式))

num1=10

num2=20

echo $(($num1+$num2))

    + - * /   括号里的$可以省略

 方式3$[表达式]

 方式4expr

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

方法5bc

bc是个交互式的计算器

echo “2*4”|bc  将字符串创给bc

 

 小数运算:

echo  “scale=2;6/4”|bc
awk  ‘BEGIN{print 1/2}’
echo5.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

上面ij的值还都是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 以调试的方式执行,查询整个执行过程

 

原文地址:https://www.cnblogs.com/yq055783/p/13091089.html