什么是shell脚本?
脚本最初是从演艺界中的一个词,指表演戏剧、拍摄电影等所依据的底本或者书稿的底本。后来,IT行业引用了这个词,我们现在所说的脚本(script)是使用一种特定的描述性语言,依据一定的格式编写的可执行文件,又称作宏或批处理文件。脚本通常可以由应用程序临时调用并执行。shell脚本就是脚本语言中的一种。它不是一门正式的编程语言,因为跑在Linux的shell中,所以就称它为shell脚本。说白了,shell脚本就是一些命令的集合。我们可以利用shell脚本将很多个操作指令记录到一个文档中,然后调用文档中的指令,一次完成所有指令的操作。例如在hadoop中我们启动集群常用到start-all.sh来启动集群中的多个服务。这个start-all.sh就是一个shell脚本,我们今天就来看看start-all是怎样启动集群的。
学习之前,我们要指定两项规定:
- 我们的自定义脚本建议都放在/user/local/sbin/目录下边,这样做是为了以后更好管理文档,也让以后接管你工作的同事知道脚本放在哪里。
- 我们的shell脚本文件要将其设置为.sh文件。这样做的目的是为了让别人一看就知道这是一个shell脚本。
下面我们就来写一个Hello World的shell脚本:
- 创建一个文件:
vi shellTest.sh
vi shellTest.sh
- 编写shell脚本
#!/bin/bashecho "hello World" #这是一条输出语句
#!/bin/bash
echo "hello World" #这是一条输出语句
输出结果为:
hello World
脚本解析:
- 第一行中的#是一个约定标记,他告诉系统这个脚本需要什么解释器来执行,后面的/bin/bash就是指明了解释器的具体位置。
- 第二行echo命令用于向标准输出文件(Standard Output,stdout,一般就是指显示器)输出文本。在.sh文件中使用命令与在终端直接输入命令的效果是一样的。
- 第二行的#及其后面的内容是注释。(shell中除了开头的#!不是注释,其他的#都是注释)
后续文档参考命令符解释:
$ #$加变量名代表引用这个变量$0 #Shell本身的文件名$1~$n #添加到Shell的各参数值。$1是第1参数、$2是第2参数…。$# #是传给脚本的参数个数. #如果使用” . "执行,则程序继承当前shell中的环境变量,同时,若在程序中改变了当前shell中的环境变量,则当前shell中该环境变量的值也会改变另外一个区别点在于, “ ./ "只能用于拥有执行权限的文件, 而 ” . " 则可以暂时提升执行权限if *** fi #判断语句类似于Java中if{}-f #判断给定的是不是文件case *** in *** esac #类似java中的caseshift #命令表示参数向左偏移,后面可跟数字(数字大小在参数个数范围内)
下面我们再来写一个稍微复杂一点的脚本:
#!/usr/bin/env bashbin=`dirname "${BASH_SOURCE-$0}"` bin=`cd "$bin"; pwd` #bin中存储当前脚本所在路径DEFAULT_LIBEXEC_DIR="$bin"/../libexec#获取HADOOP_LIBEXEC_DIR路径,如果系统环境变量已经配置则用环境变量中路径,否则用获取到的默认的路径HADOOP_LIBEXEC_DIR=${HADOOP_LIBEXEC_DIR:-$DEFAULT_LIBEXEC_DIR} . $HADOOP_LIBEXEC_DIR/hadoop-config.sh# start hdfs daemons if hdfs is presentif [ -f "${HADOOP_HDFS_HOME}"/sbin/start-dfs.sh ]; then "${HADOOP_HDFS_HOME}"/sbin/start-dfs.sh --config $HADOOP_CONF_DIRfi# start yarn daemons if yarn is presentif [ -f "${HADOOP_YARN_HOME}"/sbin/start-yarn.sh ]; then "${HADOOP_YARN_HOME}"/sbin/start-yarn.sh --config $HADOOP_CONF_DIRfi
#!/usr/bin/env bash
bin=`dirname "${BASH_SOURCE-$0}"`
bin=`cd "$bin"; pwd` #bin中存储当前脚本所在路径
DEFAULT_LIBEXEC_DIR="$bin"/../libexec
#获取HADOOP_LIBEXEC_DIR路径,如果系统环境变量已经配置则用环境变量中路径,否则用获取到的默认的路径
HADOOP_LIBEXEC_DIR=${HADOOP_LIBEXEC_DIR:-$DEFAULT_LIBEXEC_DIR}
. $HADOOP_LIBEXEC_DIR/hadoop-config.sh
# start hdfs daemons if hdfs is present
if [ -f "${HADOOP_HDFS_HOME}"/sbin/start-dfs.sh ]; then
"${HADOOP_HDFS_HOME}"/sbin/start-dfs.sh --config $HADOOP_CONF_DIR
fi
# start yarn daemons if yarn is present
if [ -f "${HADOOP_YARN_HOME}"/sbin/start-yarn.sh ]; then
"${HADOOP_YARN_HOME}"/sbin/start-yarn.sh --config $HADOOP_CONF_DIR
fi
脚本解析:
第一行是指定我们的解释器,并指明了解释器的具体位置。
第二行相当于我们将文件目录的字符串赋值给了bin变量,${BASH_SOURCE-$0}这一串是为了获取当前执行的脚本文件的全路径,于是我们的bin就得到了脚本文件的全路径。
第三行我们首先执行了cd "$bin"($加上变量名可以用来引用这个变量),进入了脚本的目录环境,然后又输出了当前目录,并把这个目录给了bin变量。执行完这行之后我们就进入了脚本文件目录环境。并且bin值成为了当前脚本的所在文件。
第四行中我们获取绝对路径以备后用:
${HADOOP_HOME}/libexec
第五行为HADOOP_LIBEXEC_DIR变量三元赋值。如果HADOOP_LIBEXEC_DIR为空或者环境变量没有配置,就赋值默认的绝对路径,为下一步执行该目录下面的脚本做准备。
第六行执行下面脚本。为后面执行启动各节点和启动YARN做预处理:
${HADOOP_HOME}/libexec/hadoop-config.sh
${HADOOP_HOME}/libexec/hadoop-config.sh
另外要提的是:
如果使用" ./ " 执行,可以理解为程序运行在一个全新的shell中不继承当前shell的环境变量的值同时若在程序中改变了当前shell中的环境变量(不使用export)则当前shell的环境变量值不变。如果使用" . "执行,则程序继承当前shell中的环境变量同时,若在程序中改变了当前shell中的环境变量则当前shell中该环境变量的值也会改变另外一个区别点在于" ./ "只能用于拥有执行权限的文件而 " . "则可以暂时提升执行权限。
第七行执行启动脚本,if是判断语句(类似于Java)fi是if的字母倒序,表示if判断语句结束;-f 做判断是不是文件类型。这段脚本的意图是:
如果${HADOOP_HDFS_HOME}/sbin/start-dfs.sh为文件,则联合--config参数及其后面的参数值执行start-dfs.sh。start-dfs.sh后面做详细分析。
第八行执行YRAN调度服务,这段脚本的意图是:
如果${HADOOP_HDFS_HOME}/sbin/start-yarn.sh为文件,则联合--config参数及其后面的参数值执行start-yarn.sh。start-yarn.sh后面做详细分析。
这就是完整的start-all.sh的shell执行流程。其中涉及到的start-yarn.sh和start-dfs.sh脚本,两个脚本十分类似,我们再看看start-dfs.sh脚本。
#!/usr/bin/env bash# Start hadoop dfs daemons. # Optinally upgrade or rollback dfs state. # Run this on master node. usage="Usage: start-dfs.sh [-upgrade|-rollback]" #定义usage变量,即start-dfs.sh的使用说明,在后面的内容可以看到,当参数输入错误时,会打印该消息 bin=`dirname "$0"` bin=`cd "$bin"; pwd` . "$bin"/hadoop-config.sh # get arguments if [ $# -ge 1 ]; then nameStartOpt=$1 shift case $nameStartOpt in (-upgrade) ;; (-rollback) dataStartOpt=$nameStartOpt ;; (*) echo $usage exit 1 ;; esac fi # start dfs daemons # start namenode after datanodes, to minimize time namenode is up w/o data # note: datanodes will log connection errors until namenode starts "$bin"/hadoop-daemon.sh --config $HADOOP_CONF_DIR start namenode $nameStartOpt "$bin"/hadoop-daemons.sh --config $HADOOP_CONF_DIR start datanode $dataStartOpt "$bin"/hadoop-daemons.sh --config $HADOOP_CONF_DIR --hosts masters start secondarynamenode
前面的代码我们上面已经解释过,不再赘述,要提的是,$0是指shell本身的文件名,其他的类似。后面的完整的判断语句的意思是:如果参数值的个数大于等于1的时候(代表还有服务需要开启),如果参数是upgrade,则设置nameStartOpt变量以备后用;如果是rollback,则设置dataStartOpt变量以备后用。 后面的脚本分别则是用来启动NameNode、DataNode、Master。