shell之flock 和 dirname
1、flock
最大的用途就是实现对 crontab 任务的串行化;为了防止crontab 任务出现多实例的情况,导致系统内存被耗尽。
在 crontab 任务中,有可能出现某个任务的执行时间超过了 crontab 中为此任务设定的执行周期,这就导致了当前的任务实例还未执行完成,crontab 又启动了同一任务的另外一个实例
这通常不是用户所期望的行为。极端情况下,如果某个任务执行异常一直未返回,crontab 不会处理这种情形,会继续启动新的实例,而新的实例很可能又会异常,
这样就导致 crontab 对同一任务不断的启动新的实例,最终导致系统内存被耗尽,影响到整个操作系统的运行。为了防止crontab 任务出现多实例的情况,可以使用 flock 命令将crontab 中任务的周期性执行串行化
* * * * * flock -xn /tmp/mytest.lock -c 'php /home/fdipzone/php/test.php'
flock 命令中,-x 表示对文件加上排他锁,-n 表示文件使用非阻塞模式,-c 选项指明加锁成功后要执行的命令。
因而上面flock 命令的整体含义就是:如果对 /tmp/mytest.lock 文件(如果文件不存在, flock 命令会自动创建)加锁成功就执行后面的命令,否则不执行。
假如上面 php 命令要执行2分钟,而crontab 任务每分钟就会执行一次,如果当前 php 命令正在执行,说明 flock 已经锁定了文件 /tmp/mytest.lock,crontab 到了再次执行任务的时间时,会发现文件已经被加了锁。
由于设置的是非阻塞模式的文件锁,flock 会在加锁失败时直接返回,并不执行php 命令,这样就使 php 命令得以顺序执行,crontab 任务就不会出现同时有两个实例运行的情况了,达到了串行化目的。
2、 $(dirname $0) 的用途
场景:一个脚本引用其它脚本,使用绝对路径执行该脚本时会找不到引用的脚本
通过使用dirname命令获取脚本执行时的文件路径,并在脚本中进入该目录,你可以确保无论脚本在哪里执行,都能够找到所需的文件。这种方法可以应用于调用同目录下的其他Shell脚本或使用相对路径访问其他文件的情况。
bash /root/shell/test1/a.sh /root/shell/test1/a.sh: line 5: b.sh: No such file or directory /root/shell/test1/a.sh: line 6: c.sh: No such file or directory
aaa
加入 cd $(dirname $0) 执行成功
root@arm:~# cat /root/shell/test1/a.sh #!/bin/bash cd $(dirname $0) . b.sh . c.sh a=aaa echo ${b} echo ${c} echo ${a} root@arm:~# bash /root/shell/test1/a.sh bbb ccc aaa
还可以进一步优化该方案。如果在其它路径创建了start.sh的符号链接,此时就需要获取该符号链接的目标文件地址。可以使用readlink命令来实现。以下是优化后的代码:
# 判断是否为符号链接 ftype=$(ls -l $0|cut -c 1) if [[ $ftype == l ]];then # 如果是符号链接,获取其目标文件地址 path=$(readlink $0) else # 否则直接使用当前文件路径 path=$0 fi # 进入脚本所在的目录 cd $(dirname $path)
不过还有一点需要注意,readlink只是获取它接受的参数指定的符号链接的目标文件,如果该目标文件仍然是一个符号链接,则需要继续使用readlink获取目标文件。可以将这部分封装成一个函数并递归调用,以下是该函数示例:
# 递归调用readlink获取符号链接的最终目标文件 rrl() { ftype=$(ls -l $1|cut -c 1) if [[ $ftype == l ]];then # 如果是符号链接,递归获取其目标文件地址 echo $(rrl $(readlink $1)) else # 否则直接使用当前文件路径 echo $1 fi } # 进入脚本所在的目录 cd $(dirname $(rrl $0))
dir=$(cd $(dirname $0);pwd) 常用于打印脚本所在的当前目录
之所以不能直接使用pwd
获取脚本所在目录,是因为如果在脚本目录之外调用该脚本,返回的是调用命令所在的目录而不是脚本所在目录。
3、检查用户是root,否则退出脚本的运行
#!/bin/bash umask 0022 unset IFS ##unset 删除变量 unset OFS unset LD_PRELOAD unset LD_LIBRARY_PATH export PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' if [ -w '/usr' ]; then #判断是否可写 installPath="/usr/local/qcloud/stargate" else installPath="/var/lib/qcloud/stargate" fi agent_name="$installPath/bin/sgagent" check_user() ##检查用户是root,否则退出脚本的运行 { if [ "root" != "`whoami`" ]; then echo "Only root can execute this script" exit 2 fi } check_alive() { status=`ps ax | grep "$agent_name" | grep -v "grep" |wc -l` if [ $status -ne 0 ]; then # process exist echo "stargate agent already exist" exit 1 fi } ### Main Begin ### check_user check_alive cd $(dirname $0) export LD_LIBRARY_PATH=$installPath/lib:$LD_LIBRARY_PATH $agent_name -d ret=$? if [ $ret -eq 0 ] then echo "stargate agent run succ" else echo "stargate agent run failed, errcode: $ret" fi exit $ret