手机版
你好,游客 登录 注册 搜索
背景:
阅读新闻

Shell脚本编程基础

[日期:2017-03-28] 来源:Linux社区  作者:Linux [字体: ]

什么是Shell

   操作系统最外层的程序,shell通过提示符让用户输入,向操作系统解释该输入,然后处理来自操作系统的任何结果输出来,管理用户与操作系统之间的交互。

    Shell是一个用户跟操作系统之间的一个命令解释器。Shell是用户与Linux操作系统之间沟通的桥梁。用户可以输入命令执行,又可以利用 Shell脚本编程去运行。

为什么要用到shell

shell是一个交互式程序,当用户输入一条命令,shell就解释一条,一次只处理一条命令。如果我们一些复杂操作,逐个敲命令工作量就会增大,因此,我们可以事先写一个脚本,在脚本中写入多条命令,让shell一次性把这些命令全都执行完毕,而不必一条一条的敲

常见的shell种类

    /bin/sh,/bin/csh,/bin/ksh,/bin/bash等,bash是大多数linux默认的shell程序,因此,在日常工作中被广泛使用

编程基础:

    程序:指定+数据

        指令:由程序文件提供

        数据:IO设备、文件、管道、变量等

    程序编程风格:

        过程式:以指令为中心,数据服务于指令

        对象式:以数据为中心,指令服务于数据

    编程语言:

        强类型:必须实现声明定义的变量及变量类型。如 JavaPython

        弱类型:无需事先声明变量,默认均为字符串类型,可直接调用变量。如 shell

    程序运行方式:

        编译运行:源代码 --> 编译器编译 --> 可执行的二进制文件

        解释运行:源代码 --> 运行时启动解释器,边解释边运行

    如何编写shell脚本:

        脚本文件的第一行,顶格:给出shebang,解释器路径,用于指明解释执行当前脚本的解释器程序文件

            常见的解释器:

                #!/bin/bash

                #!/usr/bin/perl

                #!/usr/bin/python

        编写第一个shell脚本:

[root@CentOS6 bin]# cat first.sh
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: first shell script
echo "Hello,world"
[root@CentOS6 bin]# bash first.sh
Hello,world
[root@CentOS6 bin]#

        一个好的shell脚本会标明、作者、日期、版本、作用,这是一个良好的习惯,不然我们代码写到百行的时候,过几个月或几年,回过头来看,又能知道自己写的是什么脚本呢

    如何运行shell脚本:

        (1)赋予脚本文件执行权限,并直接运行脚本

            chmod +x SCRIPT_FILE

            ./PATH/TO/SCRIPT_FILE

        (2)直接运行解释器,将脚本以命令参数传递给解释器程序

            bash /PATH/TO/SCRIPT_FILE

    变量:指向内存的命令空间

        变量名+指向的内存空间

        变量赋值:VAR_NAME=value

        变量类型:存储格式、表示数据范围、参与的运算

            字符型

            数值:整型、浮点型(shell不支持浮点型)

         变量替换:把变量名出现的位置替换为指定的内存空间中的数据

        变量引用:${var_name},$var_name

        变量名命名规则:

            1.不能与系统中定义过的变量同名,不能与bash中的关键字同名,如 if case等等

            2.只能包含数字、下划线、字母

            3.要做到见名只义

       bash变量类型:

            本地变量:仅对当前shell进程生效

            环境变量:对当前shell以及子shell生效

            局部变量:仅对shell进程中某一代码片段生效

            位置变量:脚本执行时传递给脚本的参数

            特殊变量:shell内置的有特殊功用的变量

           

            本地变量:

                变量赋值:name='value'

                变量引用:${name},$name

                    "":变量名会被替换为其值

                    '':变量名不会替换为值,当做字符串来处理

                查看变量:set

                撤销变量:unset var_name

           

            环境变量:

                变量赋值:

                    (1)export name='value'

                    (2)name='value'

                      export name

                    (3)declare -x name='value'

                    (4)name='value'

                      declare -x name

                变量引用:${name},$name

                查看环境变量:export、declare、env、printenv

                撤销变量:unset

                bash有许多内置的环境变量:PATH,SHELL,USER,UID,GID,HISTSIZE,HISTFILESIZE,HOME,PWD,OLDPWD,HISTFILE,HISTCONTROL等

            只读变量:只能声明,不可修改删除

                readonly var_name

                declare -r var_name

            位置变量:在执行脚本时,传递给脚本的参数

                $1,$2,...${10}、${11}对应1、2个参数

            特殊变量:shell内置变量

                $?:存放上一条命令的执行状态返回码

                $#:执行脚本时传递给脚本的参数个数

                $*:传递给脚本的所有参数,会把这些参数当做一个字符串

                $@:传递给脚本的所有参数,把每个参数当做一个字符串来处理

                $0:此脚本的名称

                $$:当前shell程序的PID

