天天看点

12-学习Shell-Scripts

Please indicate the source: http://blog.csdn.net/gaoxiangnumber1

Welcome to my github: https://github.com/gaoxiangnumber1

12.1什么是 Shell scripts

12.1.1干嘛学习 shell scripts

  • shell script使用外部指令与bash shell的默认工具,它常呼叫外部的函式库,因此在指令周期上比不上程序语言。所以shell script用于系统管理上很好,但用于数值运算上不好,因为它的速度较慢,且使用的CPU资源较多。

12.1.2 第一支 script 的撰写与执行

    1. 指令是从上而下、从左而右执行。
    2. 指令、选项、参数间的多个空白,空白行都会被忽略,tab键的空白视为空格。
    3. 读取到一个Enter符号CR,就开始执行该行或该串命令。
    4. 一行的内容太多可使用

      \Enter

      延伸至下一行。
  • 执行/home/xiang/shell.sh的方法:
    1. 直接指令下达(shell.sh必须要有

      rx

      权限):
      • 绝对路径

        /home/xiang/shell.sh

      • 相对路径

        ./shell.sh

      • 利用PATH:将shell.sh放在PATH的目录内,例如: ~/bin/。
    2. 通过

      bash shell.sh

      sh shell.sh

      执行。此时shell.sh只要有r权限即可被执行。

撰写第一支script

#!/bin/bash
#Program:
#This program shows "Hello World!" in your screen.
# History:
# 2016-10-12 22:44:04 xiang First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "Hello World!" #echo自动输出换行符
exit 
           
    1. #!/bin/bash

      宣告本script使用的shell名称。

      当该程序执行时,它就能加载bash的相关环境配置文件(一般来说是non-login shell的~/.bashrc),并且使用bash执行指令。

    2. 程序内容的说明。

      除了第一行的#之外,其它的#都用作注释。建议说明该script的:内容与功能、版本信息、作者与联络方式、建档日期、历史纪录。

    3. 主要环境变量的宣告。

      建议设定重要的环境变量,如PATH,可让这支程序在执行时能够直接下达外部指令,不必写绝对路径。

    4. 主要程序部分。

      本例中就是echo一行。

    5. 定义返回值。

      exit n

      使程序中断,返回n给系统。
xiang :~ $ sh hello.sh 
Hello World!
xiang :~ $ chmod a+x hello.sh
xiang :~ $ ./hello.sh 
Hello World!
           

12.1.3 撰写 shell script 的良好习惯

  • 在每个script文件头处记录:功能、版本信息、作者与联络方式、版权宣告方式、History(历史纪录)。
  • script内特殊的指令,使用绝对路径的方式下达。
  • script运行时需要的环境变量预先设定。
  • 以tab缩进。

12.2简单的shell script练习

12.2.1简单示例

12.2.2 script的执行方式差异(source,sh script,./script)

利用直接执行的方式来执行script

  • 直接指令下达(绝对路径/相对路径/利用PATH)或使用bash/sh执行脚本时,该script会使用一个新的bash环境来执行脚本内的指令,即script在子bash内执行。当子程序完成后,在子程序内的各项变量或动作将会结束而不会传回到父程序中。

利用source来执行脚本:在父程序中执行

  • source script_name.sh

    在本bash中执行指令,各项动作、变量在本bash内生效。

12.3善用判断式

