1.何谓shell script
shell script是利用shell的功能写一个“程序”,这个程序是使用纯文本文件,将一些shell的语法与命令写在里面。
2.脚本或程序源文件都是纯文本文件。
3.脚本或程序的执行一般有两种方式:
编译执行:预处理-->编译-->汇编-->链接;编译执行是一种计算机语言的执行方式。
由编译程序将目标代码一次性编译成目标程序,再由机器运行目标程序。如:PASCAL,C,C++等语言。
效率高于解释执行。
解释执行:由解释器全程参与运行过程,每次读取一行,运行一行;程序是由一组执行和数据组成。
4.bash的执行过程
1>命令的执行是从上到下,从左到右的分析与执行
2>命令执行时,命令和参数间的多个空白都会被忽略
3>空白行也会被忽略
4>没读取一个Enter字符,就开始执行该程序
5>“#”作为批注,任何加在#后面的数据都将视为批注
6>shell script 都是以*.sh结尾,而且一个shell脚本能否被执行,必须得有x权限
7>bash shell程序 必须由bash进程来执行
5.bash shell 中的变量,什么是变量?变量就是程序中可变化的量,通过变量来命名内存空间。
bash的变量类型如下:
本地变量:当前shell进程;
环境变量:当前shell进程及其子进程;
局部变量:某个函数执行过程;
位置参数变量:在脚本中引用传递给脚本的参数;在函数中引用传递给函数的参数;
特殊变量:
$?:上一个命令执行状态的返回值:
程序执行可能有两种返回值:
1. 程序执行结果
2. 程序状态返回吗(0-255)
0 则为执行正确
1-255 则执行出错(1,2,127系统预留);
$#:获取当前shell命令行中参数的总个数
$*:获取当前shell的所有参数 “$1 $2 $3 …,受IFS控制
$@:这个程序的所有参数 “$1″ “$2″ “$3″ “…”,不受IFS控制
$0 获取当前执行的shell脚本的文件名
$n 获取当前执行的shell脚本的第n个参数值,n=1..9
$$ 获取当前shell的进程号(PID)
$! 执行上一个指令的PID
自定义脚本的状态结果:
exit [n]
注意:脚本中任何位置执行了exit命令即会终止当前shell进程;
6.变量的类型:数值,字符,其中数值又可以分为整数和浮点数,字符分为ASCII和纯文本字符。
7.变量类型的作用:存储空间,进行数值运算,定义存储格式
8.bash的变量使用特点:弱类型、无须事先声明;
9.变量的命名规则:
     首个字符必须为字母(a-z,A-Z)
     中间不能有空格,可以使用下划线(_)
     不能使用标点符号
     不能使用bash里的关键字(可用help命令查看保留关键字)
10.本地变量和环境变量
1>本地变量:varname=value:作用域为整个bash进程可以使用。其中varname是变量名,=是赋值符号,
value是值
引用:
弱引用: "", 其内部的变量引用会被替换为变量值;
强引用:'',其变量的变量引用会保持原有字符;
命令引用:`COMMAND`, $(COMMAND),引用命令的执行结果;
声明为整型:
declare -i name[=value]
let name=value
2>环境变量:被“导出”的本地变量,作用域为当前shell进程及其子进程,不能影响到其父进程
环境变量的两种常见的定义方法:
export name[=value]
declare -x name[=value]
查看所有环境变量:env, printenv, export ,环境变量的销毁也是使用unset  name
11.变量的生命周期,当一个shell进程终止后,变量就会自动销毁,或者手动销毁变量,可以使用unset 变量名

shell的工作原理:

shell是用户和Linux内核之间的接口程序。如果把Linux内核想象成一个球体的中心,shell就是围绕内核的外层。当从shell或其他程序向Linux传递命令时,内核会做出相应的反应。shell是一个命令语言解释器,它拥有自己内建的shell命令集,shell也能被系统中其他应用程序所调用。shell的另一个重要特性是它自身就是一个解释型的程序设计语言,shell程序设计语言支持绝大多数在高级语言中能见到的程序元素,如函数、变量、数组和程序控制结构。当普通用户成功登录,系统将执行一个称为shell的程序。正是shell进程提供了命令行提示符。作为默认值对普通用户用“$”作提示符,对超级用户(root)用“#”作提示符。

