鸟哥的linux私房菜_学习shell script

28 篇文章 0 订阅

13 学习shell script


13.1 什么是shell script


13.1.1 为什么学习shell script


13.1.2 第一个shell script 的编写与执行


注意事项:
1. 指令的执行是从上而下、从左而右的分析与执行;
2. 指令的下达就如同第四章内提到的: 指令、选项与参数间的多个空白都会被忽略掉;
3. 空白行也将被忽略掉,并且 [tab] 按键所推开的空白同样视为空格键;
4. 如果读取到一个 Enter 符号 (CR) ,就尝试开始执行该行 (或该串) 命令;
5. 至于如果一行的内容太多,则可以使用『 \[Enter] 』来延伸至下一行;
6. 『 # 』可做为批注!任何加在 # 后面的资料将全部被视为批注文字而被忽略!


执行方法


  直接指令下达: shell.sh 文件必须要具备可读与可执行 (rx) 的权限:
假设你写的这个程序文件名是 /home/dmtsai/shell.sh :
o  绝对路径:使用 /home/dmtsai/shell.sh 来下达指令;
o  相对路径:假设工作目录在 /home/dmtsai/ ,则使用 ./shell.sh 来执行
o  变量『PATH』功能:将 shell.sh 放在 PATH 指定的目录内,例如: ~/bin/


 以 bash 程序来执行:透过『 bash shell.sh 』或『 sh shell.sh 』来执行


编写shell


1. 第一行 #!/bin/bash 在宣告这个 script 使用的 shell 名称:


2. 程序内容的说明:
一般来说, 建议你一定要养成说明该script 的:1. 内容与功能; 2. 版本信息; 3. 作者与联络方式; 4. 建檔日期;5. 历史纪录 等等。这将有助于未来程序的改写与 debug


3. 主要环境变量的宣告:
建议务必要将一些重要的环境变量设定好,鸟哥个人认为, PATH 与 LANG (如果有使用到输出相关的信息时) 是当中最重要的! 如此一来,则可让我们这支程序在进行时,可以直接下达一些外部指令,而不必写绝对路径呢!比较方便!


4. 主要程序部分


5. 执行成果告知 (定义回传值)


13.1.3 编写shell script 良好习惯


建议你一定要养成良好的 script 撰写习惯,在每个 script 的文件头处记录好:
  script 的功能;
  script 的版本信息;
  script 的作者与联络方式;
  script 的版权宣告方式;
  script 的 History (历史纪录);
  script 内较特殊的指令,使用『绝对路径』的方式来下达;
  script 运作时需要的环境变量预先宣告与设定。


13.2 简单的shell script 练习


13.2.1 简单范例


13.2.2 script 的执行方式的区别(source,shscript,./script)


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


当使用前一小节提到的直接指令下达 (不论是绝对路径/相对路径还是 ${PATH} 内),或者是利用bash (或 sh) 来下达脚本时, 该 script 都会使用一个新的 bash 环境来执行脚本内的指令!也就是说,使用这种执行方式时, 其实 script 是在子程序的 bash 内执行的!
重点在于:『当子程序完成后,在子程序内的各项变量或动作将会结束而不会传回到父程序中』


范例
 [dmtsai@study bin]$  echo ${firstname} ${lastname}
<==确认了,这两个变量并不存在喔!
[dmtsai@study bin]$  sh showname.sh
Please input your first name: VBird <==这个名字是鸟哥自己输入的
Please input your last name: Tsai
Your full name is: VBird Tsai <==看吧!在 script 运作中,这两个变数有生效
[dmtsai@study bin]$  echo ${firstname} ${lastname}
<==事实上,这两个变量在父程序的 bash 中还是不存在的!


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


source 对 script 的执行方式会在父程序中执行的,因此各项动作都会在原本的 bash 内生效!这也是为啥你不注销系统而要让某些写入 ~/.bashrc 的设定生效时,需要使用『 source ~/.bashrc 』而不能使用『 bash ~/.bashrc 』是一样的