算术运算:

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

    算术运算格式:

        (1)let VAR=Expression

        (2)VAR=$[Expression]

        (3)VAR=$((Expression))

        (4)VAR=$(expr $ARG1 $OP $ARG2)

        (5)echo "Expression" | bc

    bash內建随机数生成器:$RANDOM(1-32767)

    增强型赋值:+=, -=, *=, /=, %=

        例如:let COUNT+=3

            COUNT等于自身加3

    自增,自减:

        let var+=1

        let var++

        let var-=1

        let var--

    i++运算后加1,i--运算后减1

    ++i运算前加1,--i运算前减1

逻辑运算:

    运算数:

        true:真,用1表示

        false:假,用0表示

    与运算:两个条件同时满足,则为真

       1 && 1 = 1

        1 && 0 = 0

        0 && 1 = 0

        0 && 0 = 0

       

    或运算:两个条件有一个为真,则为真

        1 || 1 = 1

        1 || 0 = 1

        0 || 1 = 1

        0 || 0 = 0

           

    :取反

        ! 1 = 0

        ! 0 = 1

 

    短路法则:

        ~]# COMMADN1 && COMMADN2

        COMMADN1为“假”,COMMADN2将不会执行

        否则,COMMADN1为“真”,COMMADN2必须执行

       

        ~]# COMMADN1 || COMMADN2

        COMMAND1为“真”,则COMMADN2不会被执行

        否则,COMMADN1为“假”,则COMMAND2必须执行

   

    异或:^

        异或的两个值,相同为假,不同则真

 

退出状态码:

    进程使用退出状态码来报告命令的执行结果,成功或失败

        $?:上一条命令的执行状态返回码

        成功:0

        失败:1-255

    自定义状态返回值:

       exit#:#为自己指定的状态返回码

       注意:当脚本中遇到exit时,将终止整个脚本运行,默认是脚本中执行的最后一条命令的状态返回值

 

 

条件测试:

    判断某个需求是都满足,如果满足则执行相应的操作,需要由测试机制来实现

     如何编写测试表达式以实现所需的测试:

        (1)评估布尔声明,以便用在条件测试中

            若真,则返回0

            若假,则返回非1

        (2)测试表达式:

            test EXPRESSION

            [ EXPRESSION ]

            [[ EXPRESSION ]]

           

            注意:EXPRESSION前后两端要有空格,否则报错

   

    bash测试类型:

        数值测试:

        字符串测试:

        文件测试:

   

        数值测试:数值比较

            -eq:测试是否等于

            -gt:是否大于

            -lt:是否小于

            -ge:是否大于等于

            -le:是否小于等于

            -ne:是否不等于

       

        字符串测试:字符比较

            ==:测试字符是否相等

            !=:是否不等于

            =~:右侧的字符是否能被左侧的PATTERN所匹配

            >:是否大于

            <:是否小于

            -z:测试指定字符串是否为空,空为真,否则为假

            -n:测试指定的字符串是否不不空,不空则真,否则为假

           

            注意:字符串比较实用[[ ]]

       

       文件测试:

            存在性测试:

                [ -a FILE ]:测试文件是否存在

                [ -e FILE ]

            存在性及类型测试:

                [ -b FILE ]:测试文件是否为快设备文件

                [ -c FILE ]:测试文件是否为字符设备

                [ -d FILE ]:测试文件是否为目录文件

                [ -f FILE ]:测试文件是否为普通ASCII文件

                [ -p FILE ]:侧是文件是否为管道文件

                [ -h FILE ]或[ -L FILE ]:测试文件是否为符号链接文件

                [ -S FILE ]:测试文件是否为套接字文件

            文件权限测试:      

                [ -r FILE ]:测试当前用户是否对指定文件可读

                [ -w FILE ]:测试当前用户是否对文件可写

                [ -x FILE ]:测试当前用户是否对文件可执行

            特殊权限测试:

                [ -u FILE ]:测试文件是否具有SUID权限

                [ -g FILE ]:测试文件是否具有SGID权限

                [ -k FILE ]:测试文件是否有Sticky权限

            测试文件中是否有内容:

                [ -s FILE ]:测试文件中是否有内容,有为真,否则假

            时间戳:

                [ -N FILE ]:文件自从上次读取后是否被修改过

            从属关系测试:

                [ -O FILE ]:测试当前用户是否是文件的属主

                [ -G FILE ]:测试当前用户是否是文件属组

            双目测试:

                [ FILE1 -ef FILE2 ]:FILE1与FILE2是否指向同一个文件系统的相同inode的硬链接

                [ FILE1 -nt FILE2 ]:FILE1是否新于FILE2

                [ FILE1 -ot FILE2 ]:FILE1是否旧于FILE2

