1.1 为什么要学Shell编程?
Shell脚本语言是实现Linux/UNIX系统管理及自动化运维所必备的重要工具,Linux/UNIX系统的底层及基础应用软件的核心大都涉及Shell脚本的内容。每一个合格的Linux系统管理员或运维工程师,都需要能够熟练的编写Shell脚本语言,并能够阅读系统及各类软件附带的Shell脚本内容。只有这样才能提升运维人员的工作效率,适应日益复杂的工作环境,减少不必要的重复工作。
1.2 学好Shell编程所需的Linux基础
1)熟练使用vim编辑器。
2)熟练掌握120个左右Linux运维基础命令。
3)熟练掌握Linux正则表达式及三剑客。
1.3 如何才能学好Shell编程
1)掌握Shell脚本基本语法的方法。
2)掌握Shell脚本的各种常见语法。
3)从简单做起,简单判断,简单循环。
4)锻炼编程思维,形成自己的脚本开发风格,要多试着去解决企业实际问题。切记拿来主义。
1.3 学好Shell编程最关键的条件
1)多练习,多敲。学好Shell编程,没有捷径可走。
2. Shell脚本初步入门
2.1 什么是Shell
Shell就是一个命令解释器,它的作用就是解释执行用户输入的命令及程序等,用户输入的每一条命令,Shell就解释执行一条。这种从键盘一输入命令,就可以立即得到回应的对话方式,称为交互式的方式。下图就是一个命令解释器Shell在操作系统中所处位置的基本图解。
2.2 什么是Shell脚本
当命令或程序语句不在命令行执行,而是通过一个程序文件来执行时,该程序就被称为Shell脚本,也就是说,Shell脚本就是由命令、变量、流程控制语句等组成的。
初学者如果想要快速的掌握Shell脚本的编写方法,最有效的思路就是采用电子游戏中过关的方式,如下:
第一关:必须是root才能执行脚本,否则给出友好提示并终止脚本运行。
第二关:切换目录的时候,必须要成功切换才行,否则给出友好提示并终止脚本运行。
第三关:清理日志文件时,若清理成功,则给出正确提示。
第四关:通关成功或通关失败,分别给出相应的提示。
2.3 Shell脚本在Linux运维工作中的地位
Shell脚本语言很适合用于处理纯文本类型的数据,在Linux系统中几乎所有的配置文件、日志文件及绝大多数的启动文件都是纯文本类型的文件。那么可想而知,Shell脚本在Linux运维中的地位有多重要了,就如下图所示一样:
2.4 脚本语言的种类
2.4.1 Shell脚本语言的种类
Shell脚本语言是弱类型语言(无须定义变量的类型即可使用),在Unix\Linux中主要有两大类Shell:一类是Bourne shell,另一类是C shell。
1. Bourne shell
Bourne shell 又包括了Bourne shell(sh)、Korn shell(ksh)、Bourne Again shell(bash)三种类型。
2. C shell
C shell 又包括csh、tcsh两种类型。
可以通过以下命令查看CentOS7系统的Shell支持情况。
[root@web01 ~]# cat /etc/shells /bin/sh/bin/bash/usr/bin/sh/usr/bin/bash
2.4.2 其他常用的脚本语言种类
1. PHP语言
PHP是网页程序语言,同时也是脚本语言。它是一款更专注于Web页面开发(前端展示)的语言。
2. Perl语言
Perl脚本语言比Shell脚本语言更加强大,它的语法灵活、复杂,在实现不同的功能时可以用多种不同的方式,缺点是不易读,团队协作困难。
3. Python语言
Python不但可以用于脚本程序开发,还可以实现Web页面程序开发,甚至还可以实现软件的开发、游戏开发、大数据开发、移动端开发。
2.4.3 Shell脚本语言的优势
Shell脚本语言的优势在于处理偏操作系统底层的业务。对于一些常见的系统脚本,使用Shell脚本开发会更简单、更快速,例如:让软件一键自动化安装、优化,监控报警脚本,软件启动脚本,日志分析脚本等。
2.5 常用操作系统默认的Shell
Linux系统下默认的Shell是Bourne Again shell(bash);Solaris和FreeBSD下默认的是Bourne(sh);AIX下默认的是Korn Shell(ksh)。
通过以下两种方法可以查看CentOS Linux系统默认的Shell。
方法1:
[root@web01 ~]# echo $SHELL/bin/bash
方法2:
[root@web01 ~]# grep"^root"/etc/passwd|awk -F":"'{print $NF}'/bin/bash
2.6 Shell脚本的建立和执行
2.6.1 Shell脚本的建立
1. 脚本开头(第一行)
一个规范的Shell脚本在第一行会指出由哪个程序(解释器)来执行脚本中的内容,如下:
#!/bin/bash或#!/bin/sh
期中,开头的#号又称为幻数符,在执行bash脚本的时候,内核会根据“#!”后的解释器来确定用哪个程序解释这个脚本中的内容。
2. bash与sh的区别
sh为bash的软连接,大多数情况下,脚本开头使用“#!/bin/bash”和“#!/bin/sh”是没有区别的,但更加规范的写法是在脚本的开头使用“#!/bin/bash”。
[root@m01 ~]# ll /bin/shlrwxrwxrwx.1root root4Apr2819:23/bin/sh -> bash
Linux中常用脚本开头的写法如下:
#!/bin/sh#!/bin/bash#!/usr/bin/awk#!/bin/sed#!/usr/bin/tcl#!/usr/bin/expect #<===expect语言解释器#!/usr/bin/perl #<===perl语言解释器#!/usr/bin/env python #<===python语言解释器
如果在脚本开头的第一行不指定解释器,那么就要用对应的解释器来执行脚本,这样才能确保脚本正确执行。例如:
Shell脚本,用bash或sh test.sh来执行。
Python脚本,用python test.py来执行。
expect脚本,用expect test.exp来执行。
3. 脚本注释
在Shell脚本中,#号后面的内容表示注释,用来对脚本进行注释说明,注释部分不会被当做程序来执行,仅仅是给开发者和使用者看的。注释的时候尽量不要用中文,脚本内容中也不要有中文。
2.6.2 Sehll脚本的执行
当Shell脚本运行时,它会先查找系统环境变量ENV,该变量指定了环境变量文件(加载顺序通常是/etc/profile、~/.bash_profile、~/.bash、/etc/bashrc等),在加载了上述环境变量文件后,Shell就开始执行Shell脚本中的内容。
Shell脚本执行的顺序是从上至下、从左至右依次执行每一行的命令及语句的,即执行完了一个命令后再执行下一个,如果在Shell脚本中遇到子脚本(即脚本嵌套)时,就会先执行子脚本的内容,完成后再返回父脚本继续执行父脚本内后续的命令及语句。
通常情况下,在执行Shell脚本时,会向系统内核请求启动一个新的进程,以便在该进程中执行脚本的命令及子Shell脚本,基本流程如下所示:
特别注意:设置Linux的crond任务时,最好能在定时任务脚本中重新定义系统环境变量,否则,一些系统环境变量将不会被加载。
Shell脚本执行的方式
1)bash script-name 或 sh script-name:这是当脚本文件本身没有可执行权限时常用的方法。
2)cat script-name|sh
3)sh
4)./script-name:在当前路径下执行脚本文件,需要脚本文件本身有可执行权限。
5)source script-name 或 . script-name:不需要脚本文件有可执行权限。
范例2-4:创建模拟脚本test.sh。
第1种方法执行:
[root@m01 ~]# echo 'echo I am oldboy' > test.sh
[root@m01 ~]# cat test.shecho I am oldbo
y[root@m01 ~]# sh test.shI am oldboy
[root@m01 ~]# bash test.shI am oldboy
第2种方法执行:
[root@m01 ~]# ll test.sh -rw-r--r--1root root17May2719:50test.sh
[root@m01 ~]# ./test.sh -bash:./test.sh:Permission denied#<===由于没有可执行权限,所以执行失败,甚至连tab补全都不可以
[root@m01 ~]# . test.sh #<===虽然没有可执行权限,但是可以用.或source来执行I am oldboy[root@m01 ~]# source test.sh #<===.或source命令功能相同,都是读入脚本并执行脚本I am oldboy
给test.sh添加可执行权限,命令如下:
[root@m01 ~]# chmod u+x test.sh
[root@m01 ~]# ./test.sh I am oldboy
第3种方法执行:
[root@m01 ~]# echo 'userdir=`pwd`' > testsource.sh
[root@m01 ~]# cat testsource.shuserdir=`pwd`
[root@m01 ~]# sh testsource.sh #<===执行后没有内容显示
[root@m01 ~]# echo $userdir #<===echo 变量也没有内容显示,解决方法如下
[root@m01 ~]# source testsource.sh
[root@m01 ~]# echo $userdir/root
上述结果总结:
通过source或“.”加载执行过的脚本,由于是在当前Shell中执行脚本,因此脚本运行结束后,脚本中的变量(包括函数)值在当前Shell依然存在,而sh和bash执行脚本都会启动新的子Shell执行,执行完后回退到父Shell。 因此,变量(包括函数)值等无法保留。在进行Shell脚本开发时,如果脚本中有引用或执行其他脚本的内容或配置文件需求时,最好用“.”或source先加载该脚本或配置文件,处理后,再将它们加载到脚本下面,就可以调用source加载的脚本及配置文件中的变量函数等内容了。
第4种方法执行:
[root@web01 /server/scripts]# ll oldboy.sh-rw-r--r-- 1 root root 246 May 31 15:24 oldboy.sh
[root@web01 /server/scripts]# cat oldboy.sh
#!/bin/bash
##############################################################
# File Name: test.sh
# Version: V1.0
# Author: oldboy
# Organization: www.oldboyedu.com
##############################################################
echo'I am oldboy.'
[root@web01 /server/scripts]# sh < oldboy.sh <===不推荐使用这种方法I am oldboy.
[root@web01 /server/scripts]# cat oldboy.sh|bashI am oldboy.
已知如下命令返回结果,请问echo $used的返回结果为()?
[root@web01 /server/scripts]# cat test.sh
#!/bin/bash
##############################################################
# File Name: test.sh
# Version: V1.0
# Author: oldboy
# Organization: www.oldboyedu.com
##############################################################
user=`whoami`
[root@web01 /server/scripts]
# sh test.sh
[root@web01 /server/scripts]# echo $user
参考答案如下:
a)当前用户
b)root
c)没有内容输出
通过上述面试题得出如下结论:
子Shell脚本会直接继承父Shell脚本的变量、函数等,反之则不能。
如果希望父Shell也能够继承子Shell的脚本变量、函数等,就要使用source或.在父Shell脚本中事先加载子Shell脚本,也就是先把结果弄出来。
2.6.3 Shell脚本开发的基本习惯推荐
1)Shell脚本的第一行是指定脚本解释器,通常为:
#!/bin/bash或#!/bin/sh ~ ~
2)Shell脚本的开头会加版本、版权等信息:
#!/bin/bash
##############################################################
# File Name: 1.sh
# Version: V1.0
# Author: oldboy
# Organization: www.oldboyedu.com
##############################################################
3)在Shell脚本中尽量不要用中文(不限于注释)。
4)Shell脚本的命名应以.sh为扩展名。
[root@web01 /server/scripts]#ls1.sh222.sh9_1.sh9_2_2.sh9_2_3.sh9_2.sholdboy.sholdgirl.shplush_color.shtest.sh
5)Shell脚本应存放在固定的路径下。
[root@web01 /server/scripts]#
以下则是Shell脚本代码书写的良好习惯。
1)成对的符号应尽量一次性写出来,然后退格在符号里增加内容,以防止遗漏。这些成对的符号包括:
{ }[ ]' '` `" "
2)中括号([ ])两端至少要有一个空格。
[ $a -eq0]
3)对于流程控制语句,应一次性将格式写完,再添加内容。
比如,一次性完成if语句的格式,应为:
#!/bin/bash
##############################################################
# File Name: 5.sh
# Version: V1.0
# Author: oldboy
# Organization: www.oldboyedu.com
##############################################################
if
then
fi
一次性完成for循环语句的格式,应为:
#!/bin/bash
###############################################################
# File Name: 5.sh
# Version: V1.0
# Author: oldboy
# Organization: www.oldboyedu.com
##############################################################
for
do
done
4)通过缩进让代码更易读,比如:
#!/bin/bash
###############################################################
#File Name: 5.sh
# Version: V1.0
# Author: oldboy
# Organization: www.oldboyedu.com
##############################################################
if[ 条件内容 ]then内容fi
5)对于常规变量的字符串定义变量应加双引号,并且等号前后不能有空格,需要强引用的(指所见即所得的字符引用),则用单引号(’’),如果是命令的引用,则用反引号(``)。例如:
#!/bin/bash
###############################################################
# File Name: 5.sh
# Version: V1.0
# Author: oldboy
# Organization: www.oldboyedu.com
##############################################################
OLDBOY="test.txt"
a=`who`
6)脚本中的单引号、双引号及反引号必须为英文状态下的符号,其实所有的Linux字符及符号都应该是英文状态下的符号,这点需要特别注意