范例
[dmtsai@study bin]$  source showname.sh
Please input your first name: VBird
Please input your last name: Tsai
Your full name is: VBird Tsai
[dmtsai@study bin]$  echo  ${firstname} ${lastname}
VBird Tsai <==嘿嘿!有数据产生喔!


13.3 善用判断式


13.3.1利用test命令测试功能


当我要检测系统上面某些文件或者是相关的属性时,利用 test 这个指令


例子:检查 /dmtsai 是否存在时
>    test -e /dmtsai
执行结果并不会显示任何讯息,但最后我们可以透过 $? 或 && 及 || 来展现整个结果,
例如我
们在将上面的例子改写成这样:
>  test - - e /dmtsai && echo "exist" || echo "Not exist" 


其他参数:


1. 关于某个档名的『文件类型』判断,如 test -e filename 表示存在否
-e :该『档名』是否存在?(常用)
-f: 该『档名』是否存在且为文件(file)?(常用)
-d :该『文件名』是否存在且为目录(directory)?(常用)
-b :该『档名』是否存在且为一个 block device 装置?
-c :该『档名』是否存在且为一个 character device 装置?
-S :该『档名』是否存在且为一个 Socket 文件?
-p :该『档名』是否存在且为一个 FIFO (pipe) 文件?
-L :该『档名』是否存在且为一个连结档?




2. 关于文件的权限侦测,如 test -r filename 表示可读否 (但 root 权限常有例外)
-r: 侦测该档名是否存在且具有『可读』的权限?
-w :侦测该档名是否存在且具有『可写』的权限?
-x :侦测该档名是否存在且具有『可执行』的权限?
-u: 侦测该文件名是否存在且具有『SUID』的属性?
-g: 侦测该文件名是否存在且具有『SGID』的属性?
-k :侦测该文件名是否存在且具有『Sticky bit』的属性?
-s :侦测该档名是否存在且为『非空白文件』?


3. 两个文件之间的比较,如: test file1 -nt file2
-nt: (newer than)判断 file1 是否比 file2 新
-ot: (older than)判断 file1 是否比 file2 旧
-ef:判断 file1 与 file2 是否为同一文件,可用在判断 hard link 的判定上。 主要意义在判定,两个文件是否均指向同一个 inode 哩!


4. 关于两个整数之间的判定,例如 test n1 -eq n2
-eq :两数值相等 (equal)
-ne :两数值不等 (not equal)
-gt :n1 大于 n2 (greater than)
-lt: n1 小于 n2 (less than)
-ge: n1 大于等于 n2 (greater than or equal)
-le :n1 小于等于 n2 (less than or equal)


5. 判定字符串的数据
test -z string :判定字符串是否为 0 ?若 string 为空字符串,则为 true
test -n string:判定字符串是否非为 0 ?若 string 为空字符串,则为 false。注: -n 亦可省略
test str1 == str2: 判定 str1 是否等于 str2 ,若相等,则回传 true
test str1 != str2: 判定 str1 是否不等于 str2 ,若相等,则回传 false


6. 多重条件判定,例如: test -r filename -a -x filename
-a:(and)两状况同时成立!例如 test -r file -a -x file,则 file 同时具有 r 与 x 权限时,才回传 true。
-o:(or)两状况任何一个成立!例如 test -r file -o -x file,则 file 具有 r 或 x 权限时,就可回传 true。
! :反相状态,如 test ! -x file ,当 file 不具有 x 时,回传 true


13.3.2 利用判断符号[ ]


中括号的使用方法与 test 几乎一模一样啊~ 只是中括号比较常用在条件判断式 if ..... then ..... fi 的情况中,
举例来说,如果我想要知道 ${HOME} 这个变量是否为空的,可以这样做:
>   [ - - z "${HOME}" ] ; echo $?


注意:
  在中括号 [] 内的每个组件都需要有空格键来分隔;
在中括号内的变数,最好都以双引号括号起来;
在中括号内的常数,最好都以单或双引号括号起来。


