0 前言

本文参考以下链接:

1 不同的执行方式

shell 脚本通常有 sh filenamebash filename./filenamesource filename 这四种执行方式

  • source filename 可以使用 . filename 代替,在当前的 bash 环境下读取并执行脚本文件中的命令,且脚本文件的变量,在脚本执行完成后会保存下来
  • ./filenamesh filename 或者 bash filename 是等效的,都是开启一个子 shell 来运行脚本文件,脚本中设置的变量执行完毕后不会保存

./filename 外,source filename. filenamesh filenamebash filename 都是不需要执行权限的

变量和权限问题示例

# 设置临时变量,仅在当前 bash 环境生效
[root@lvbibir ~]# name=lvbibir
[root@lvbibir ~]# echo $name
lvbibir
[root@lvbibir ~]#
[root@lvbibir ~]# cat test.sh
#!/bin/bash
echo $name

# source 或者 . 可以获取到父 bash 环境的变量
[root@lvbibir ~]# source test.sh
lvbibir
[root@lvbibir ~]# . test.sh
lvbibir

# sh、bash、./三种方式都使用了子 bash 环境,所以无法获取父 bash 环境的变量
# ./ 方式需要脚本有执行权限
[root@lvbibir ~]# sh test.sh

[root@lvbibir ~]# bash test.sh

[root@lvbibir ~]# ./test.sh
-bash: ./test.sh: Permission denied
[root@lvbibir ~]# chmod a+x test.sh
[root@lvbibir ~]# ./test.sh

同理,使用 source 或者 . 也可以在 bash 环境中获取到脚本中设置的变量

[root@lvbibir ~]# cat > test.sh << EOF
> #!/bin/bash
> number=22
>
> EOF
[root@lvbibir ~]# echo $number

# sh bash ./ 三种方式无法获取脚本中的变量
[root@lvbibir ~]#
[root@lvbibir ~]# sh test.sh
[root@lvbibir ~]# echo $number

[root@lvbibir ~]# bash test.sh
[root@lvbibir ~]# echo $number

[root@lvbibir ~]# ./test.sh
[root@lvbibir ~]# echo $number

# source 方式可以获取脚本中的变量
[root@lvbibir ~]# source test.sh
[root@lvbibir ~]# echo $number
22
[root@lvbibir ~]#

2 其他问题

关于是否在子 bash 环境运行的区别出了变量问题还会存在一些其他影响,如下测试

已知目前存在一个 mysqld 进程,其 pid 为 29426 ,写一个监控 pid 的脚本

[root@lvbibir ~]# cat test.sh
#!/bin/bash
process=$1
pid=$(ps -elf | grep $process | grep -v grep | awk '{print $4}')
echo $pid

两种方式分别运行一下

[root@lvbibir ~]# sh test.sh mysqld
27038 27039 29426
[root@lvbibir ~]# bash test.sh mysqld
27047 27048 29426
[root@lvbibir ~]# ./test.sh mysqld
27056 27057 29426
[root@lvbibir ~]#
[root@lvbibir ~]# source test.sh mysqld
29426
[root@lvbibir ~]# . test.sh mysqld
29426
[root@lvbibir ~]#

问题出现了,由于某种原因导致子 bash 环境中执行的脚本监控到多个 pid ,给脚本添加个 sleep 来看下

[root@lvbibir ~]# cat test.sh
#!/bin/bash
process=$1
pid=$(ps -elf | grep $process | grep -v grep | awk '{print $4}')
echo $pid
sleep 30

[root@lvbibir ~]# ./test.sh mysqld
27396 27397 29426

新开一个终端,查看进程

image-20220630160731914

  • 第一个 pid 是在子 shell 中执行监控脚本的进程号
  • 第二个 pid 不太清楚哪里来的,也 grep 不到这个进程号,应该是脚本执行一瞬间就释放掉了
  • 第三个 pid 是 mysql 实际运行中的进程号

实际中脚本的 pid 和 mysqld 的 pid 顺序不太一样,取决于 pid 的大小

在脚本再添加个 grep 过滤掉脚本本身的进程来规避这个问题

[root@lvbibir ~]# cat test.sh
#!/bin/bash
process=$1
pid=$(ps -elf | grep $process | grep -v grep | grep -v bash | awk '{print $4}')
echo $pid

[root@lvbibir ~]# ./test.sh mysqld
29426

以上