组合条件测试:

    第一种方式:

        COMMAND1 && COMMAND2

        COMMAND1 || COMMAND2

        ! COMMAND

       

        [ -x FILE ] && [ -r FILE ]

   

    第二种方式:

        -a:与

        -o:或

        !:非

 

        [ EXPRESSION1 -a EXPRESSION2 ]

        [ EXPRESSION1 -o EXPRESSION2 ]

        [ ! EXPRESSION ]

练习:

1.编写脚本/root/bin/systeminfo.sh,显示当前主机系统信息,包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小。

[root@CentOS6 bin]# cat systeminfo.sh     #脚本内容
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 显示当前系统信息包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小
   
Hostname=$(hostname)
IP=$(ifconfig sed -n '2p' cut -d: -f2 | cut -d' ' -f1)
OS=$(lsb_release | grep "Description" cut -d: -f2 | sed 's@^[[:space:]]\+@@')
Kernel=$(uname -r)
CPU=$(cat /proc/cpuinfo grep "model name" cut -d: -f2 | sed 's@^ @@')
MEM=$(free -h | tr -s ' ' cut -d' ' -f2 | sed -n '2p')
DISK=$(fdisk -l | grep "Disk /dev/sd" cut -d: -f2 | cut -d, -f1)
   
echo -e "\033[32mShow system information.\033[0m"
echo "Hostname: $Hostname"
echo "IPv4 address: $IP"
echo "OS version: $Kernel"
echo "CPU model: $CPU"
echo "Mem size: $MEM"
echo "Disk size: $DISK"
[root@CentOS6 bin]# vim systeminfo.sh 
[root@CentOS6 bin]# bash systeminfo.sh     #运行结果
Show system information.
Hostname: CentOS6.localdomain
IPv4 address: 10.1.252.233
OS version: 2.6.32-642.el6.x86_64
CPU model: Intel(R) Core(TM) i7-6500U CPU @ 2.50GHz
Mem size: 980M
Disk size:  107.4 GB
[root@CentOS6 bin]#

2.编写脚本/root/bin/backup.sh,可实现每日将/etc/目录备份到/root/etcYYYY-mm-dd中

[root@CentOS6 bin]# cat backup.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 将/etc/目录下的内容备份到/root/etcYYYY-mm-dd中
   
FILE="/root/etc`date +%F`"
   
cp -rpf /etc/ $FILE 
   
echo "$FILE file backup finished."
[root@CentOS6 bin]# bash backup.sh 
/root/etc2016-08-12 file backup finished.
[root@CentOS6 bin]# ls -d ../etc2016-08-12/
../etc2016-08-12/
[root@CentOS6 bin]# 

3.编写脚本/root/bin/disk.sh,显示当前硬盘分区中空间利用率最大的值

[root@CentOS6 bin]# cat disk.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 显示当前硬盘分区中空间利用率最大的值
   
Max_Used=$(df -lhTP | sed -r 's@.* ([0-9]+%).*@\1@' sed '1d' sort tail -1)
   
echo "分区空间利用率最大为$Max_Used"
[root@CentOS6 bin]# bash disk.sh 
分区空间利用率最大为19%
[root@CentOS6 bin]# 

4.编写脚本/root/bin/links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序

[root@CentOS6 bin]# cat links.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序
   
echo -e "当前连接本机的远程主机IP\n"
echo -e "连接次数     IP地址"
   
netstat -tn | tr -s ' ' cut -d' ' -f5 | cut -d: -f1 | sed '1,2d' sort uniq -c | sort -n -t' ' -k1
[root@CentOS6 bin]# bash links.sh 
当前连接本机的远程主机IP
连接次数     IP地址
      2 10.1.250.60
      3 10.1.253.23
[root@CentOS6 bin]# 