13.3.3 shell script的默认值变量($0 $1.....)


>/path/to/scriptname opt1 opt2 opt3 opt4
$0                                $1 $2     $3    $4
执行的脚本档名为 $0 这个变量,第一个接的参数就是 $1


除了这些数字的变量之外,我们还有一些较为特殊的变量可以在 script 内使用:


  $# :代表后接的参数『个数』,以上表为例这里显示为『 4 』;
  $@ :代表『 "$1" "$2" "$3" "$4" 』之意,每个变量是独立的(用双引号括起来);
  $* :代表『 "$1c$2c$3c$4" 』,其中 c 为分隔字符,默认为空格键, 所以本例中代表『 "$1 $2 $3 $4" 』之意。
那个 $@ 与 $* 基本上还是有所不同!不过,一般使用情况下可以直接记忆 $@ 即可!


范例


[dmtsai@study bin]$  vim how_paras.sh
#!/bin/bash
# Program:
#  Program shows the script name, parameters...
# History:
# 2015/07/16  VBird  First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "The script name is ==> ${0}"
echo "Total parameter number is ==> $#"
[ "$#" -lt 2 ] && echo "The number of parameter is less than 2. Stop here." && exit 0
echo "Your whole parameter is ==> '$@'"
echo "The 1st parameter ==> ${1}"
echo "The 2nd parameter ==> ${2}"
执行结果如下:
[dmtsai@study bin]$  sh how_paras.sh theone haha quot
The script name is ==> how_paras.sh <==檔名
Total parameter number is ==> 3 <==果然有三个参数
Your whole parameter is ==> 'theone haha quot' <==参数的内容全部
The 1st parameter ==> theone <==第一个参数
The 2nd parameter ==> haha <==第二个参数


shift :造成参数变量号码偏移
 shift 会移动变量,而且 shift 后面可以接数字,代表拿掉最前面的几个参数的意思。 


范例


[dmtsai@study bin]$  vim shift_paras.sh
#!/bin/bash
# Program:
#  Program shows the effect of shift function.
# History:
# 2009/02/17  VBird  First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "Total parameter number is ==> $#"
echo "Your whole parameter is ==> '$@'"
shift # 进行第一次『一个变量的 shift 』
echo "Total parameter number is ==> $#"
echo "Your whole parameter is ==> '$@'"
shift 3 # 进行第二次『三个变量的 shift 』
echo "Total parameter number is ==> $#"
echo "Your whole parameter is ==> '$@'"
这玩意的执行成果如下:
[dmtsai@study bin]$  sh shift_paras.sh one two three four five six <==给予六个参数
Total parameter number is ==> 6 <==最原始的参数变量情况
Your whole parameter is ==> 'one two three four five six'
Total parameter number is ==> 5 <==第一次偏移,看底下发现第一个 one 不见了
Your whole parameter is ==> 'two three four five six'
Total parameter number is ==> 2 <==第二次偏移掉三个,two three four 不见了
Your whole parameter is ==> 'five six'


13.4 条件判断式


13.4.1 利用if then


单层、简单条件判断式


if [ 条件判断式  ]; then
当条件判断式成立时,可以进行的指令工作内容;
fi <==将 if 反过来写,就成为 fi 啦!结束 if 之意


将多个条件写入一个中括号内的情况』之外, 我还可以有多个中括号来隔开!而括号与括号之间,则以 && 或 || 来隔开,他们的意义是:
  && 代表 AND ;
  || 代表 or ;
所以,在使用中括号的判断式中, && 及 || 就与指令下达的状态不同了。举例来说,
[ "${yn}" == "Y" -o "${yn}" == "y" ]
上式可替换为
[ "${yn}" == "Y" ] || [ "${yn}" == "y" ]