它的执行过程基本上按如下步骤:
(1)读取用户由键盘输入的命令行
(2)分析命令,以命令名作为文件名,并将其它参数改造为系统调用execve( )内部处理所要求的形式
(3)终端进程调用fork( )建立一个子进程
(4)终端进程本身用系统调用wait4( )来等待子进程完成(如果是后台命令,则不等待)。当子进程运行时调用execve( ),子进程根据文件名(即命令名)到目录中查找有关文件(这是命令解释程序构成的文件),将它调入内存,执行这个程序(解释这条命令)
(5)如果命令末尾有&号(后台命令符号),则终端进程不用系统调用wait4( )等待,立即发提示符,让用户输入下一个命令,转⑴。如果命令末尾没有&号,则终端进程要一直等待,当子进程(即运行命令的进程)完成处理后终止,向父进程(终端进程)报告,此时终端进程醒来,在做必要的判别等工作后,终端进程发提示符,让用户输入新的命令,重复上述处理过程。

shell 执行过程示意图:

wKioL1X2SYKgjQcWAAHCmfruSmo743.jpg

好了,现在我们开始进入bash shell的世界,首先我们通过bash shell 实现字符串的输出。为了以后编写shelll script 方便,建议大家建立一个~/scripts 目录,用于存放脚本。

 wKioL1X1fwjiD5zqAABk2r9ZLNY677.jpg

其中#/bin/bash是shell编程的固定格式,来声明这个文件内的语法使用bash的语法,当这个程序执行时,它就能够加载bash的相关环境配置文件。整个script中,除了第一个“#”是用来申明shell之外,其他的#都是“批注”用途。为了以后方便查看shell script,建议写上本shell的功能,作者,时间等关键休息,如上所示。

bash的常用选项:

-n: 检查脚本中的语法错误;

-x:调试执行脚本;

-v:在执行脚本前,先把脚本输出到屏幕

检查脚本中的语法错误,什么都没有,说明没有语法错误。

[root@lys scripts]# bash -n first.sh

调试执行脚本

[root@lys scripts]# bash -x first.sh

+ echo -e 'welcome to linux shell script.\a \n'

welcome to linux shell script.

+ exit 0

自定义脚本的状态结果:

exit [n]

注意:脚本中任何位置执行了exit命令即会终止当前shell进程

//自定义状态返回值为1

wKiom1X1gGSiU2joAABZU99NLjQ993.jpg

//执行程序后查看 ,为1说明是正确的。

wKioL1X1gjjTwZ51AAA2rChWoxE700.jpg

script的执行方式的区别(bash,source,.)

以下面的name.sh脚本为例

wKioL1X1hwawADyGAABity56ycI142.jpg

[root@lys scripts]# sh name.sh //先用bash执行脚本

please input your first name: l

please input your last name: ys

Your full name is : lys

wKiom1X1hX-xQ-VGAAAq_txQaTk848.jpg

看到没有怎么设置好的变量firstname,lastname都没有值,这是为什么呢?当你使用直接执行的方式来处理时,系统会给与一个新的bash来执行name.sh里面的命令,因此我们这个变量firstname,lastname其实是在新的bash里面执行的,子进程完成后,子进程内的各项变量或操作将会结束而不会传回父进程中。下面我们用source或者.来执行看看。

wKiom1X1htyiMijZAABQFXGJfn8287.jpg

我再echo $firstname $lastname 就有值了 。因为source和.会在父进程中执行,各项操作都会在原本的bash内生效。这下明白了bash,source和.执行shell脚本的区别了吧。

条件测试

如何测试一个shell脚本呢?除了根据运行的命令的状态结果外,我们还可以使用测试表达式。当我们要检测系统上某些文件或者相关属性时,就可以使用test这个命令来显示整个结果。

  1. 关于两个整数的比较,例如,num1 ,num2

-eq                     两数相等

ne                    两数不等

-gt                   num1大于num2

-lt                    num1小于num2

-ge                    num1大于等于num2

-le                    num1小于等于num2

2.字符串的数据,ASCII数值越大,字符比较时其值越大

测试的标志                   代表的意义

-z string                   是否为空;空则为“真”,否则为“假”

-n string                   是否不空;不空则“真”,空则为“假”

test str1 = str2         判定str1是否等于str2,等于为真,不等于为假

test str1 != str2         判定str1是否不等于str2,不等于为真,等于为假

3.两个文件之间的测试,如test file1 和file2

测试标志               代表的意义

-nt             判定file1是否比file2新