12.3.1利用test指令的测试功能

  • 判断文件类型,如

    test -e filename

    表示是否存在

    -e 该文件名是否存在

    -f 该文件名是否存在且为文件

    -d 该文件名是否存在且为目录

    -b 该文件名是否存在且为一个block device装置

    -c 该文件名是否存在且为一个character device装置

    -S 该文件名是否存在且为一个Socket文件

    -p 该文件名是否存在且为一个FIFO/pipe文件

    -L 该文件名是否存在且为一个链接档

  • 判断文件权限,如

    test -r filename

    表示是否可读(root权限例外)

    -r 该文件名是否存在且有可读权限

    -w 该文件名是否存在且有可写权限

    -x 该文件名是否存在且有可执行权限

    -u 该文件名是否存在且有SUID属性

    -g 该文件名是否存在且有SGID属性

    -k 该文件名是否存在且有Sticky bit属性

    -s 该文件名是否存在且内容非空

  • 比较两个文件,如

    test file1 -nt file2

    -nt 判断file1是否比file2新

    -ot 判断file1是否比file2旧

    -ef 判断file1与file2是否为同一文件(hard link),即是否均指向同一个inode

  • 比较两个整数,如

    test n1 -eq n2

    -eq 两数值相等

    -ne 两数值不等

    -gt n1大于n2

    -lt n1小于n2

    -ge n1大于等于n2

    -le n1小于等于n2

  • 判断字符串

    test -z string 判断字符串是否为空,空返回true

    test -n string 判断字符串是否非空,非空返回true。

    -n

    可省略

    test str1 == str2 判断str1是否等于str2,相等返回true

    test str1 != str2 判断str1是否不等于str2,不相等返回true

  • 多重条件判断,如:test -r filename -a -x filename

    -a 两条件同时成立返回true

    -o 两条件至少一个成立返回true

    ! 反向测试

  • 让用户输入一个文件名,我们判断:1. 该文件是否存在,若不存在则给予一个Filename does not exist的信息,并中断程序;2. 若该文件存在,则判断它是个文件或目录,结果输出Filename is regular file或 Filename is directory3. 判断一下,执行者的身份对该文件或目录所拥有的权限,并输出权限数据。
#!/bin/bash
# 1. 判断是否输入字符串
read -p "Input filename: " filename
test -z ${filename} && echo "You MUST input a filename." && exit 

# 2. 判断文件是否存在?若不存在则显示讯息并结束脚本
test ! -e ${filename} && echo "${filename} doesn't exist!" && exit 

# 3. 判断文件类型与属性
test -f ${filename} && filetype="regulare file"
test -d ${filename} && filetype="directory"
test -r ${filename} && perm="readable"
test -w ${filename} && perm="${perm} & writable"
test -x ${filename} && perm="${perm} & executable"

# 4. 输出信息
echo "${filename} is a ${filetype}"
echo "Permissions for you are: ${perm}"
           

12.3.2 利用判断符号 [ ]

  • 在bash中使用[]作判断时,中括号两端要有空格。用□表示空格,比较两字符串 HOME与 {MAIL}是否相同

    [□"$HOME"□==□"$MAIL"□]

    判断式中使用一个等号与两个等号的结果一样。注意:

    []内的每两个元素间都用空格分隔;

    []内的变量最好都用双引号括起来;

    []内的常数最好都用单或双引号括起来。