多重、复杂条件判断式
------------------------------
# 一个条件判断,分成功进行与失败进行 (else)
if [ 条件判断式  ]; then
当条件判断式成立时,可以进行的指令工作内容;
else
当条件判断式不成立时,可以进行的指令工作内容;
fi
------------------------------
如果考虑更复杂的情况,则可以使用这个语法:
------------------------------
# 多个条件判断 (if ... elif ... elif ... else) 分多种不同情况执行
if [ 条件判断式一  ]; then
当条件判断式一成立时,可以进行的指令工作内容;
elif [ 条件判断式二  ]; then
当条件判断式二成立时,可以进行的指令工作内容;
else
当条件判断式一与二均不成立时,可以进行的指令工作内容;
fi
------------------------------
你得要注意的是, elif 也是个判断式,因此出现 elif 后面都要接 then 来处理!但是else 已经是最后的没有成立的结果了, 所以 else 后面并没有 then 


13.4.2 利用case ..esac判断


语法如下:


-----------------
case $变量名称  in <==关键词为 case ,还有变数前有钱字号
"第一个变量内容")  <==每个变量内容建议用双引号括起来,关键词则为小括号 )
程序段
;; <==每个类别结尾使用两个连续的分号来处理!
"第二个变量内容") 
程序段
;;
*) <==最后一个变量内容都会用 * 来代表所有其他值
不包含第一个变量内容与第二个变量内容的其他程序执行段
exit 1
;;
esac <==最终的 case 结尾!『反过来写』思考一下!
-----------------


范例


[dmtsai@study bin]$  vim hello-3.sh
#!/bin/bash
# Program:
# Show "Hello" from $1.... by using case .... esac
# History:
# 2015/07/16  VBird  First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
case ${1} in
"hello")
echo "Hello, how are you ?"
;;
"")
echo "You MUST input parameters, ex> {${0} someword}"
;;
*) # 其实就相当于通配符,0~无穷多个任意字符之意!
echo "Usage ${0} {hello}"
;;
esac


 case $变量 in 变量的取值方式:
一般来说,使用『 case $变量 in 』这个语法中,当中的那个『 $变量 』大致有两种取值的方式:
  直接下达式:例如上面提到的,利用『 script.sh variable 』 的方式来直接给予 $1 这个变量的内容,这也是在 /etc/init.d 目录下大多数程序的设计方式。
  交互式:透过 read 这个指令来让用户输入变量的内容。


13.4.3 利用function功能


是什么


什么是『函数 (function)』功能啊?简单的说,其实, 函数可以在 shell script 当中做出一个类似自定义执行指令的东西,最大的功能是, 可以简化我们很多的程序代码。


function fname () {
程序段



范例


简单例子:
#!/bin/bash
# Program:
#  Use function to repeat information.
# History:
# 2015/07/17  VBird  First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
function printit(){
echo -n "Your choice is " # 加上 -n 可以不断行继续在同一行显示
}
echo "This program will print your selection !"
case $1 in
"one")
printit; echo ${1} | tr 'a-z' 'A-Z' # 将参数做大小写转换!
;;
"two")
printit; echo $1 | tr 'a-z' 'A-Z'
;;
"three")
printit; echo $1 | tr 'a-z' 'A-Z'
;;
*)
echo "Usage $0 {one|two|three}"
;;
esac
---------
以上面的例子来说,鸟哥做了一个函数名称为 printit ,所以,当我在后续的程序段里面, 只要执行printit 的话,就表示我的 shell script 要去执行『 function printit .... 』 里面的那几个程序段落




13.5 循环(loop)


13.5.1 while do done ,until do done(不定循环)


语法


当 condition 条件成立时,就进行循环,直到condition 的条件不成立才停止:
while [ condition  ] 
do 
程序段落
done 


与 while 相反,它说的是『当 condition 条件成立时,就终止循环, 否则就持续进行循环的程序段。
until [ condition ] ]
do
程序段落
done


范例


[dmtsai@study bin]$  vim yes_to_stop.sh
#!/bin/bash
# Program:
#  Repeat question until user input correct answer.
# History:
# 2015/07/17  VBird  First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
while [ "${yn}" != "yes" -a "${yn}" != "YES" ]
do
read -p "Please input yes/YES to stop this program: " yn
done
echo "OK! you input the correct answer."