-ot             判定file1是否比file2旧

-ef             判定file1和file2是否为同一个文件

4.文件权限的检测

测试参数                                                        代表的意义

-r                   检测此文件是否存在且具有“可读”权限

-w                   检测此文件是否存在且“可写”权限

-x                   检测此文件是否存在且“可执行”权限

-u                   检测此文件是否存在且“SUID”权限

-g                   检测此文件是否存在且“SGID”权限

-k                   检测此文件是否存在且“Sticky bit”权限

-s                   检测此文件是否存在且为“非空白文件”

-O                   当前用户是否为指定文件的属主

-G                   当前用户是否为指定文件的属组

5.文件类型的判定

参数                                     代表的意义

-e                                 是否存在;存在则为“真”,否则为“假”

-f                                    该文件名是否为普通文件

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

-b                                    是否存在且为块设备文件

-c                                    是否存在且为字符设备文件

-S                                    是否存在且为套接字文件

-P                                    是否存在且为管道文件

-L                                    是否存在且为链接文件

6.多重条件判定


-a                  两个条件同时成立

-o                  任何一个条件成立

!                   反向状态,不成立为真

特殊设备:

/dev/null: 空,bit buckets,吞下所有数据,并直接丢弃;

/dev/zero:吐出一堆0

好了,现在我们开始进入bash shell的世界,首先我们通过bash shell 实现字符串的输出。

  1. 让用户输入一个文件名,我们来判断这个文件是否存在,若不存在则给与“Filename is not exist”的信息,并中断程序

  2. 若这个文件存在,则判断它是个文件还是目录,结果输出“Filename is regular file” 或“Filename is a directorp”

[root@lys scripts]# cat test.sh
#/bin/bash
#

#给用户说明,这个脚本是干嘛用的

echo -e "Please input a filename ,i will check it a file or a directory\n" 

#给用户提示,输入一个文件

read -p  "Please input a filename :  " filename

#让用户输入文件名,并且判断用户是否真的有输入字符串
test -z  $filename && echo "You must input a filename."   && exit 0

#判断文件如果不存在就中断程序
test ! -e $filename  && echo "The file `$filename` not exist" && exit 0

#判断文件是否为普通文件
test -f $filename  && filetype="regulare file"

#判断文件是否为目录
test -d $filename  && filetype="directory file"

#输出文件类型到屏幕
echo "The filename :$filename  is a  $filetype" 

以下-n 参数是检测语法是否有错误,-x 查看程序的执行过程

wKioL1X2NYvyEBWiAADglxAoDfA068.jpg

 

例2.输入一个文件,判定该文件的属性。

[root@lys scripts]# cat  test1.sh
#/bin/bash
#

read -p  "Please input a filename: " filename
test -z  $filename && echo "you must input a filename. " && exit 0

#判定文件是否存在
test ! -e $filename && echo "the filename `$filename` is not exist. " && exit 0

#判定文件是否有“读”权限
test -r $filename && perm="readable"

#判定文件是否有“写”权限

test -w $filename && perm="$perm  writable"

#判定文件是否有“执行”权限

test -x $filename && perm="$perm  executable"
echo  "the filename permissions are : $perm"
wKioL1X2O1_y8sV_AABKDX7-C00223.jpg

除了使用test之外,还可以使用判定符号“[]”来进行数据的判断。举例来说

例3.判断一个文件是否存在

wKioL1X2PTjxhGFoAAAZTNB5J1k362.jpg

shell script的默认变量

我们知道命令可以带参数,那么shell script能否带参数呢?当然可以。通过命令后面接参数,这个命令的执行就不需要再次输入一些变量行为。其实,在shell script中针对参数已经设置好了一些变量名称了,如下所示

/path/to/scriptname   opt1 opt2 opt3

$0                             $1     $2     $3

执行脚本为$0这个变量,第一个参数$1,以此类推……。其中还有一些特殊变量

$#: 传递给脚本或函数的参数的个数

$@:代表"$1" "$2" "$#" ,每个变量都是独立的

$*:代表"$1 $2  $3" ,变量在同一个引号内部

例4.执行一个携带参数的脚本,输出脚本的文件名,参数个数,全部参数内容,第一个参数,第二个参数。

[root@lys scripts]# cat var.sh
#/bin/bash
#

#脚本文件名
echo  "The script name is :  " $0    

#参数个数        

echo  "Total parameter of this script is :  " $#   

