shell简介

本文最后更新于:July 24, 2019 am

简单介绍一下shell的概念、分类以及脚本的几种执行方式。

1、什么是shell

Shell的中文翻译名是贝壳的意思,也就是相当于下图所示,是一个命令解释器,它的作用是解释执行用户输入的命令及程序等,用户每输入一条命令,Shell就解释执行一条。这种从键盘一输入命令,就可以立即得到回应的对话方式,称为交互的方式。

Shell存在于操作系统的最外层,负责与用户直接对话,把用户的输入解释给操作系统,并处理各种各样的操作系统的输出结果,然后输出到屏幕返回给用户。输入系统用户名和密码并登录到Linux后的所有操作都是由Shell解释与执行的。

2、什么是shell脚本

当命令或程序语句不在命令行下执行,而是通过一个程序文件来执行时,该程序就被称为Shell脚本。

如果在Shell脚本里内置了很多条命令、语句及循环控制,然后将这些命令一次性执行完毕,这种通过文件执行脚本的方式称为非交互的方式。Shel脚本类似于DOS或windows系统下的批处理程序(扩展名一般为“*.bat”)。用户可以在Shell脚本中敲入一系列的命令及命令语句组合。这些命令、变量和流程控制语句等有机地结合起来,就形成了一个功能强大的Shell脚本。

比如下面的这个脚本,就是用来清空log日志中的messages文件

1
2
3
4
5
6
7
#! /bin/bash

cd /var/log

cat /dev/null > messages

echo "Logs clean up done"

上面的这个脚本就是最简单的由命令直接堆砌而成的脚本文件,需要注意的是,脚本在执行的时候可能会有权限限制,一般需要使用chmod +x给脚本加执行权限,还可能需要切换到root用户或者使用sudo

实际上我们可以看到上面的脚本其实一条命令就能解决

1
cat /dev/null > /var/log/messages && echo "Logs clean up done"

接下来我们对上面的脚本进行改进,加入执行用户和切换目录的判断以及根据执行结果返回不同的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#! /bin/bash
# A shell script that use to clean the system's logs.
CLEAN_DIR=/var/log

# $UID=0 means the user must be root
ROOT_UID=0

# This script need the root to execute
# We have to judge whether the user is root or not
# If not, echo warning and then exit
if ["$UID" -ne "$ROOT_UID"]
then
echo " Must be root to execute this script "
exit 1
if

# If change directory fail, give the tip and then exit
cd $CLEAN_DIR || {
echo "Cannot change to necessary directory"
exit 1
}

# Begin to clean the log
# Echo the tips whether it success or not
cat /dev/null > messages && {
echo "Logs cleaned up"
exit 0
# eixt 0 means success and exit 1 means fail
}
echo "Logs cleaned up fail"
exit 1

3、shell脚本语言的种类

Shell脚本语言是弱类型语言(无须定义变量的类型即可使用),在Unix/Linux中主要有两大类Shell:一类是Bourne shell,另一类是Cshell

3.1 Bourne shell

Bourne shell又包括Bourne shell(sh)、Korn shell(ksh)、Bourne Again Shell(bash)三种类型。

  • Bourne shell(sh)由AT&T的Steve Bourne开发,是标准的UNIX Shell,很多UNIX系统都配有sh。
  • Korn shell(ksh)由David Korn开发,是Bourne shell(sh)的超集合,并且添加了csh引入的新功能,是目前很多UNIX系统标准配置的Shell,这些系统上的/bin/sh往往是指向/bin/ksh的符号链接。
  • Bourne Again Shell(bash)由GNU项目组开发,主要目标是与POSIX标准保持一致,同时兼顾对sh的兼容,bash从csh和ksh借鉴了很多功能,是各种Linux发行版默认配置的Shell,Linux系统上的/bin/sh往往是指向/bin/bash的符号链接。

尽管如此,bash和sh还是有很多的不同之处:一方面,bash扩展了一些命令和参数;另一方面,bash并不完全和sh兼容,它们有些行为并不一致,但在大多数企业运维的情况下区别不大,特殊场景可以使用bash替代sh。

在Linux中,一般使用的shell是bash,所以说bash其实是shell的一个子集,当然也还有其他的一些流行的shell工具。如果我们想要Linux中的一个用户无法登录使用shell交互,最简单的方法就是在/etc/passwd文件中设置该用户的默认shell为/sbin/nologin即可。

3.2 Cshell

