shell之flock 和 dirname

1、flock

最大的用途就是实现对 crontab 任务的串行化;为了防止crontab 任务出现多实例的情况,导致系统内存被耗尽。

在 crontab 任务中,有可能出现某个任务的执行时间超过了 crontab 中为此任务设定的执行周期,这就导致了当前的任务实例还未执行完成,crontab 又启动了同一任务的另外一个实例

这通常不是用户所期望的行为。极端情况下,如果某个任务执行异常一直未返回,crontab 不会处理这种情形,会继续启动新的实例,而新的实例很可能又会异常,

这样就导致 crontab 对同一任务不断的启动新的实例,最终导致系统内存被耗尽,影响到整个操作系统的运行。为了防止crontab 任务出现多实例的情况,可以使用 flock 命令将crontab 中任务的周期性执行串行化

1
* * * * * 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、 (dirname0) 的用途

场景:一个脚本引用其它脚本,使用绝对路径执行该脚本时会找不到引用的脚本

通过使用dirname命令获取脚本执行时的文件路径,并在脚本中进入该目录,你可以确保无论脚本在哪里执行,都能够找到所需的文件。这种方法可以应用于调用同目录下的其他Shell脚本或使用相对路径访问其他文件的情况。

1
2
3
4
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<br><br>
aaa

加入 cd (dirname0) 执行成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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命令来实现。以下是优化后的代码:

1
2
3
4
5
6
7
8
9
10
11
# 判断是否为符号链接
ftype=$(ls -l $0|cut -c 1)
if [[ $ftype == l ]];then
  # 如果是符号链接,获取其目标文件地址
  path=$(readlink $0)
else
  # 否则直接使用当前文件路径
  path=$0
fi
# 进入脚本所在的目录
cd $(dirname $path)

不过还有一点需要注意,readlink只是获取它接受的参数指定的符号链接的目标文件,如果该目标文件仍然是一个符号链接,则需要继续使用readlink获取目标文件。可以将这部分封装成一个函数并递归调用,以下是该函数示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 递归调用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,否则退出脚本的运行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#!/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

4、$(dirname "$(realpath "$0")") 

是一个常见的 Bash 表达式,用于获取脚本文件的所在目录的绝对路径。这在编写脚本时很有用,因为脚本可以在不同的路径下执行,但仍能准确找到与其相关的文件。让我们逐步解读这个表达式:

  1. $(): 这一对反引号是命令替换的语法,它会执行括号内部的命令,并返回其输出结果。在这种情况下,dirname "$(realpath "$0")" 被执行,结果将替换到表达式的位置。

  2. realpath "$0":

    • realpath: 这个命令用于解析给定路径的绝对路径,比如将相对路径转换为绝对路径。它还能解析符号链接,以确保最终得到真正的物理路径。
    • "$0": 在 Bash 中,$0 特殊变量代表当前脚本的名称,包括相对或绝对路径(取决于调用它时使用的命令)。
    • 组合起来,realpath "$0" 会返回当前执行脚本的绝对路径。
  3. dirname ...:

    • dirname: 这个命令用于获取给定路径中的目录名,实际上就是去掉路径中的文件名。
    • 所以 dirname "$(realpath "$0")" 提供的是去掉脚本名之后的该脚本目录路径。

举个例子

假设一个脚本文件 /home/user/scripts/myscript.sh,用多种方式调用这个脚本的效果如下:

  • 直接调用: 执行 ./myscript.sh 时,realpath "$0" 返回 /home/user/scripts/myscript.sh,然后 dirname 会提取并返回 /home/user/scripts

  • 绝对路径调用: 执行 /home/user/scripts/myscript.sh,结果同上。

 

posted @   凡人半睁眼  阅读(796)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
· Manus的开源复刻OpenManus初探
历史上的今天:
2020-09-28 Linux清理入侵痕迹

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示