[ "$#" -lt 2 ] && echo "tht parameter is less than 2." && exit 0

#列出所有参数
echo "whole parameter : "  $@   

# 列出第一个参数

echo "The first parameter : " $1   

#列出第二个参数    
echo "The second parameter: " $2   

wKiom1X2VDijv7YEAABc2vLmBUM390.jpg

shift:可以是参数变量号码偏移,以shift.sh这个脚本来说明

例5.

[root@lys scripts]# cat shift.sh
#/bin/bash
#
echo  "The script name is :  " $0
echo  "Total parameter of this script is :  " $#

echo "whole parameter : "  $@

 #默认偏移一位shift                                                                       

echo  "Total parameter of this script is :  " $#

echo "whole parameter : "  $@

#指定偏移三位
shift 3                                                                   

echo  "Total parameter of this script is :  " $#

echo "whole parameter : "  $@

wKiom1X2VU7jnWBYAACMsYO4D8U414.jpg

以上我添加了四个参数,第一个偏移一位,第二次偏移三位。shift可以移动变量位置。

shell中与用户的交互

read命令:read [options] VAR...

-p    固定格式
-t     timeout,提示失效时间单位为s

例6.read命令的使用

[root@lys scripts]# cat read.sh
#/bin/bash
#

#提示用户输入一个数字,等待时间5s
read -p   "Please input a number : " -t 5  num            echo  "the number is $num"
shell中命令的引用,通常有两个格式:1.    `commond`   2.$(commond),看下面两个简单例子

1.[root@lys ~]# echo "`date`"
Mon Sep 14 13:18:15 CST 2015
2.[root@lys ~]# num=$(wc -l  /etc/passwd )
[root@lys ~]# echo $num
25 /etc/passwd

shell script的条件判断式:

if ……then是最常见的条件判断式。当某个条件符合时,就进行某项工作。

格式:

if [条件判断式];then

   当满足条件时的命令工作内容;

fi

例1.

写一个脚本传入两个参数,判定参数大小。

[root@lys scripts]# cat num.sh
#/bin/bash
#提示用户输入两个变量
read -p  "Please input two numbers :  "  -t  5  num1 num2

#判断用户是否输入变量
if [  -z "$num1" ] ;then
      echo "Please input two numbers."
      exit 1
fi

#判断用户是否输入变量

if [  -z "$num2" ] ;then
      echo "Please input two numbers."
      exit 1
fi

# 判断num1是否大于等于num2
if [ $num1 -ge $num2 ] ;then
      echo "Max number is $num1,Min number is $num2"
else
     echo "Max number is $num2,Min number is $num1"
fi

例2.

用户随便输入一个字符,并判断该字符是否匹配

[root@lys scripts]# cat n
name.sh  num.sh  
[root@lys scripts]# cat yn.sh
#/bin/bash
#program
#show user`s choice
#2015/9/14

#提示用户输入字符
read -p  "Please enter [y/n] : " yn

#判断输入的是“Y” 还是“y”
if  [ $yn == "Y" ] || [ $yn == "y" ] ;then
    echo " ok,continue."
    exit 0
fi

#判断输入的是“N” 还是“n”
if [ $yn == "N" ] || [ $yn == "n" ]  ; then

    echo "oh,interrupt."
    exit 0
fi
echo  " I dont know what your choics is " && exit 0

多重,复杂条件判断: 

1.格式如下:

if [条件判断式];then

当满足条件时的命令工作内容;

else

当不满足条件时的命令工作内容;

fi

2.格式如下:

if [条件判断式];then

当满足条件时的命令工作内容;

elif [条件判断式2] ;then

当条件判断式2成立时,执行命令工作内容

else

当条件判断式1和2都不成立时执行

fi

例3.使用if多分支实现比较数值大小。如下所示

[root@lys scripts]# cat num1.sh
#/bin/bash
#输入两个数字
read -p  "Please input two numbers :  "  -t  5  num1 num2
#判断用户是否输入变量

if [  -z "$num1" ] ;then
      echo "Please input two numbers."
      exit 1
fi

#判断用户是否输入变量

if [  -z "$num2" ] ;then
      echo "Please input two numbers."
      exit 1
fi

#num1是否大于num2
if [ $num1 -gt $num2 ] ;then
      echo "Max number is $num1,Min number is $num2"