5.写一个脚本/root/bin/sumid.sh,计算/etc/passwd文件中的第10个用户和第20用户的ID之和

[root@CentOS6 bin]# cat sumid.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#descriptions: 计算/etc/passwd文件中的第10个用户和第20用户的ID之和
   
FILE="/etc/passwd"
   
ID_10=$(cat $FILE | sed -n '10p' cut -d: -f3)
ID_20=$(cat $FILE | sed -n '20p' cut -d: -f3)
   
SUMID=$[$ID_10+$ID_20]
echo "$FILE file 10 user and 20 user uid sum is $SUMID."
[root@CentOS6 bin]
[root@CentOS6 bin]# bash sumid.sh 
/etc/passwd file 10 user and 20 user uid sum is 180.
[root@CentOS6 bin]# 

6.写一个脚本/root/bin/sumfile.sh,统计/etc, /var, /usr目录中共有多少个一级子目录和文件

[root@CentOS6 bin]# cat sumfile.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 统计/etc, /var, /usr目录中共有多少个一级子目录和文件
   
ETC=$(ls -A /etc/ wc -l)
VAR=$(ls -A /var/ wc -l)
USR=$(ls -A /usr/ wc -l)
   
SUMFILE=$[$ETC+$VAR+$USR]
echo "/etc/ /var/ /usr/ A total of $SUMFILE files"
[root@CentOS6 bin]
[root@CentOS6 bin]# bash sumfile.sh 
/etc/ /var/ /usr/ A total of 296 files
[root@CentOS6 bin]# 

7.写一个脚本/root/bin/argsnum.sh,接受一个文件路径作为参数;如果参数个数小于1,则提示用户“至少应该给一个参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数

[root@CentOS6 bin]# cat argsnum.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 接受一个文件路径作为参数;如果参数个数小于1,则提示用户“至少应该给一个参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数
   