$ name="xiang gao"
$ [ ${name} == "xiang gao" ]
bash: [: too many arguments
#原因:${name}没有使用双引号括起来,变成[ xiang gao == "xiang gao" ]
$ [ "${name}" == "xiang gao" ]
$ echo $?

           
  • []常用于条件判断式

    if then fi

    中。完成以下script:

    用户输入Y或y时显示“Y/y”;输入N或n时显示“N/n”;对其他所有字符显示“I don’t know.”

#!/bin/bash
read -p "Input Y/y/N/n: " input
test -z ${input} && echo "Must input!" && exit 
[ "${input}" == "Y" -o "${input}" == "y" ] && echo "Y/y" && exit 
[ "${input}" == "N" -o "${input}" == "n" ] && echo "N/n" && exit 
echo "I don't know" && exit 
           

12.3.3 Shell script的默认变量( 0, 1…)

  • script默认变量对应如下:
/path/to/script_name        opt1    opt2    opt3    opt4
$0						$1     $2		$3     $4
           
  • 0变量是执行的脚本文件名, 1是第一个参数。特殊变量:

    $# 代表接的参数个数,上例为4

    [email protected] 代表” 1"" 2” “ 3"" 4”,每个变量是独立的(用双引号括起来)

    $* 代表” 1c 2c 3c 4”,c为分隔字符,默认为空格

#!/bin/bash
echo "The script name is ${0}"
echo "Total parameter number is $#"
[ "$#" -lt  ] && echo "The number of parameter is less than 2. Stop." && exit 
echo "Your whole parameter is [email protected]"
echo "The 1st parameter ${1}"
echo "The 2nd parameter ${2}"
           
$ ./script.sh gao xiang number one
The script name is ./script.sh
Total parameter number is 
Your whole parameter is gao xiang number one
The st parameter gao
The nd parameter xiang
           

shift造成参数变量号码偏移

#!/bin/bash
echo "Total parameter number is $#"
echo "Your whole parameter is '[email protected]'"
shift #第一次shift一个变量
echo "Total parameter number is $#"
echo "Your whole parameter is '[email protected]'"
shift  #第二次shift三个变量
echo "Total parameter number is $#"
echo "Your whole parameter is '[email protected]'"
           
$ ./script.sh      
Total parameter number is 
Your whole parameter is '     '
Total parameter number is 
Your whole parameter is '    '
Total parameter number is 
Your whole parameter is ' '
$ ./script.sh   
Total parameter number is 
Your whole parameter is '  '
Total parameter number is 
Your whole parameter is ' '
Total parameter number is 
Your whole parameter is ' '
           
  • shift接数字X,不接默认为1,代表删除最前面X个参数。

12.4条件判断式

12.4.1利用if … then

单层条件判断式

if [ 条件判断式 ]; then
    #当条件判断式成立时执行的指令
fi  #结束if
           
  • 判别多个条件:
    1. 将多个条件写入一个中括号内。
    2. 写入多个中括号;括号之间以

      &&

      (代表AND)或

      ||

      (代表or)隔开。
#!/bin/bash
read -p "Input Y/y/N/n: " input
if [ "${input}" == "Y" ] || [ "${input}" == "y" ]; then
    echo "Y/y"
    exit 
fi
if [ "${input}" == "N" ] || [ "${input}" == "n" ]; then
    echo "N/n"
    exit 
fi
echo "I don't know" && exit 
           

多重条件判断式

#一个条件判断,分成功进行与失败进行(else)
if [ 条件判断式 ]; then
    #条件判断式成立时可以执行的指令
else
    #条件判断式不成立时可以执行的指令
fi
           
#多个条件判断(if ... elif ... elif ... else)分多种不同情况执行
if [ 条件判断式一 ]; then
    #条件判断式一成立时可以执行的指令
elif [ 条件判断式二 ]; then
    #条件判断式二成立时可以执行的指令
else
    #条件判断式一与二均不成立时可以执行的指令
fi
           
#!/bin/bash
read -p "Input Y/y/N/n: " input
if [ "${input}" == "Y" ] || [ "${input}" == "y" ]; then
    echo "Y/y"
elif [ "${input}" == "N" ] || [ "${input}" == "n" ]; then
    echo "N/n"
else
    echo "I don't know"
fi
exit 
           
  • 若没有任何参数,提示用户必须加参数。判断$1是否为hello,是就显示”Hello”;不是就提醒用户仅能使用hello为参数。
#!/bin/bash
if [ -z ${1} ]; then
    echo "You must input arguments!"
elif [ "${1}" == "hello" ]; then
    echo "Hello"
else
    echo "Argument must be 'hello'"
fi
exit 
           

12.4.2利用case ….. esac判断

case $变量名称 in
    "第一个变量内容")  #用双引号括起来每个变量内容,小括号`)`为关键词
        程序段
        ;;              #每个类别结尾使用两个连续分号
    "第二个变量内容")
        程序段
        ;;
    *)                  #最后一个变量内容用*代表其他所有值
        不包含第一个变量内容与第二个变量内容的其他程序执行段
        exit 
        ;;
esac                        #case结尾
           
#!/bin/bash
case ${1} in
    "hello")
        echo "hello"
        ;;
    "")
        echo "Must input"
        ;;
    *)
        echo "Usage ${0} hello"
        ;;
esac
           
  • $变量

    有两种获得方式:
    1. 直接下达式:利用

      script.sh variable

      的方式来直接设定

      $变量

      的内容。
    2. 交互式:通过

      read

      指令让用户输入变量的内容。

12.4.3利用function功能

function fun_name() {
    程序段
}
           
  • fun_name是自定义指令的名称,程序段是它执行的内容。因为script的执行顺序是从上到下、从左到右,因此function一定要在程序最前面。