#num1是否小于num2
elif [ $num1 -lt $num2 ] ; then

     echo "Max number is $num2,Min number is $num1"
else
     echo "$num1 == $num2 "

fi

wKioL1X2bcqic8m1AADNVJXcjS4041.jpg 

case ...esac判断式:case 语句匹配一个值或一个模式,如果匹配成功,执行相匹配的命令。

[root@lys scripts]# cat  case.sh
#/bin/bash

 

echo " Input a number between 1 to 4."                    #输入1-4
echo " Your number is:   "
read  Num                                                                 #读入变量
case $Num in                                                            #匹配变量值      
    1)  echo 'You select 1'
    ;;
    2)  echo 'You select 2'
    ;;
    3)  echo 'You select 3'
    ;;
    4)  echo 'You select 4'
    ;;
    *)  echo 'You do not select a number between 1 to 4'
    ;;
esac
利用function功能:

linux shell 可以定义函数,然后在shell脚本中可以随便调用。下面说说它的定义方法,以及调用需要注意的地方。

语法:

[ function ]   funname [()]

{

        action;

       [return int;]

 }

实例:

[root@lys scripts]# cat case1.sh
#/bin/bash

#定义一个函数申明
function printit () {
       echo -n "Your choice is  "
}
       echo "This program will print your selection "
case $1 in
  "one")
  printit;echo $1
  ;;
  "two")
  printit;echo $2
;;
  "three")
  printis;echo $3
;;
  * ) 
  echo "Usage $0 {one|two|three}"
;;
esac

另外,function也是拥有内置变量的。它的内置变量与shell script很类似,函数名称代表$0.而后续接的变量也是以$1,$2……来代替的。

循环:

除了if...then...fi这种条件判断式之外,循环可能就是程序当中最重要的一环了。循环可以不断的执行某个程序段落,直到用户设置的条件达成为止。

 一般来说,不定循环就是下面这两种状态

1.while [ condition ]

do                      循环的开始

程序段落

done                  循环的结束

 2.until [condition]

do

程序段落

done

假设现在让用户输入yes或者YES才结束程序,否则一直提醒用户输入字符串。

[root@lys scripts]# cat while.sh
#/bin/bash
#

while  [ "$ys" != "YES" -a "$ys" != "yes" ]
do
read -p  "Please input YES/yes to stop this program: " ys
done
echo  "you are right."

上面例题说明的是当ys这个变量的值既不是YES,也不是yes时就一直提醒用户输入,如果是就结束程序。

[root@lys ~]# cat until.sh
#/bin/bash


until [ "$ys" == "YES"  -o  "$ys" == "yes" ]
do
read -p  "Please input YES/yes to stop this program: " ys
done
echo  "you are right."

说明例题说明的是ys这个变量的值是YES或者yes,程序就执行,否则一直提醒用户输入。

 for...do...done

相对于while,until的循环方式,for这种语法则是“已经知道要执行几次循环”

for  var in con1 con2 con3 ……

do

    程序段

done

例1.通过cut命令找出单纯的账号后,以id分别检查用户的标识符。

[root@lys scripts]# cat test2.sh
#/bin/bash

users=$( cut -d ':' -f1 /etc/passwd)
for username in $users
do
    id $username
done

 wKiom1X400mAS5-SAAJTA3y9RBM138.jpg

例2 写一个脚本查看本地局域网内在线的主机

[root@lys scripts]# cat ping1.sh
#/bin/bash
network="192.168.2"                  #定义一个网段
for i in $(seq 1 254)                      #定义变量i,i的值从1到254
ping -c  4  $network.$i                   #-c 指定ping包的个数
done
wKioL1X42-WigQt7AANNatw4yUM793.jpg

for...do...done的数值处理

语法

for ((初始值;循环条件;循环变量增值))

do

      程序段

done

初始值:某个变量在循环中的的初始值

限制值:当变量在这个限制值的范围内,就继续执行

执行跨度:每一次循环时的变化量

例1.for循环的简单实例

[root@lys scripts]# cat  test3.sh
#/bin/bash

for (( i=1;i<=5;i++ ))
do
    echo  "welcome $i times..."
done

wKiom1X43jyjwzdEAABBgOSz7-M841.jpg 

总结:shell script是利用shell的功能所写的一个程序,这个程序使用纯文本文件,将一些shell的语法与命令写在里面,搭配正则表达式,管道命令与数据流重定向等功能,以达到我们要处理的目的。