您的位置:首页 > 运维架构 > Shell

bash script 编程基础

2015-09-14 09:57 423 查看
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

注意:脚本中任何位置执行了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 执行过程示意图:



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



其中#/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

注意:脚本中任何位置执行了exit命令即会终止当前shell进程
//自定义状态返回值为1



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




script的执行方式的区别(bash,source,.)
以下面的name.sh脚本为例



[root@lys scripts]# sh name.sh //先用bash执行脚本
please input your first name: l
please input your last name: ys
Your full name is : lys



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



我再echo $firstname $lastname 就有值了 。因为source和.会在父进程中执行,各项操作都会在原本的bash内生效。这下明白了bash,source和.执行shell脚本的区别了吧。
条件测试
如何测试一个shell脚本呢?除了根据运行的命令的状态结果外,我们还可以使用测试表达式。当我们要检测系统上某些文件或者相关属性时,就可以使用test这个命令来显示整个结果。
关于两个整数的比较,例如,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 实现字符串的输出。
让用户输入一个文件名,我们来判断这个文件是否存在,若不存在则给与“Filename is not exist”的信息,并中断程序

若这个文件存在,则判断它是个文件还是目录,结果输出“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 查看程序的执行过程




例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"



除了使用test之外,还可以使用判定符号“[]”来进行数据的判断。举例来说
例3.判断一个文件是否存在



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



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 : " $@




以上我添加了四个参数,第一个偏移一位,第二次偏移三位。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




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



例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



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



总结:shell script是利用shell的功能所写的一个程序,这个程序使用纯文本文件,将一些shell的语法与命令写在里面,搭配正则表达式,管道命令与数据流重定向等功能,以达到我们要处理的目的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux bash script