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

  

posted @ 2022-09-28 16:49  凡人半睁眼  阅读(754)  评论(0编辑  收藏  举报