#!/bin/bash
function print(){
    echo -n "Your choice is " #-n:不断行在同一行显示
}

echo "This program will print your selection."
case ${1} in
    "one")
        print;
        echo ${1} | tr 'a-z' 'A-Z' #将参数做大小写转换
        ;;
    "two")
        print;
        echo ${1} | tr 'a-z' 'A-Z'
        ;;
    "three")
        print;
        echo ${1} | tr 'a-z' 'A-Z'
        ;;
    *)
        echo "Usage ${0} {one|two|three}"
        ;;
esac
           
  • function的内建变量:$0是函数名,$1、$2是后续接的变量;但它们与script的$0、$1等不同。
#!/bin/bash
function print(){
    echo "Your choice is ${1}" #这个$1必须要参考底下的指令
}

echo "This program will print your selection."
case ${1} in
    "one")
        print ; #print指令后面接参数
        ;;
    "two")
        print 
        ;;
    "three")
        print 
        ;;
    *)
        echo "Usage ${0} {one|two|three}"
        ;;
esac
           
$ ./script.sh
Usage ./script.sh {one|two|three}
$ ./script.sh one
Your choice is 
$ ./script.sh two
Your choice is 
$ ./script.sh three
Your choice is 
           

12.5 循环(loop)

12.5.1不定循环while do done、until do done

while [ 判断式 ]
do      #do是循环开始
    程序段落
done    #done是循环结束
           
until [ 判断式 ] #当判断式成立时终止循环
do
    程序段落
done
           
#让用户输入yes或YES才结束程序,否则一直告知用户输入
#!/bin/bash
while [ "${input}" != "yes" -a "${input}" != "YES" ]
do
    read -p "Please input yes/YES to stop this program: " input
done
echo "Input correct"
           
#!/bin/bash
s= #累加和
i= #累加变量
while [ "${i}" != "100" ]
do
    i=$(($i+))   #每次i都会增加1
    s=$(($s+$i)) #每次都会加和
done
echo "1 + 2 + 3 + ... + 100 = $s"
           

12.5.2 for…do…done(固定循环)

for var in con1 con2 con3 ...
do
    程序段
done
           
  • $var

    的变量内容在循环时:第一次循环

    $var

    的内容为con1;第二次循环

    $var

    的内容为con2;第三次循环

    $var

    的内容为con3。
  • 利用ping判断192.168.1.1-192.168.1.100内的主机。
#!/bin/bash
network="192.168.1" #定义网域前面部分
for num in {..}
do
    ping -c  -w  ${network}.${num} &> /dev/null && result= || result=
    if [ "${result}" ==  ]; then
        echo "Server ${network}.${num} is UP."
    else
        echo "Server ${network}.${num} is DOWN."
    fi
done
           
  • 让用户输入某目录名,然后找出该目录内文件名的权限。
#!/bin/bash

#先判断目录是否存在
read -p "Please input a directory: " dir
    if [ "${dir}" == "" -o ! -d "${dir}" ]; then
        echo "The ${dir} is NOT exist in your system."
        exit 
fi
#开始测试文件
filelist=$(ls ${dir})   # 列出所有在该目录下的文件名
for filename in ${filelist}
do
    perm=""
    test -r "${dir}/${filename}" && perm="${perm} readable"
    test -w "${dir}/${filename}" && perm="${perm} writable"
    test -x "${dir}/${filename}" && perm="${perm} executable"
    echo "The file ${dir}/${filename}'s permission is ${perm} "
done
           

12.5.3 for…do…done 的数值处理

for ((初始值; 限制值; 执行次数))
do
    程序段
done
           
  • 初始值:变量在循环中的起始值,如

    i=1

    限制值:若变量的值在限制值的范围内,就进行循环。如

    i<=100

    执行次数:每次循环变量的变化量。如

    i=i+1

12.5.4搭配随机数与数组

12.6 shell script的追踪与debug

  • -n 仅检查语法问题,不执行

    -v 将scripts的内容输出到屏幕再执行

    -x 将用到的script内容显示到屏幕

Please indicate the source: http://blog.csdn.net/gaoxiangnumber1

Welcome to my github: https://github.com/gaoxiangnumber1