第一章、shell入门基础
1.1 为什么学习和使用Shell编程
对于一个合格的系统管理员来说,学习和掌握Shell编程是非常重要的。通过编程,可以在很大程度上简化日常的维护工作,使得管理员从简单的重复劳动中解脱出来。
Shell程序的特点:
1、简单易学。
2、解释性语言,不需要编译即可执行
1.2 什么是Shell
在学习Shell编程之前,必须弄清楚什么是Shell。为了能够使读者在学习具体的Shell编程之前对Shell有个基本的了解,本节将对Shell进行概括性的介绍,包括Shell的起源、功能和分类。
1.2.1 shell起源
-
1964年,美国AT&T公司的贝尔实验室、麻省理工学院及美国通用电气公司共同参与开始研发一套可以安装在大型主机上的多用户、多任务的操作系统,该操作系统的名称为Multics。
-
1970年,丹尼斯•里奇和汤普逊启动了另外一个新的多用户、多任务的操作系统的项目,他们把这个项目称之为UNICS。
-
1973年,使用C语言重写编写了Unix。通过这次编写,使得Unix得以移植到其他的小型机上面。
-
1979年,第一个重要的标准UNIX Shell在Unix的第7版中推出,并以作者史蒂夫•伯恩(Stephen Bourne)的名字命名,叫做Bourne Shell,简称为sh。
-
20世纪70年代末,C Shell作为2BSD UNIX的一部分发布,简称csh。
-
之后又出现了许多其他的Shell程序,主要包括Tenex C Shell(tcsh)、Korn Shell(ksh)以及GNU Bourne-Again shell(bash)。
1.2.2 什么是shell
Shell又称命令解释器,它能识别用户输入的各种命令,并传递给操作系统。它的作用类似于Windows操作系统中的命令行,但是,Shell的功能远比命令行强大的多。在UNIX或者localhost中,Shell既是用户交互的界面,也是控制系统的脚本语言。
-
真正能够控制计算机硬件(CPU、内存、显示器等)的只有操作系统内核(Kernel),图形界面和命令行只是架设在用户和内核之间的一座桥梁,由于安全、复杂、繁琐等原因,用户不能直接接触内核(也没有必要),需要另外再开发一个程序,让用户直接使用这个程序;该程序的作用就是接收用户的操作(点击图标、输入命令),并进行简单的处理,然后再传递给内核,这样用户就能间接地使用操作系统内核
-
用户界面和命令行就是这个另外开发的程序,就是这层“代理”。在Linux下,这个命令行程序叫做 Shell,Shell 是一个应用程序,它连接了用户和 Linux 内核,让用户能够更加高效、安全、低成本地使用 Linux 内核,这就是 Shell 的本质。
-
Shell 本身并不是内核的一部分,它只是站在内核的基础上编写的一个应用程序,它和 QQ、迅雷、Firefox 等其它软件没有什么区别。然而 Shell 也有着它的特殊性,就是开机立马启动,并呈现在用户面前;用户通过 Shell 来使用 Linux,不启动 Shell 的话,用户就没办法使用 Linux。
1.3 shell的分类
-
Bourne Shell:标识为sh,该Shell由Steve Bourne在贝尔实验室时编写。在许多Unix系统中,该Shell是root用户的默认的Shell。
-
Bourne-Again Shell:标识为bash,该Shell由Brian Fox在1987年编写,是绝大多数localhost发行版的默认的Shell。
-
Korn Shell:标识为ksh,该Shell由贝尔实验室的David Korn在二十世纪八十年代早期编写。它完全向上兼容 Bourne Shell 并包含了C Shell 的很多特性。
-
C Shell:标识为csh,该Shell由Bill Joy在BSD系统上开发。由于其语法类似于C语言,因此称为C Shell。
查看当前系统支持的shell
[root@server ~]# cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
查看当前系统默认shell
[root@server ~]# echo $SHELL
/bin/bash
1.4 程序设计语言shell特性
Shell不仅仅是充当用户与UNIX或者localhost交互界面的角色,还可以作为一种程序设计语言来使用。通过Shell编程,可以实现许多非常实用的功能,提高系统管理的自动化水平。本节将介绍作为程序设计语言的Shell的一些特性。
Shell 也是一种脚本语言
-
任何代码最终都要被“翻译”成二进制的形式才能在计算机中执行。
-
有的编程语言,如 C/C++、Pascal、Go语言、汇编等,必须在程序运行之前将所有代码都翻译成二进制形式,也就是生成可执行文件,用户拿到的是最终生成的可执行文件,看不到源码。这个过程叫做编译(Compile),这样的编程语言叫做编译型语言,完成编译过程的软件叫做编译器(Compiler)。
-
有的编程语言,如 Shell、JavaScript、Python、PHP等,需要一边执行一边翻译,不会生成任何可执行文件,用户必须拿到源码才能运行程序。程序运行后会即时翻译,翻译完一部分执行一部分,不用等到所有代码都翻译完。这个过程叫做解释,这样的编程语言叫做解释型语言或者脚本语言(Script),完成解释过程的软件叫做解释器。
-
编译型语言的优点是执行速度快、对硬件要求低、保密性好,适合开发操作系统、大型应用程序、数据库等。
-
脚本语言的优点是使用灵活、部署容易、跨平台性好,非常适合 Web 开发以及小工具的制作
-
Shell 就是一种脚本语言,我们编写完源码后不用编译,直接运行源码即可。
shell脚本
如果有一系列经常需要使用的命令,把它存储在一个文件里,shell可以读取这个文件并顺序执行其中的命令,这样的文件就叫shell脚本。shell脚本按行解释。
(1)顺序执行:逐条执行(自上而下执行)
(2)选择执行:代码有一个分支:条件满足时才会执行
两个以上分支:只执行其中一个满足条件的分支
(3)循环执行:代码片断(循环体)要执行0,1或多个来回
shell脚本语言是实现linux/Unix系统管理及自动化运维所必备的重要工具,linux/unix系统的底层
及基础应用软件的核心大多都涉及shell脚本的内容。
1.4.1编程语言和脚本语言区别
脚本语言含义: shell PHP Python JavaScript Perl Ruby
脚本语言是为了缩短传统的编写-编译-链接-运行过程而创建的计算机编程语言。和传统的Java,c++等编程语言不同,脚本语言不需要编译器,它需要的是解释器。也就是说,脚本语言是解释执行的,例如有一款专用的软件,而这款专用的软件上面执行特定的操作才能和我的软件进行交互,而这个操作的集合就是解释器,进行操作就是脚本语言,当我们将这个软件操作组织起来完成一个任务的时候,这就是在运用脚本语言进行编程。
编程(译)语言含义:java c c++
编程语言是用来定义计算机程学的形式语言,是一种将程序员所定义的代码,编译即翻译成计算机所认识的二进制代码的工具,所以编译语言需要编译器。
编译器和解释器最大的不同,就是一个面向的是计算机,一个面向的是某个特定的软件或者计算机某一个部分。(移植性较差,但运行效率高)
比较
- 脚本语言不需要编译器因而省去了编译的过程减少了开发的时间,而编程语言需要编译所以时间更长一点
- 脚本语言是一种动态语言,也就是说可以实时的更改代码,而不需要将程序停止下来,这是一种高级特性。而Java等编程语言是静态的语言,一旦编译完成并且运行就不能更改代码,除非将程序停止下来。
- 脚本语言非常容易学习,但是不够全面缺乏系统性而且语法较为散漫。而高级编程语言虽然相对难学,但是规则强可以编程出简洁美观的代码,并且可读性也相对较强。
- 一般来说脚本语言通用性较差,但是可以通过专门的应用来调整。
- 随着技术的发展,其实脚本语言变得越来越强,和编程语言的界限也比较模糊,比如Python,可以将它视为编程语言了,因为它很强大。
shell脚本与php/perl/python语言的区别和优势?
shell脚本的优势在于处理操作系统系统底层的业务(linux系统内部应用的都是shell脚本来完成)因为有大量的linux系统命令为他做支撑。2000多个个命令都是shell脚本编程的有力支撑,特别是grep、awk、sed等。例如:一键安装软件、优化监控报警脚本,常规的业务应用,shell开发更简单快捷,符合韵味的简单、易用高效原则。
PHP、python优势在于开发运维工具以及web界面的管理工具,web业务的开发等。处理一键软件安装、优化、报警脚本。常规业务的应用等php/python也是能够做到的。但是开发效率和shell比差很多,代码量多更复杂。
1.5 如何学好shell
学好shell编程基础知识
-
熟练使用vi(vim)编辑器 注释:3,6 s/^/# /
-
熟练掌握Linux基本命令
-
熟练掌握文本三剑客工具(grep、sed、awk)
-
熟悉常用服务器部署、优化、日志及排错
如何学好shell编程
- 掌握Shell脚本基本语法
- 形成自己的脚本开发风格
- 从简单做起,简单判断,简单循环
- 多模仿,多参考资料练习,多思考
- 学会分析问题,逐渐形成编程思维
- 编程变量名字要规范,采用驼峰语法表示
- 不要拿来主义,特别是新手
1.6 Shell脚本的基本元素
对于一个基本的Shell程序来说,应该拥有以下基本元素: .sh /script/user.sh
- 第1行的“#!/bin/bash”。(注:脚本用什么来解释 she-Bang 魔数)
- 注释:说明某些代码的功能。
- 可执行语句:实现程序的功能。
16.1 Shell脚本中的注释和风格
-
作用:通过在代码中增加注释可以提高程序的可读性
-
传统的Shell只支持单行注释,其表示方法是一个井号“#”,从该符号开始一直到行尾都属于注释的内容,如:
#comment1
#comment2
#comment3
...
-
多行注释:使用冒号“:”配合here document,语法如下:
:<<'xxxx'
comment1
comment2
comment3
……
xxxx
xxxx 可以是字符或数字,单引号可以不加,但以防出现莫名其妙的意外发生,比如发生字符扩展,命令替换(常用EOF)
1.7 Shell脚本编写规范
脚本开头
-
开头指定脚本解释器:#!/bin/sh 或 #!/bin/bash
-
其他行#表示注释
-
程序段开头需要加版本版权等信息,如:
# Date:创建日期
# Author:作者
# Mail:联系方式
# Function:功能
# Version:版本
-
脚本自动增加注释版权信息
如何快速生成脚本开头的版本版权注释信息
[root@server ~]# vim ~/.vimrc # 新建配置文件(只在root目录下生效)
autocmd BufNewFile *.py,*.cc,*.sh,*.java exec ":call SetTitle()"
func SetTitle()if expand("%:e") == 'sh'call setline(1,"#!/bin/bash")call setline(2,"##############################################################")call setline(3, "# File Name: ".expand("%"))call setline(4, "# Version: V1.0")call setline(5, "# Author: Andy_Sun")call setline(6, "# Email: Andy_Sun@163.com")call setline(7, "# Organization: http://www.cnblogs.com/Andy_Sun/")call setline(8, "# Created Time : ".strftime("%F %T"))call setline(9, "# Description:")call setline(10,"##############################################################")call setline(11, "")endif
endfuncvim /etc/vimrc(主配置,所有生效)
脚本中尽量不用中文注释
-
别吝啬添加注释,必要的注释方便自己别人理解脚本逻辑和功能
-
尽量用英文注释,防止本机或切换系统环境后中文乱码的困扰
-
单行注释,可以放在代码行的尾部或代码行的上部
-
多行注释,用于注解复杂的功能说明,可以放在程序体中,也可以放在代码块的开始部分 代码修改时,对修改的内容
多使用内部命令
-
无论碰到哪种情况,请尽量考虑使用内部命令而不是外部命令
-
内部命令执行的效率高,性能好
-
内部命令可以在性能方面为你节省很多。
[root@HAHA ~]# echo $[3+4]
7
[root@HAHA ~]# expr 3 + 5
8
[root@HAHA ~]# time echo $[4+4]
8
real 0m0.000s
user 0m0.000s
sys 0m0.000s
[root@HAHA ~]# time expr 4 + 4
8
real 0m0.002s
user 0m0.000s
sys 0m0.002s
没有必要使用cat命令
vim % co $ grep root /etc/passwd cat /etc/passwd | grep root
-
这是我们经常在论坛里讨论的话题之一。没有必要使用cat命令指的是在有些时候,我们会发现根本没有必要使用cat命令。使用了多余的cat命令会让你的代码看起来很丑陋,而且还会带来性能上的问题
-
例如:以下两条命令的结果一样
eg:cat /etc/passwd | grep guru
使用以下方式即可
eg:grep guru /etc/passwd
代码缩进
-
shell没有强制要求,但建议缩进,这样可以提高阅读性,程序更有层次感,
-
python严格缩进标识代码块
-
例:编写99乘法表
#!/bin/bashfor((i=1; i<10; i++)) doecho -ne "$i\t" doneecho for((i=1;i<70;i++)) doecho -n "=" doneechofor((i=1; i<10; i++)) dofor((j=1;j<=i;j++))doecho -en "$i*$j=$[i*j]\t"doneecho done [root@server ~]# bash 99.sh # 运行 # 结果: 1 2 3 4 5 6 7 8 9 ===================================================================== 1*1=1 2*1=2 2*2=4 3*1=3 3*2=6 3*3=9 4*1=4 4*2=8 4*3=12 4*4=16 5*1=5 5*2=10 5*3=15 5*4=20 5*5=25 6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36 7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49 8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64 9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81
优化:
#!/bin/bash i=1 while [ $i -le 10 ] doif [ $i -le 9 ]thenusername=user0$ielseusername=user$ifi! id $username &>/dev/null && {useradd $usernameecho $username | passwd --stdin $username &>/dev/null}let i++ done
仔细阅读出错信息
-
程序员常犯的一个错误是:当我们敲入的命令报错后,我们中的大多数人只是对错误信息一瞥而过,而不会去认真的读一读,很多时候,错误信息里就包含了解决办法
-
有时候我们修改了某个错误并再次运行后,系统依旧会报错。然后我们再次修改,但系统再次报错。这可能会持续很长时间。但实际上,旧的错误可能已经被纠正,只是由于出现了其它一些新错误才导致系统再次报错。而我们依旧在怀疑为什么修改好的代码依然不能正常运行。
-
因此,请你养成仔细阅读错误信息的习惯。
脚本以.sh为扩展名
-
shell脚本文件名应见名知义 ,扩展名位sh,如:backup_mysql.sh
eg:script-name.sh
1.8 shell脚本执行
方法1
-
使用sh或bash命令执行脚本,不需要执行权限(建议使用),脚本中可以不指定解释器
[root@server ~]# vim test.sh
#!/bin/bash
echo "china"
[root@server ~]# bash test.sh
china
[root@server ~]# sh test.sh
china
-
可以使用bash -n 脚本名 ,进行语法检测,且不执行脚本
-
可以使用bash -x 脚本名 ,进行脚本执行跟踪,逐条语句的跟踪执行
方法2
-
切换到脚本所在目录使用./执行脚本,需要执行权限
[root@server ~]# ./test.sh
-bash: ./test.sh: 权限不够
[root@server ~]# chmod +x test.sh
[root@server ~]# ./test.sh
方法3
-
绝对路径执行脚本,需要执行权限
[root@server ~]# vim /t1.sh
#!/bin/bash
echo "china"
[root@server ~]# /t1.sh
-bash: /t1.sh: 权限不够
[root@server ~]# chmod +x /t1.sh
[root@server ~]# /t1.sh
china
方法4
-
使用点(.)或者source 执行脚本,不需要执行权限(一般不用 vim /etc/init.d/network )
[root@server ~]# source /t1.sh
china
[root@server ~]# . test.sh
china
注意:
-
法1、2、3都是启动一个子shell,在子shell中执行此脚本,脚本中设置的变量在脚本执行完毕后不会保存
-
法4 都是在当前shell进程中执行此脚本,而不是重新启动一个shell 在子shell进程中执行此脚本,并且脚本中设置的变量在脚本执行完毕后会保存下来。
[root@server ~]# vim temp.sh
#!/bin/bash
cd /etc[root@server ~]# chmod +x temp.sh
[root@server ~]# bash temp.sh # 目录不变
[root@server ~]# ./temp.sh # 目录不变
[root@server ~]# /root/temp.sh # 目录不变[root@server ~]# source temp.sh
[root@server etc]# cd ~
[root@server ~]# . temp.sh
[root@server etc]# cd ~
[root@server ~]#
区别:
- 方法一:可以在脚本中不指定解释器,脚本可以没有执行权限
- 方法二和方法三脚本需要有执行权限,./script_name.sh 或/path/script_name.sh bash a.sh
- 方法四:当前shell执行,方法1-3开启子shell
自定义脚本
写脚本:show_info.sh
输出: 当前系统时间是:xxxx
输出: 当前用户: $USER
vim tab键的缩进为4个空格
"add by school1024.com`
set ts=4
set softtabstop=4
set shiftwidth=4
set expandtab
set autoindentts是tabstop的缩写,设TAB宽度为4个空格。
softtabstop 表示在编辑模式的时候按退格键的时候退回缩进的长度,当使用 expandtab 时特别有用。
shiftwidth 表示每一级缩进的长度,一般设置成跟 softtabstop 一样。 expandtab表示缩进用空格
来表示,noexpandtab 则是用制表符表示一个缩进。 autoindent自动缩进五行放在~/.vimr里,与注释放在一起
1.8.1 脚本检测
- bash -n 脚本语法检测,不执行脚本文件
- bash -x 跟踪脚本执行
- shellcheck 脚本文件检测脚本 .....