stopAll()
{
# We separate the stop commands into a function so we are able to use the trap command in Unix (calling a function) to stop these services
if [ "X${ALREADY_STOPPED}" != "X" ] ; then
exit
fi
# STOP DERBY (only if we started it)
if [ "${DERBY_FLAG}" = "true" ] ; then
echo "Stopping Derby server..."
${WL_HOME}/common/derby/bin/stopNetworkServer.sh >"${DOMAIN_HOME}/derbyShutdown.log" 2>&1
echo "Derby server stopped."
fi
ALREADY_STOPPED="true"
}
这段代码是一个Shell脚本函数,名为stopAll
,其目的是停止一系列服务,特别是针对一个使用Apache Derby数据库的WebLogic环境。下面是逐行解释:
-
stopAll()
:定义了一个名为stopAll
的函数。 -
# We separate the stop commands...
:这是一个注释行,说明将停止命令放在一个函数里是为了能够方便地在Unix环境下使用trap
命令来调用这个函数以停止服务。trap
命令用于捕获和处理信号,比如当收到中断信号时自动调用此函数清理资源。 -
if [ "X${ALREADY_STOPPED}" != "X" ] ; then exit ; fi
:这行代码检查环境变量ALREADY_STOPPED
是否已设置(非空)。如果已设置,则直接退出函数,避免重复执行停止操作。 -
# STOP DERBY (only if we started it)
:另一个注释,说明接下来的代码块用于停止Derby数据库服务器,但仅当之前有启动过它时才执行。 -
if [ "${DERBY_FLAG}" = "true" ] ; then
:检查环境变量DERBY_FLAG
是否为true
,这标志着之前是否成功启动了Derby服务器。 -
echo "Stopping Derby server..."
:打印一条消息,表明正在停止Derby服务器。 -
${WL_HOME}/common/derby/bin/stopNetworkServer.sh >"${DOMAIN_HOME}/derbyShutdown.log" 2>&1
:执行停止Derby网络服务器的脚本,并将标准输出和标准错误都重定向到${DOMAIN_HOME}/derbyShutdown.log
文件中。这意味着停止过程的任何消息或错误都将被记录到这个日志文件中。 -
echo "Derby server stopped."
:打印消息,表明Derby服务器已经停止。 -
ALREADY_STOPPED="true"
:无论Derby服务器是否真的启动过并被停止,最后都会设置环境变量ALREADY_STOPPED
为true
,标记这个停止流程已经被执行过,防止之后的重复执行。
整体而言,这个函数设计得比较周到,既考虑了服务停止的实际操作,也考虑了状态标记和日志记录,以及通过条件判断避免不必要的操作,体现了良好的脚本编写实践。
其中的
if [ "X${ALREADY_STOPPED}" != "X" ] ; then
解释如下
这段代码是Shell脚本中的条件语句,使用的是测试命令test
(通过[ ... ]
语法糖来表示)和条件结构if ... then ... fi
。它的作用是检查环境变量ALREADY_STOPPED
是否为空字符串。具体分析如下:
-
if [ "X${ALREADY_STOPPED}" != "X" ]
:X
在这里作为一个前缀,主要是为了避免当$ALREADY_STOPPED
变量为空时,直接比较"" != ""
可能导致的逻辑问题,因为在某些Shell版本中直接比较两个空字符串可能不按预期工作。加上前缀后,即使是空变量也会比较为"X" != "X"
,这样就能准确判断变量是否为空了。${ALREADY_STOPPED}
是引用环境变量ALREADY_STOPPED
的值。!=
是一个不等于运算符,用于比较两边的字符串是否不相等。- 整个条件表达式的意思是:如果
$ALREADY_STOPPED
变量的值不为空字符串,则条件为真。
-
then exit
: 如果上面的条件为真(即$ALREADY_STOPPED
不是空字符串),则执行exit
命令。exit
命令会使脚本或当前shell会话退出。 -
fi
: 结束if
条件语句的关键词,与if
配对使用,表示条件判断块结束。
综上所述,这段代码的含义是:如果环境变量ALREADY_STOPPED
已被设置(即不为空),那么脚本将立即退出,不再执行后续的命令。这是一种常见的脚本保护措施,用于防止重复执行某些只需要执行一次的操作。
umask 027
mypwd="$(pwd)"
SCRIPT_PATH="$0"
case ${SCRIPT_PATH} in /*) SCRIPTPATH=`dirname "${SCRIPT_PATH}"`;; *) SCRIPTPATH=`dirname "${mypwd}/${SCRIPT_PATH}"`;; esac
这两段代码分别涉及到了Linux权限管理和Shell脚本中的路径处理。
第一部分: umask 027
umask 027
是一个Linux命令,用于设置文件和目录的默认权限掩码。当你在Linux系统中创建新文件或目录时,系统的默认权限会受到umask
值的影响。
umask
是一个用于确定新创建文件和目录权限的命令。027
是一个八进制数,表示权限掩码。它由三位数字组成,分别对应文件或目录的属主(owner)、属组(group)、其他人(others)的权限位。0
(第一位通常忽略,因为它对应于特殊权限,如setuid, setgid, sticky bits,这里没有设置特殊权限)2
(第二位,转换为二进制为010,意味着去掉写权限)表示属组权限将去除写权限。7
(第三位,转换为二进制为111)表示其他人权限将完全去除(读、写、执行权限全去)。
所以,当你设置了umask 027
后,新创建的文件默认权限会从最大权限(通常是666对于文件,777对于目录)减去umask值,变为:
- 对于文件:666 - 027 = 640(-rw-r-----),即属主有读写权限,属组只有读权限,其他人没有任何权限。
- 对于目录:777 - 027 = 750(drwxr-x—),属主有读写执行权限,属组只有读执行权限,其他人没有任何权限。
第二部分: Shell脚本中的路径处理
下面的Shell脚本代码段是为了确定当前脚本文件的绝对路径。
mypwd="$(pwd)" # 获取当前工作目录的绝对路径并赋值给mypwd变量
SCRIPT_PATH="$0" # "$0" 是Shell脚本中预定义的变量,表示脚本本身的名称(包括路径)
case ${SCRIPT_PATH} in
/*) # 如果SCRIPT_PATH是以斜杠开头,即已经是绝对路径
SCRIPTPATH=`dirname "${SCRIPT_PATH}"` # 使用dirname获取脚本所在目录路径
;;
*) # 否则,SCRIPT_PATH是相对路径
SCRIPTPATH=`dirname "${mypwd}/${SCRIPT_PATH}"` # 将当前目录与脚本相对路径组合,并获取其所在目录路径
;;
esac
这段脚本的目的是确保无论脚本是如何被调用的(无论是通过绝对路径还是相对路径),都能正确地获取到脚本所在的目录路径,并将其存储在SCRIPTPATH
变量中。这对于脚本内部需要访问其同级目录下的其他文件或资源时非常有用。
其中的dirname是啥含义
在Shell脚本中,dirname
是一个命令,用于从给定的文件或目录路径中提取出目录部分。换句话说,它会移除路径中的文件名部分,留下路径中的目录部分。
在你提供的脚本片段中:
case ${SCRIPT_PATH} in
/*) SCRIPTPATH=`dirname "${SCRIPT_PATH}"` ;;
*) SCRIPTPATH=`dirname "${mypwd}/${SCRIPT_PATH}"` ;;
esac
- 当
$SCRIPT_PATH
以斜杠(/
)开头,表示它是一个绝对路径,这时直接使用dirname "${SCRIPT_PATH}"
就可以获取到该路径的目录部分。 - 若
$SCRIPT_PATH
不是以斜杠开头,说明它是一个相对路径。这时候,脚本通过拼接当前工作目录(由mypwd=$(pwd)
获得)和相对路径$SCRIPT_PATH
来构造一个完整的路径,再使用dirname
命令从中提取出目录部分。
所以,不论是哪种情况,最终SCRIPTPATH
变量都会被赋值为包含脚本所在目录的路径,而不管原始的$SCRIPT_PATH
是绝对路径还是相对路径。
# Call setDomainEnv here.
DOMAIN_HOME="/home/weblogic/weblogic12214/Oracle/Middleware/Oracle_Home/user_projects/domains/base_domain"
. ${DOMAIN_HOME}/bin/setDomainEnv.sh $*
这行Shell脚本命令有两个主要部分,我们逐一解析:
-
变量定义:
DOMAIN_HOME="/home/weblogic/weblogic12214/Oracle/Middleware/Oracle_Home/user_projects/domains/base_domain"
这行定义了一个环境变量
DOMAIN_HOME
,并赋值为一个具体的路径。这个路径指向的是WebLogic Server的一个域目录,通常包含着启动和管理WebLogic Server所需的各种脚本和配置文件。 -
执行脚本并传递参数:
. ${DOMAIN_HOME}/bin/setDomainEnv.sh $*
.
(点号)或者source
命令用于在当前Shell环境中执行后面指定的脚本文件,而不是启动一个新的子Shell。这意味着通过.
执行的脚本设置的任何环境变量或别名等都会影响到当前Shell会话,非常适合用来设置环境变量或初始化环境。${DOMAIN_HOME}
引用了前面定义的环境变量,构成完整路径/home/weblogic/weblogic12214/Oracle/Middleware/Oracle_Home/user_projects/domains/base_domain/bin/setDomainEnv.sh
,这是WebLogic Server用来设置或更新当前Shell环境以匹配指定域环境的脚本。$*
是一个特殊变量,代表传递给当前脚本的所有参数作为一个单字符串。当这个变量在双引号中使用时(虽然在这个例子中没有直接用双引号包围,但理解其含义依然重要),每个参数会被视为单独的单词并保持原有的空格分隔。在这里,通过$*
将所有接收到的参数原封不动地传递给setDomainEnv.sh
脚本,使得该脚本可以根据需要处理这些参数,比如根据不同的参数设置不同的Java选项或其他环境变量。
综上,这行脚本的目的是设置WebLogic域的环境,确保之后执行的命令(如启动WebLogic服务器)能够在正确的环境中运行,同时允许外部调用时传递额外参数给环境设置脚本以进行定制化配置。
trap 'stopAll' 1 2 3 15
在Shell脚本中,trap
命令用于捕获并处理信号。信号是Unix/Linux系统中进程间通信的一种方式,用于通知进程发生了某种事件。上述命令的含义如下:
-
trap 'command-list' signals
是基本的trap命令格式,其中command-list
是要执行的一系列命令,signals
是希望捕获的信号列表。 -
在你的例子中,
trap 'stopAll' 1 2 3 15
表示当接收到信号1、2、3或15时,执行命令stopAll
。
具体到信号:
- 1 (SIGHUP):挂起信号,通常在终端挂断时发送给与之相关的进程。这对于长时间运行的服务特别重要,因为它可以用来优雅地重启服务或执行清理工作。
- 2 (SIGINT):中断信号,一般由用户按下Ctrl+C时发送,用于请求进程中断当前操作并退出。
- 3 (SIGQUIT):退出信号,与Ctrl+\ 组合键相关联,它会产生一个核心转储(core dump)并退出进程,常用于调试。
- 15 (SIGTERM):终止信号,是最常用的请求进程终止的信号。当通过
kill
命令不带任何信号参数时,默认发送的就是这个信号,用于正常关闭进程。
因此,trap 'stopAll' 1 2 3 15
设置了一个陷阱,使得当接收到上述任何一个信号时,脚本会执行stopAll
函数。这通常用于确保在脚本或其控制的进程收到终止请求时,能够执行必要的清理工作,比如停止后台服务、释放资源等,以保证系统状态的整洁和资源的合理释放。
shutDownStatus=$?
在Shell脚本中,$?
是一个非常重要的特殊变量,它存储了上一个命令或脚本退出时的状态码(exit status)。状态码是一个整数值,通常用来表示命令执行的成功与否:
- 如果命令执行成功,它通常返回0。
- 如果命令执行失败,它会返回一个非零值,这个值可以用来标识失败的具体原因。
因此,shutDownStatus=$?
这行代码的作用是将上一个命令执行后的退出状态码赋值给变量 shutDownStatus
。这在脚本编程中非常有用,因为它允许脚本根据前一个命令的成功或失败来决定下一步的操作。例如,你可以在之后的脚本中检查 shutDownStatus
的值,如果它是0,说明关闭操作成功;如果不是0,则可以采取相应的错误处理措施,比如记录错误日志、发送报警信息或尝试重新执行关闭操作等。
echo "Calling ${TMP_UPDATE_SCRIPT}"
(cd /tmp;${TMP_UPDATE_SCRIPT})
ustatus=$?
这段脚本执行了以下操作:
-
打印消息:
echo "Calling ${TMP_UPDATE_SCRIPT}"
:首先,脚本会打印一条消息到标准输出(通常是终端),消息内容是正在调用的脚本名称,这里用${TMP_UPDATE_SCRIPT}
变量表示。
-
执行脚本并切换目录:
(cd /tmp; ${TMP_UPDATE_SCRIPT})
:这一部分在括号中执行,意味着它在一个子Shell中运行。首先通过cd /tmp
命令将子Shell的工作目录切换到/tmp
目录下,然后紧接着执行${TMP_UPDATE_SCRIPT}
,即运行之前设定的更新脚本。这样做的好处是,即便脚本内部有路径依赖或生成临时文件等操作,都不会影响到当前脚本的工作目录。
-
捕获并存储执行状态:
ustatus=$?
:执行完上面的命令序列后,立即用$?
捕获并存储上一个命令(即${TMP_UPDATE_SCRIPT}
)的退出状态码到变量ustatus
中。这一步很关键,因为它允许脚本后续根据ustatus
的值来判断TMP_UPDATE_SCRIPT
是否执行成功,从而做出不同的响应或处理逻辑。
综上,这段脚本设计得很紧凑,它不仅展示了如何在特定目录下执行另一个脚本,并且还能跟踪该脚本的执行结果,为后续决策提供了依据。
umask 027
EXIT=exit
OS="$(uname -sr)"
这段代码是Shell脚本中的一部分,它包含了几个命令和变量赋值操作,下面是对每一部分的解释:
-
umask 027
:
这是一个Unix/Linux系统命令,用于设置默认的文件权限掩码。当用户创建新文件或目录时,系统会根据umask
值来确定文件或目录的初始权限。umask
使用八进制数来表示权限位,其中每一位对应用户(u)、同组用户(g)、其他用户(o)的权限。数字027转换为权限表示为:0
对应特殊权限位,这里没有设置特殊权限。2
对应权限位g(同组用户),表示去掉写权限(因为2代表没有写权限,4+2=6,而权限6包含读和写,去掉写就是读)。7
对应权限位o(其他用户),表示去掉所有权限(读、写、执行权限全无,因为7是最大的权限,减去自身意味着无权限)。
因此,
umask 027
意味着新创建的文件默认权限为640(-rw-r-----),目录则为750(drwxr-x—)。这意味着文件所有者有读写权限,同组用户只有读权限,而其他用户没有任何权限。 -
EXIT=exit
:
这行代码定义了一个名为EXIT
的变量,并将其值设置为字符串exit
。在Shell脚本中,这可能用于后续作为命令的别名或者条件判断后的动作标识,尽管直接赋值为内置命令exit
的字符串形式在这里看起来不是标准用法,除非后续代码中有特定的处理逻辑来利用这个变量。 -
OS="$(uname -sr)"
:
这行代码通过命令替换(由$(...)
实现)获取了系统的内核名称和版本号,并将其赋值给变量OS
。uname -sr
命令组合中:uname
是一个显示系统信息的命令。-s
参数让uname
仅输出操作系统名称。-r
参数让uname
仅输出内核发行版本。
执行这行命令后,
OS
变量将存储如Linux 5.4.0-105-generic
这样的字符串,具体取决于你的系统实际的内核版本信息。这个变量通常用于在脚本中方便地引用系统类型和版本,以便根据不同的操作系统或内核做出相应的处理。
# In order to ensure that overall formatting and date formatting are
# consistent, all output should be funneled through here and this
# should only be called by functions such as 'info' and 'error'
# $1 - type of message (e.g. 'info')
# $2-N - message to output
_msg() {
msgType="$1"
shift
# AIX/HPIA/Solaris 5.10 do not allow space in date format string
msg="<`date "+%Y-%m-%d%t%Z%t%H:%M:%S"`> <$msgType> <UpdateApplication> <$@>"
echo $msg
}
info() {
_msg Info "$@"
}
error() {
_msg Error "$@"
}
这段代码定义了几个Shell函数,用于在终端输出格式化的信息消息和错误消息。下面是每个函数的解释:
-
_msg()
函数:- 定义了一个内部使用的函数,用于生成并打印带有时间戳、消息类型、固定标签(UpdateApplication)及自定义内容的消息。
- 函数接受两个参数:第一个参数
$1
是消息类型(例如"Info"或"Error"),使用
shift命令将位置参数向左移动,从而使
"$@"`只包含剩余的参数(即用户想要打印的实际消息内容)。 - 函数内部首先构建一个时间戳,尝试使用
date
命令获取当前的日期和时间(格式为年-月-日 时:分:秒,中间没有空格,以兼容AIX、HPIA和Solaris 5.10等系统)。 - 然后,它构造一条消息字符串,包括时间戳、消息类型、固定标签
UpdateApplication
以及通过"$@"
传递的所有剩余参数组成的自定义消息内容。 - 最后,使用
echo
命令打印这条格式化后的消息。
-
info()
函数:- 是一个简化的接口,调用
_msg
函数并预设消息类型为"Info",用于打印信息类消息。用户只需提供想要打印的信息内容,无需指定消息类型。
- 是一个简化的接口,调用
-
error()
函数:- 同样是
_msg
函数的一个简化接口,但预设消息类型为"Error",专门用于打印错误类消息。用法与info()
相同,只需提供错误描述信息即可。
- 同样是
总结来说,这段代码提供了一种标准化的方式来打印带有时间戳和分类的消息,使得脚本输出更加规范和易于识别,特别是在需要区分不同类型的日志(如信息、错误)时非常有用。
BP=100
SP=$BP
pushd()
{
if [ -z "$1" ]
then
return
fi
SP=`expr $SP - 1`
## _stack是一个前缀,用于构成一系列动态变量名
eval _stack$SP=`pwd`
cd $1
return
}
popd()
{
if [ $SP -eq $BP ]
then
return
fi
eval cd \${_stack$SP}
SP=`expr $SP + 1`
return
}
这段脚本定义了两个自定义的bash函数pushd
和popd
,它们模拟了shell内置命令pushd
和popd
的功能,用于管理目录栈,但在实现上使用了变量来保存目录路径,而非直接使用shell的内置目录栈。下面是对这两个函数的详细解释:
变量说明
BP
: 基础指针(Base Pointer),初始化为100,表示堆栈的底部位置。SP
: 堆栈指针(Stack Pointer),初始化等于BP
,指向当前栈顶的位置。
pushd() 函数
这个函数用于将当前目录"压入"一个模拟的堆栈中,并跳转到指定的目录。
- 首先检查是否有提供参数(即要切换到的目录路径),如果没有则直接返回。
- 然后,
SP
减1,模拟将新的目录路径"压入"堆栈顶部。这里使用了eval
和反引号(在新版本的bash中建议使用$(...)
替代反引号)来执行命令 substitution,将当前目录(pwd
)赋值给一个动态生成的变量_stack$SP
。例如,如果SP
为100,则实际变量名为_stack100
。 - 最后,使用
cd
命令切换到提供的目录。
popd() 函数
这个函数用于从模拟的堆栈中"弹出"最近的目录,并跳回该目录。
- 首先检查堆栈指针
SP
是否等于基础指针BP
,如果是,则说明堆栈为空,直接返回。 - 使用
eval
和变量展开来获取_stack$SP
所指向的目录路径,并使用cd
命令切换回该目录。 - 然后,
SP
加1,模拟从堆栈中"弹出"一个目录。
注意点
- 这个实现依赖于bash的
eval
命令和动态变量名,这可能不是最安全或最高效的方式,尤其是在处理用户输入时。 - 内存管理依赖于
SP
和BP
的正确操作,如果管理不当,可能会导致预期外的行为,比如栈溢出或下溢。 - 实际使用中,通常推荐直接使用shell的内置
pushd
和popd
命令,因为它们更安全且效率更高,无需手动管理堆栈指针和目录存储。