Cshel又包括csh、tcsh两种类型。

  • csh由Berkeley大学开发,随BSDUNIX发布,它的流程控制语句很像C语言,支持很多Bourne shell所不支持的功能,例如:作业控制、别名、系统算术、命令历史、命令行编辑等。
  • tcsh是csh的增强版,加入了命令补全等功能,在FreeBSD、Mac OSX等系统上替代了csh。

3.3 shell目前形势

以上介绍的这些Shell中,较为通用的是标准的Bourne shell(sh)和Cshell(csh)。其中Bourne shell(sh)已经被Bourne Again shell(bash)所取代。

Linux系统中的主流Shell是bash,bash是由Bourne Shell(sh)发展而来的,同时bash还包含了csh和ksh的特色,但大多数脚本都可以不加修改地在sh上运行,如果使用了sh后发现结果和预期有差异,那么可以尝试用bash替代sh。

Shel脚本语言的优势在于处理偏操作系统底层的业务,例如:Linux系统内部的很多应用(有的是应用的一部分)都是使用Shell脚本语言开发的,因为有1000多个Linux系统命令为它做支撑,特别是Linux正则表达式及三剑客grep、awk、sed等命令。

对于一些常见的系统脚本,使用Shell开发会更简单、更快速,例如:让软件一键自动化安装、优化,监控报警脚本,软件启动脚本,日志分析脚本等,虽然PHP/Python语言也能够做到这些,但是,考虑到掌握难度、开发效率、开发习惯等因素,它们可能就不如Shell脚本语言流行及有优势了。对于一些常规的业务应用,使用Shell更符合Linux运维简单、易用、高效的三大基本原则。

在常用的操作系统中,Linux下默认的Shell是Bourne Again shell(bash);Solaris和FreeBSD下默认的是Bourne shell(sh);AIX下默认的是Korn Shell(ksh)。

4、脚本的执行

Shell脚本的执行通常可以采用以下几种方式。

4.1 bash script-namesh script-name

这是当脚本文件本身没有可执行权限(即文件权限属性x位为-号)时常使用的方法,或者脚本文件开头没有指定解释器时需要使用的方法。

4.2 path/script-name./script-name

指在当前路径下执行脚本(脚本需要有执行权限),需要将脚本文件的权限先改为可执行(即文件权限属性加x位),具体方法为chmod +x script-name。然后通过脚本绝对路径或相对路径就可以直接执行脚本了。

4.3 source script-name.script-name

这种方法通常是使用source.(点号)读入或加载指定的Shel脚本文件(如san.sh),然后,依次执行指定的Shell脚本文件san.sh中的所有语句。这些语句将在当前父Shell脚本father.sh进程中运行(其他几种模式都会启动新的进程执行子脚本)。

因此,使用source.可以将san.sh自身脚本中的变量值或函数等的返回值传递到当前父Shel脚本father.sh中使用。这是它和其他几种方法最大的区别,也是值得读者特别注意的地方。

这里说起来可能不好理解,我们用一个简单脚本来操作一下。

这里我们可以看到,这个脚本的主要操作是使用pwd命令打印出当前的目录

然后我们分别用上面的方法1和这里的方法3来执行这个脚本

这里我们可以看到,使用方法1的时候,并没有打印出当前目录userdir,或者说,当前目录userdir这个变量为空。再使用方法3来执行该脚本,我们可以看到这时候的变量userdir就不为空了。

结论:通过source.加载执行过的脚本,由于是在当前Shell中执行脚本,因此在脚本结束之后,脚本中的变量(包括函数)值在当前Shell中依然存在;而sh和bash执行脚本都会启动新的子Shell执行,执行完后退回到父Shell,因此,变量(包括函数)值等无法保留。

实在不懂就看下小七画的这个灵魂流程图

在进行Shell脚本开发时,如果脚本中有引用或执行其他脚本的内容或配置文件的需求时,最好用.”或source先加载该脚本或配置文件,处理完之后,再将它们加载到脚本的下面,就可以调用source加载的脚本及配置文件中的变量及函数等内容了。

source.命令的功能是:在当前Shell中执行source.加载并执行的相关脚本文件中的命令及语句,而不是产生一个子Shell来执行文件中的命令。

source.的实际功能相同,都是读入脚本并执行。

注意.和后面的脚本名之间要有空格。

如果大家学过PHP开发就知道,source.相当于include的功能。HTTP服务软件Apache、Nginx等配置文件里都支持这样的用法。

4.4 sh<script-namecat scripts-namelsh

同样适用于bash,不过这种用法不是很常见。一般都是用来实现拼接字符串等骚操作的,留个坑,以后有机会再来试一下。