+, -, *(乘), /(除), **(乘方), %(取模)

  • let var=算术运算符表达式

    高级let用法:let i+=2

    +=,-=,*=,/=,%=,**=

    自增:let var++

  • var=$[算术运算符表达式]

  • var=$((算术运算符表达式))

  • var=$(expr $ARG1 OP $APG2)

注意:乘法符号在某些场景中需要使用转义符

练习1:写一个脚本文件,完成如下功能:添加3个用户,求这3个用户的UID之和。

#!/bin/bash

id testUser1 > /dev/null || useradd testUser1
id testUser2 > /dev/null || useradd testUser2
id testUser3 > /dev/null || useradd testUser3

uid1=`grep "testUser1" /etc/passwd | cut -d: -f3`
uid2=`grep "testUser2" /etc/passwd | cut -d: -f3`
uid3=$(grep "testUser3" /etc/passwd | cut -d: -f3)

#方法1
#let num=$uid1+$uid2+$uid3
#方法2
#num=$[$uid1+$uid2+$uid3]
#方法3
num=$(($uid1+$uid2+$uid3))
#方法4
#num=$(expr $uid1 + $uid2 + $uid3)
echo $num

练习2:写一个脚本文件,完成如下功能:求/etc/passwd文件中第10行和第20行的用户的UID之和

#!/bin/bash

#先用head取前10行,再用tail取最好一行,然后再用cut切
la=`head -10 /etc/passwd | tail -1 | cut -d: -f3`
lb=`head -20 /etc/passwd | tail -1 | cut -d: -f3`

echo $la
echo $lb

let lc=$la+$lb
echo $lc

练习3:写一个脚本文件,完成如下功能:求/etc/passwd文件中,uid最大的2个uid之和

#!/bin/bash

#先用冒号把文件分成多列,用第三列从大到小排序后,分别取第一行和第二行,最后在用cut切开
la=`sort -t: -k3 -nr /etc/passwd | head -1 | cut -d: -f3`
lb=`sort -t: -k3 -nr /etc/passwd | head -2 | tail -1 | cut -d: -f3`

echo $la
echo $lb

let lc=$la+lb
echo $lc

练习4:写一个脚本文件,完成如下功能:通知2个文件里的空白行数之和

#!/bin/bash

#用grep找出空白行(使用正则表达式),然后再用wc数一数有多少行
la=`grep -e "^[[:space:]]*$" /etc/rc.d/init.d/functions | wc -l`
lb=`grep -e "^[[:space:]]*$" /etc/inittab | wc -l`

echo $la
echo $lb

let lc=$la+$lb
echo $lc

条件语句

测试表达式:

  • test expression
  • [ expression ]
  • [[ expression ]]

注意:中括号中间的空格,没有空格就是语法错误