[ $# -lt 1 ] && echo "At least one parameter." && exit 1
   
SpaceLine=$(grep "^$" $1 | wc -l)
echo "$1 spaceline is $SpaceLine."
[root@CentOS6 bin]
[root@CentOS6 bin]# bash argsnum.sh     #没有给出参数,执行结果提示至少一个参数
At least one parameter.
[root@CentOS6 bin]# bash argsnum.sh /etc/inittab     #给出一个文件路径,显示此文件的空白行数
/etc/inittab spaceline is 8.
[root@CentOS6 bin]# 

8.写一个脚本/root/bin/hostping.sh,接受一个主机的IPv4地址做为参数,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”

[root@CentOS6 bin]# cat hostping.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 接受一个主机的IPv4地址做为参数,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”
   
[ $# -lt 1 ] && echo "At least one parameter." && exit 1
   
ping -c 1 -W 1 $1 &> /dev/null && echo "$1 IP accessible" || echo "$1 IP not accessible."
[root@CentOS6 bin]
[root@CentOS6 bin]# bash hostping.sh 10.1.1.1   
10.1.1.1 IP not accessible.        #IP不可访问
[root@CentOS6 bin]
[root@CentOS6 bin]# bash hostping.sh 10.1.252.233
10.1.252.233 IP accessible        #IP可访问
[root@CentOS6 bin]# 

9.chmod -rw /tmp/file1,编写脚本/root/bin/per.sh,判断当前用户对/tmp/fiile1文件是否不可读且不可写

[root@CentOS6 /]# cat per.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 判断当前用户对/tmp/fiile1文件是否不可读且不可写
   
FILE="/tmp/file1"
   
[ ! -r $FILE -a ! -w $FILE  ] && echo "yes" || echo "no"    #如果不可读也不可写,则输出yes,否则输出no
[root@CentOS6 /]
[root@CentOS6 /]# ll /tmp/file1     #首先查看/tmp/file1文件的权限,为640
-rw-r-----. 1 root root 0 Aug 11 22:43 /tmp/file1
[root@CentOS6 /]
[root@CentOS6 /]# bash per.sh 
no        #root用户对此文件可读写,输出no
[root@CentOS6 /]# su - zhai    #切换至其他用户
[zhai@CentOS6 ~]$ whoami
zhai
[zhai@CentOS6 ~]$ bash /per.sh 
yes        #zhia用户对此文件不可读也不可写,输出yes
[zhai@CentOS6 ~]$ 

10.编写脚本/root/bin/nologin.sh和login.sh,实现禁止和充许普通用户登录系统。

用户不可登录nologin
[root@CentOS6 bin]# cat nologin.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 实现禁止普通用户登录系统
   
FILE="/etc/nologin"
   
([ ! -e $FILE ] && touch $FILE && echo "create $FILE finished. ") || echo "$FILE exists."    #判断/etc/nologin文件是否存在,如果不存在,则创建
[root@CentOS6 bin]# bash nologin.sh 
create /etc/nologin finished.     #提示创建文件成功,此时普通用户已经不可登录系统了
[root@CentOS6 bin]#

 

用户可登录login
[root@CentOS6 bin]# cat login.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 实现允许普通用户登录系统
   
FILE="/etc/nologin"
   
([ -e $FILE ] && rm -rf $FILE && echo "delete $FILE finished.") || echo "$FILE not exists."    #判断/etc/nologin文件是否存在,如果存在就删除
[root@CentOS6 bin]
[root@CentOS6 bin]# bash login.sh 
delete /etc/nologin finished.    #提示删除文件完成,此时普通用户就可以登录到系统了
[root@CentOS6 bin]#

11.写一个脚本/root/bin/hostping.sh,接受一个主机的IPv4地址做为参数,先判断是否合格IP,否,提示IP格式不合法并退出,是,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”

[root@CentOS6 bin]# cat hostping.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 写一个脚本/root/bin/hostping.sh,接受一个主机的IPv4地址做为参数,先判断是否合格IP,否,提示IP格式不合法并退出,是,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”
   
[ $# -lt 1 ] && echo "At least one parameter." && exit 1
   
echo "$1" grep -E -q "\<([1-9]|[1-9][0-9]|1[0-9]{2}|2[0-1][0-9]|22[0-3])\>\.(\<([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\>\.){2}\<([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\>"
RETVAL=$?
[ $RETVAL -ne 0 ] && echo "$1 not is IP address." && exit 5
   
(ping -c 1 -W 1 $1 &> /dev/null && echo "$1 IP accessible") || (echo "$1 IP not accessible" && exit 7)
[root@CentOS6 bin]
[root@CentOS6 bin]# bash hostping.sh 255.1.1.1    #给定非IP地址
255.1.1.1 not is IP address.       
[root@CentOS6 bin]# bash hostping.sh 10.1.252.255    #给定正确IP地址
10.1.252.255 IP accessible        #此IP可访问
[root@CentOS6 bin]# bash hostping.sh 10.1.252.252
10.1.252.252 IP not accessible    #此IP不可访问
[root@CentOS6 bin]# 

12.计算1+2+3+…+100的值

[root@CentOS6 bin]# cat sum2.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 计算1到100之间所有整数之和
   
SUM=$(echo {1..100} | tr ' ' '+' bc)
   
echo "1到100之间的所有整数之和为$SUM."
[root@CentOS6 bin]
[root@CentOS6 bin]# bash sum2.sh 
1到100之间的所有整数之和为5050.
[root@CentOS6 bin]# 

13.计算从脚本第一参数A开始,到第二个参数B的所有数字的总和,判断A是否大于B,否提示错误并退出,是则计算

[root@CentOS6 bin]# cat sum3.sh 
#!/bin/bash
#author: xiaozhai
#version: 1.0
#date: 2016-8-11
#description: 计算从脚本第一参数A开始,到第二个参数B的所有数字的总和,判断B是否大于A,否提示错误并退出,是则计算之
   
[ $# -ne 2 ] && echo "Unknown Argument." && exit 1
expr $1 + $2 &> /dev/null && echo "Non integer parameter." && exit 2
[ $1 -gt $2 ] && echo "$1 greater $2." && exit 3
   
SUM=$(seq $1 $2 | tr '\n' ' ' tr ' ' '+' sed -r 's@\+$@\n@' bc)
   
echo "$1至$2之间所有整数之和为$SUM."
[root@CentOS6 bin]
[root@CentOS6 bin]# bash sum3.sh     #脚本后不跟参数,提示未知参数
Unknown Argument.
[root@CentOS6 bin]# bash sum3.sh 1 a    #给出的参数不是整数
Non integer parameter.
[root@CentOS6 bin]# bash sum3.sh 10 1    #$1大于$2
10 greater 1.
[root@CentOS6 bin]# bash sum3.sh 1 99    #正确执行脚本
1至99之间所有整数之和为4950.
[root@CentOS6 bin]#

本文永久更新链接地址http://www.linuxidc.com/Linux/2017-03/142274.htm

linux
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款