[dmtsai@study bin]$  vim  yes_to_stop- - 2.sh
#!/bin/bash
# Program:
#  Repeat question until user input correct answer.
# History:
# 2015/07/17  VBird  First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
until [ "${yn}" == "yes" -o "${yn}" == "YES" ]
do
read -p "Please input yes/YES to stop this program: " yn
done
echo "OK! you input the correct answer."


13.5.2 for ..do ...done (固定循环)


for var  in con1 con2 con3 ...
do
程序段
done
以上面的例子来说,这个 $var 的变量内容在循环工作时:
1. 第一次循环时, $var 的内容为 con1 ;
2. 第二次循环时, $var 的内容为 con2 ;
3. 第三次循环时, $var 的内容为 con3 ;
4. ....


范例


简单例子:
#!/bin/bash
# Program:
# Using for .... loop to print 3 animals
# History:
# 2015/07/17  VBird  First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
for animal in dog cat elephant
do
echo "There are ${animal}s.... "
done


让我们想象另外一种状况,由于系统上面的各种账号都是写在 /etc/passwd 内的第一个字段,你能不能透过管线命令的 cut 捉出单纯的账号名称后,以 id 分别检查使用者的标识符与特殊参数呢?由于不同的 Linux 系统上面的账号都不一样!此时实际去捉 /etc/passwd 并使用循环处理,就是一个可行的方案了!程序可以如下:


[dmtsai@study bin]$  vim userid.sh
#!/bin/bash
# Program
# Use id, finger command to check system account's information.
# History
# 2015/07/17 VBird first release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
users=$(cut -d ':' -f1 /etc/passwd) # 撷取账号名称
for username in ${users} # 开始循环进行!
do
id ${username}
done


13.5.3 for do done的数值处理


语法如下:
for (( 初始值; ; 限制值; ; 执行步阶  ))
do
程序段
done


这种语法适合于数值方式的运算当中,在 for 后面的括号内的三串内容意义为:
  初始值:某个变量在循环当中的起始值,直接以类似 i=1 设定好;
  限制值:当变量的值在这个限制值的范围内,就继续进行循环。例如 i<=100;
  执行步阶:每作一次循环时,变量的变化量。例如 i=i+1。
值得注意的是,在『执行步阶』的设定上,如果每次增加 1 ,则可以使用类似『i++』的方式,亦即是 i 每次循环都会增加一的意思。


#!/bin/bash
# Program:
# Try do calculate 1+2+....+${your_input}
# History:
# 2015/07/17  VBird  First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
read -p "Please input a number, I will count for 1+2+...+your_input: " nu
s=0
for (( i=1; i<=${nu}; i=i+1 ))
do
s=$((${s}+${i}))
done
echo "The result of '1+2+3+...+${nu}' is ==> ${s}"


13.6 shell script的追踪与调试


语法


我们就直接以 bash 的相关参数来进行判断
语法:
>sh [-nvx] scripts.sh
选项与参数:
-n :不要执行 script,仅查询语法的问题;
-v :再执行 sccript 前,先将 scripts 的内容输出到屏幕上;
-x :将使用到的 script 内容显示到屏幕上,这是很有用的参数!


范例


范例一:测试 dir_perm.sh 有无语法的问题?
[dmtsai@study ~]$  sh -n dir_perm.sh
# 若语法没有问题,则不会显示任何信息!


范例二:将 show_animal.sh 的执行过程全部列出来~
[dmtsai@study ~]$  sh -x show_animal.sh
+ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/root/bin
+ export PATH
+ for animal in dog cat elephant
+ echo 'There are dogs.... '
There are dogs....
+ for animal in dog cat elephant
+ echo 'There are cats.... '
There are cats....
+ for animal in dog cat elephant
+ echo 'There are elephants.... '
There are elephants....

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值