前些天,花了一些时间整理linux shell编程。看了一些简单的语法后,觉得还是不够深刻。想着,是不是可以直接查看一些开源软件的启动脚本快速提升shell编写能力。
下面来看看tomcat bin目录下的 start.sh 文件到底在做什么呢?

源码解析

#!/bin/sh

# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements.  See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# -----------------------------------------------------------------------------
# Start Script for the CATALINA Server
# -----------------------------------------------------------------------------

# Better OS/400 detection: see Bugzilla 31132
os400=false
case "`uname`" in
OS400*) os400=true;;
esac

# resolve links - $0 may be a softlink
PRG="$0"

while [ -h "$PRG" ] ; do
  ls=`ls -ld "$PRG"`
  link=expr "$ls" : '.*-> \(.*\)$'`
  if expr "$link" : '/.*' > /dev/null; then
    PRG="$link"
  else
    PRG=`dirname "$PRG"`/"$link"
  fi
done

上述为第一段代码块:默认设置变量os400=false,接着判断系统是不是属于OS400,如果uname的结果为OS400开头的字符串,那么会匹配上 OS400*,并且 赋值os400=true
第二段代码块:设定执行的脚本名称为PRG,比如执行start.sh脚本的时候,如果执行的命令是/tomcat/bin/start.sh,那么PRG=/tomcat/bin/start.sh;如果执行命令为:./bin/start.sh,那么PRG=bin/start.sh
第三段代码块: 通过一个while循环判断"$PRG"的路径是不是一个链接,如果是一个链接,那么找到其链接源,直到"$PRG"不是一个链接时,循环结束;代码中while循环条件-h是判断"$PRG" 是否为链接,如果是链接进入循环,否则跳出循环;循环内ls -ld "$PRG"是获取"$PRG"脚本路径的目录。通过正则expr来匹配'.*-> \(.*\)$',若匹配上,则是链接,否则为非链接。匹配结果重定向至/dev/null,这个文件可以理解为一个大窟窿,多大的文件都能吃掉。若匹配上,匹配结果重新赋值给PRGPRG="$link"。若没有匹配上,那么赋值执行文件的完整路径给PRG。正常的链接文件,会有下面的特殊格式:

[root@test-cdh bin]# ll /usr/bin/python3
lrwxrwxrwx 1 root root 38 10月 25 2017 /usr/bin/python3 -> /usr/local/python3/install/bin/python3
PRGDIR=`dirname "$PRG"`
EXECUTABLE=catalina.sh

# Check that target executable exists
if $os400; then
  # -x will Only work on the os400 if the files are:
  # 1. owned by the user
  # 2. owned by the PRIMARY group of the user
  # this will not work if the user belongs in secondary groups
  eval
else
  if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then
    echo "Cannot find $PRGDIR/$EXECUTABLE"
    echo "The file is absent or does not have execute permission"
    echo "This file is needed to run this program"
    exit 1
  fi
fi

exec "$PRGDIR"/"$EXECUTABLE" start "$@"

上述代码块,第一段代码块:获取本脚本的真实执行目录赋值给PRGDIR,定义变量EXECUTABLE。当$os400为真时,执行eval命令;为假时,判断"$PRGDIR"/"$EXECUTABLE"文件是否为可执行/存在的文件,若不是可执行/存在文件,给出echo提示;若是存在的可执行文件,那么执行命令:"$PRGDIR"/"$EXECUTABLE" start "$@"。比如我执行的start.sh的脚本的完整路径(非链接)为:/tomcat/bin/start.sh,并且命令后传参为arg1 arg2 arg3,那么上述命令实际为:/tomcat/bin/start.sh catalina.sh arg1 arg2 arg3$@$*都是表示列表中的所有参数。
注:eval后的接一个命令参数,如果后面的参数是一个变量,那么会将变量转换成其真实命令。如下:

[root@host2 ~]# f="head -n 10 install.log"
[root@host2 ~]# echo $f
head -n 10 install.log
[root@host2 ~]# eval $f
Installing libgcc-4.4.7-4.el6.x86_64
warning: libgcc-4.4.7-4.el6.x86_64: Header V3 RSA/SHA1 Signature, key ID c105b9de: NOKEY
Installing fontpackages-filesystem-1.41-1.1.el6.noarch
Installing m17n-db-1.5.5-1.1.el6.noarch
Installing setup-2.8.14-20.el6_4.1.noarch
Installing xml-common-0.6.3-32.el6.noarch
Installing filesystem-2.4.30-3.el6.x86_64
Installing kernel-headers-2.6.32-431.el6.x86_64
Installing iso-codes-3.16-2.el6.noarch
Installing xkeyboard-config-2.6-6.el6.noarch

上述echo $f能将变量替换成真实命令,而eval $f先将变量翻译成命令head -n 10 install.log,而后执行该命令结果。eval对其后的命令进行了二次扫描。
上述最后一行代码exec "$PRGDIR"/"$EXECUTABLE" start "$@"exec命令也有讲究。exec执行其后的命令时,会关闭当前进程,建立一个新进程。此进程比较特殊,此进程的pid与被关闭的进程pid是同一个pi,所以看上去并不会产生新的进程。同时,若exec后的命令执行后,则退出当前进程。如下:

[root@host2 ~]# cat demo.sh
#!/bin/bash

f="head -n 10 install.log"
exec echo "execute illegal command:"
exec ls_nocommand
exec eval $f
[root@host2 ~]# ./demo.sh
execute illegal command:

exec执行命令成功后,后续的命令并未执行

[root@host2 ~]# cat demo.sh
#!/bin/bash

f="head -n 10 install.log"
echo "execute illegal command:"
exec ls_nocommand
exec eval $f
[root@host2 ~]# ./demo.sh
execute illegal command:
./demo.sh: line 5: exec: ls_nocommand: not found

exec执行命令成功后,后续的命令并未执行

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