bash的条件测试类型

  • 数值比较大小

    例子:[ 2 -ne 3 ]和test 2 -ne 3效果一样

    #!/bin/bash
    
    if [ 2 -ne 3 ]; then
        echo 11
    else
        echo 22
    fi
    
    if test 2 -ne 3; then
        echo 111
    else
        echo 222
    fi
    • -eq:等于 [ $num1 -eq $num2 ]
    • -ne:不等于[ $num1 -ne $num2 ]
    • -gt:大于[ $num1 -gt $num2 ]
    • -ge:大于等于[ $num1 -ge $num2 ]
    • -lt:小于[ $num1 -lt $num2 ]
    • -le:小于等于[ $num1 -le $num2 ]
  • 字符串比较

    == :等于

    > :大于

    < :小于

    != :不等于

    =~ :左侧字符串是否被右侧的正则表达式所匹配。

    -z "STRING" :指定字符串为空则为真

    -n "STRING":指定字符串不为空则为真

    做字符串比较的变量,最好加上“”。不加的话,如果做比较的某个变量没有被定义的话,执行就会出错。

    下面的例子,tom是不存在的变量,不加括号就出错误了,加了就没有错误。

    $ [ tom = $tom ]
    -bash: [: tom: unary operator expected
    $ [ tom = "$tom" ]
    $ echo $?
    1

    字符串比较,最好要用[[]]。

    下面的例子,说明了用[]无法比较

    $ [ a > b ]
    $ echo $?
    0
    $ [ a < b ]
    $ echo $?
    0
    $ [ "a" < "b" ]
    $ echo $?
    0
    $ [ "a" > "b" ]
    $ echo $?
    0

    下面的例子,说明用[[ ]]达到了预期。

    $ [[ a < b ]]
    $ echo $?
    0
    $ [[ a > b ]]
    $ echo $?
    1
    $ [[ "a" > "b" ]]
    $ echo $?
    1
    $ [[ "a" < "b" ]]
    $ echo $?
    0

    字符串空的判断:

    $ [[ -z "$tom" ]]
    $ echo $?
    1
    [ys@localhost ~]$ [[ -n "$tom" ]]
    [ys@localhost ~]$ echo $?

    正则表达式的匹配:

    $ tom=ooo
    [ys@localhost ~]$ [[ $tom =~ o.* ]]
    [ys@localhost ~]$ echo $?
    0
    [ys@localhost ~]$ tom=jerry
    [ys@localhost ~]$ [[ $tom =~ o.* ]]
    [ys@localhost ~]$ echo $?
    1
  • 文件测试

    • 文件存在性检查:-a FILE,或者,-e FILE。存在返回0.

      例子:[ -a /etc/passwd ],[ -e /etc/passwd ]

    • 文件存在性和类型检查

      -b FILE:文件存在,且类型是块设备,则返回0.

      -c FILE:文件存在,且类型是字符设备,则返回0.

      -d FILE:文件存在,且类型是目录,则返回0.

      -f FILE:文件存在,且类型是普通文件,则返回0.

      -h或 -L FILE:文件存在,且类型是符号链接,则返回0.

      -p FILE:文件存在,且类型是命名管道,则返回0.

      -S FILE:文件存在,且类型是本地套接字,则返回0.

      $ [ -b /dev/sda ]
      [ys@localhost scripts]$ echo $?
      0
      [ys@localhost scripts]$ [ -b /dev/sdad ]
      [ys@localhost scripts]$ echo $?
      1
      [ys@localhost scripts]$ [ -b 2.sh ]
      [ys@localhost scripts]$ echo $?
      1
    • 文件权限检查

      -r FILE:文件存在,且当前用户(运行此shell进程的用户)可读,则返回0.

      -w FILE:文件存在,且当前用户(运行此shell进程的用户)可写,则返回0.

      -x FILE:文件存在,且当前用户(运行此shell进程的用户)可执行,则返回0.

    • 特殊权限检查

      -u FILE:文件存在,且拥有suid权限,则返回0.

      -g FILE:文件存在,且拥有sgid权限,则返回0.

      -k FILE:文件存在,且拥有sticky权限,则返回0.

    • 文件是否有内容

      -s FILE:文件存在,且有内容,则返回0

    • 时间戳

      -N FILE:文件自从上一次读取操作后,是否被修改过。

    • 从属关系

      -O FILE:当前用户是否文件属主

      -G FILE:当前用户是否在文件属组里。

    • 双目

      FILE1  -ef FILE2:如果FILE1和FILE2是指向同一个文件系统的相同inode的硬链接,则返回0.

      FILE1 -nt FILE2:如果FILE1的修改时间戳新于FILE,则返回0

      FILE 1 -ot FILE2:如果FILE1的修改时间戳旧于FILE,则返回0

组合测试条件

第一种方式:

  • [ expression1 ] && [ expression2 ]
  • [ expression1 ] || [ expression2 ]
  • ! COMMOND

第二种方式:

  • 与运算:[ expression1 -a expression2 ]
  • 或运算:[ expression1 -o expression2 ]

练习:判断主机名称是否为空,或者是否是localhost.localdomain

#!/bin/bash

hostname=`hostname`
if [ -z "$hostname" -o "$hostname" == "localhost.localdomain" ]; then
    hostname=11111
else
    echo "ng"
fi
echo $hostname

向脚本文件传递执行时候的参数

使用位置参数变量:$1,$2,...${10},${11}.

10之后的参数必须加上{}。

$0:是脚本本身的路径加脚本文件的名字。

$#:是参数的个数

$*:拿到所有参数。但把所有参数汇总成了一个串

$@:拿到所有参数。但没有把参数汇总成一个串。

当执行下面命令后,$1的值就是111;$2的值就是222;$3的值就是333;

$ ./script.sh 111 222 333

shift用法:shift n:踢掉前n个位置参数变量

下面的内容保存在shift.sh中

#!/bin/bash
echo $1 $2
shift 1
echo $1
shift 1
echo $1

执行shift.sh的结果:

# bash shift.sh 111 222 333
111 222
222
333

练习:传递2个文本文件的路径给脚本,计算出空白行数之和。

#!/bin/bash
la=`grep -e "^[[:space:]]*$" $1 | wc -l`
lb=`grep -e "^[[:space:]]*$" $2 | wc -l`
echo $la
echo $lb

let lc=$la+$lb
echo $lc

执行结果:

# bash sumforarg.sh /etc/rc.d/init.d/functions /etc/inittab
91
0
91

if语法

if  con1 ; then

elif con2 ; then

else

fi

练习:查看给定用户是否存在,不存在则创建此用户。

#!/bin/bash

if ! grep "^$1\>" /etc/passwd &> /dev/null ; then
    useradd $1
    # 设定用户$1的密码是111
    echo "111" | passwd --stdin $1 &> /dev/null
    echo "add user $1 donw!"
fi

练习:比较2个数字的大小

#!/bin/bash

#声明一个变量,是整数。
declare -i max
#参数个数小于2
if [ $# -lt 2 ]; then
    echo "at least one argu"
    exit 1
fi
#参数1大于参数2
if [ $1 -ge $2 ]; then
    max=$1
else
    max=$2
fi
echo "max number is $max"

练习:判断给定用户名的id号是奇数还是偶数

#!/bin/bash

if [ $# -lt 1 ]; then
    echo "at least one argu"
    exit 1
fi

#取得用户的id
uid=$(grep "^$1\>" /etc/passwd | cut -d: -f3)
echo $uid

#判断奇偶数
if [ $[$uid % 2] -eq 0 ]; then
    echo "oushu"
else
    echo "jishu"
fi

运行结果:

# bash jigou.sh ys
1000
oushu
# bash jigou.sh fedora
1005
jishu

练习:给2个文本文件路径,如果文件不存在,则结束脚本;都存在则返回每个文件的行数,并找出行数多的文件

#!/bin/bash

if [ $# -lt 2 ]; then
    echo "at least two argu"
    exit 1
fi

if ! [ -e $1 -a -e $2 ]; then
    exit 1
else
    la=`wc -l < $1`
    lb=`wc -l < $2`

    echo "$1 行数:$la;$2 行数:$lb"
    if [ $la -ge $lb ]; then
        echo "$1 行数多"
    else
        echo "$2 行数多"
    fi

fi

运行结果:

# bash sum1.sh /etc/passwd /etc/fstab
/etc/passwd 行数:56;/etc/fstab 行数:13
/etc/passwd 行数多

case语法

case $var in
    pat1)

        ;;
    pat2)

        ;;
    pat3)

        ;;
    *)

        ;;
esac

pat支持globbing风格:

  • *:匹配任意长度的任意字符
  • ?:匹配任意单个字符
  • []:匹配指定范围内的任意单个字符
  • [^]:匹配指定范围外的任意单个字符
  • {}:表示符合括号内包含的多个文件
  • aaa|bbb:aaa或bbb

执行完脚本文件后,返回值是啥?

默认是脚本文件里最后一条命令的返回值。

可以自定义返回值:使用exit n命令,n为数字。

当shell进程遇到exit命令时,进程会立即终止,因此exit后面的命令就不执行了。