Linux Shell学习 之 /etc/init.d/functions 详解


=Start=

缘由:

简单学习一下Linux下原生的functions脚本提供的功能,可以为自己编写Bash脚本提供一些参考。

正文:

参考解答:
[root@sec-test ~]# ls -lt /etc/init.d/functions
-rw-r--r-- 1 root root 17500 May  3 06:17 /etc/init.d/functions

[root@sec-test ~]# rpm -qf /etc/init.d/functions
initscripts-9.49.39-1.el7.x86_64

&

# 字符串包含判断
# returns OK if $1 contains $2
strstr() {
  [ "${1#*$2*}" = "$1" ] && return 1
  return 0
}

# 判断给出的第一个参数表示的文件名是否为特定后缀
# Check whether file $1 is a backup or rpm-generated file and should be ignored
is_ignored_file() {
    case "$1" in
	*~ | *.bak | *.orig | *.rpmnew | *.rpmorig | *.rpmsave)
	    return 0
	    ;;
    esac
    return 1
}

# 判断提供的第一个参数是否为「t/T/y/Y/yes/YES/Yes/.../true/...」
# Evaluate shvar-style booleans
is_true() {
    case "$1" in
	[tT] | [yY] | [yY][eE][sS] | [tT][rR][uU][eE])
	return 0
	;;
    esac
    return 1
}

# 判断提供的第一个参数是否为「f/F/n/N/no/NO/No/.../false/...」
# Evaluate shvar-style booleans
is_false() {
    case "$1" in
	[fF] | [nN] | [nN][oO] | [fF][aA][lL][sS][eE])
	return 0
	;;
    esac
    return 1
}

# 判断参数中给定的pid对应进程是否存在
# Check if any of $pid (could be plural) are running
checkpid() {
	local i

	for i in $* ; do
		[ -d "/proc/$i" ] && return 0
	done
	return 1
}

#起到类似readlink命令的作用
__readlink() {
    ls -bl "$@" 2>/dev/null| awk '{ print $NF }'
}

# 输出指定进程的pid列表(通过调用pidof命令实现)
# Output PIDs of matching processes, found using pidof
__pids_pidof() {
	pidof -c -m -o $$ -o $PPID -o %PPID -x "$1" || \
		pidof -c -m -o $$ -o $PPID -o %PPID -x "${1##*/}"
}

# 以守护进程的方式启动一个进程
# A function to start a program.
daemon() {
}

# 杀掉进程的函数
# A function to stop a program.
killproc() {
}

# 多彩打印
echo_success() {
}
echo_failure() {
}
echo_warning() {
}
...

# 记录操作并以多彩样式输出其执行结果
# Run some action. Log its output.
action() {
  local STRING rc

  STRING=$1
  echo -n "$STRING "
  shift
  "$@" && success $"$STRING" || failure $"$STRING"
  rc=$?
  echo
  return $rc
}

&

#!/bin/bash
# Date:        20171024
# set -x

# Source function library.
if [ -f /etc/init.d/functions ]; then
    . /etc/init.d/functions
else
	echo '"/etc/init.d/functions" not exists'
	echo 'try "rpm -ql initscripts | grep --color /functions"'
	exit 0
fi

export PATH=/usr/local/bin:/bin:/usr/bin:/sbin:/usr/sbin/:/usr/local/sbin

#action 'date +"%F %T"
#' date +"%F %T"
## [ "$?" -eq 0 ] && echo_success || echo_failure
#
## date +"%F %T"
#action 'rpm -ql /etc/init.d/functions
#' rpm -ql /etc/init.d/functions
#
## [ "$?" -eq 0 ] && echo_success || echo_failure
#$SETCOLOR_SUCCESS && echo "success" && $SETCOLOR_NORMAL
#$SETCOLOR_FAILURE && echo "failure" && $SETCOLOR_NORMAL
#$SETCOLOR_WARNING && echo "warning" && $SETCOLOR_NORMAL
#
#echo_s() {
#    $SETCOLOR_SUCCESS && echo "$@" && $SETCOLOR_NORMAL
#}
#
#echo_f() {
#    $SETCOLOR_FAILURE && echo "$@" && $SETCOLOR_NORMAL
#}
#
#echo_w() {
#    $SETCOLOR_WARNING && echo "$@" && $SETCOLOR_NORMAL
#}
#
#echo_s haha
#echo_f haha
#echo_w haha


s=$"Starting $prog (via systemctl): "    #在字符串前面添加 $ 是为啥?
echo $s

s2="Starting $prog (via systemctl): "    #测试来看没啥区别
echo $s2

#获取屏幕宽度
[ -z "${COLUMNS:-}" ] && COLUMNS=80
echo $COLUMNS

action 'checkpid 1' checkpid 1
action 'checkpid 19999' checkpid 19999

# returns OK if $1 contains $2
action 'strstr()' strstr "this is the whole string." "search_string"

action 'is_true yes' is_true yes
action 'is_true yeS' is_true yeS
action 'is_true Yes' is_true Yes
action 'is_true yEs' is_true yEs
action 'is_true yy' is_true yy

action 'is_false no' is_false no
action 'is_false No' is_false No
action 'is_false N' is_false N
action 'is_false false' is_false false
action 'is_false x' is_false x
参考链接:

=END=

, , ,

《 “Linux Shell学习 之 /etc/init.d/functions 详解” 》 有 2 条评论

  1. Shell 函数式编程
    http://blog.xiayf.cn/2018/05/16/functional-shell-code/
    `
    true_then_run() {
    condition=$1
    action=$2

    (${condition})
    if [ $? -eq 0 ]
    then
    (${action})
    else
    echo “‘$1’ is false, don’t run ‘$2′”
    fi
    }

    false_then_run() {
    condition=$1
    action=$2

    (${condition})
    if [ $? -ne 0 ]
    then
    (${action})
    else
    echo “‘$1’ is true, don’t run ‘$2′”
    fi
    }

    map() {
    for item in $2
    do
    ($1 ${item})
    done
    }

    reduce() {
    operator=$1
    #
    list=($2)
    list_len=${#list[@]}
    #
    result=””
    if [ ${list_len} -gt 0 ]
    then
    result=${list[0]}
    fi
    #
    if [ ${list_len} -gt 1 ]
    then
    for item in ${list[@]:1}
    do
    result=$(${operator} ${result} ${item})
    done
    fi
    echo ${result}
    }

    filter() {
    for item in $2
    do
    ($1 ${item})
    if [ $? -eq 0 ]
    then
    echo ${item}
    fi
    done
    }

    sum() {
    v=0
    for arg in “$@”
    do
    v=$(($v+${arg}))
    done
    echo $v
    }
    `

  2. 运维必看!如何让shell脚本自杀
    https://www.yunweipai.com/45372.html
    https://www.cnblogs.com/f-ck-need-u/p/8661501.html
    `
    1.脚本自杀正文
    有些时候我们写的shell脚本中有一些后台任务,当脚本的流程已经执行到结尾处或将其kill掉时,这些后台任务会直接挂靠在init/systemd进程下,而不会随着脚本退出而停止。

    例如:

    [root@mariadb ~]# cat test1.sh
    #!/bin/bash
    echo $BASHPID
    sleep 50 &

    [root@mariadb ~]# ps -elf | grep slee[p]
    0 S root 10806 1 0 80 0 – 26973 hrtime 19:26 pts/1 00:00:00 sleep 50
    从结果中可以看到,脚本退出后,sleep进程的父进程变为了1,也就是挂在了init/systemd进程下。

    这时我们可以在脚本中直接使用kill命令杀掉sleep进程。

    [root@mariadb ~]# cat test1.sh
    #!/bin/bash
    echo $BASHPID
    sleep 50 &
    kill $!
    但是,如果这个sleep进程是在循环中(for、while、until均可),那就麻烦了。

    例如下面的例子,直接将循环放入后台,杀掉sleep、或者exit、或者杀掉脚本自身进程、或者让脚本自动退出、甚至exec退出当前脚本shell都是无效的。

    [root@mariadb ~]# cat test1.sh
    #!/bin/bash
    echo $BASHPID

    while true;do
    sleep 50
    echo 1
    done &

    killall sleep
    kill $BASHPID
    为了分析,新建一个脚本test2.sh:

    #!/bin/bash
    echo $BASHPID

    while true;do
    sleep 50
    echo 1
    done &

    sleep 60
    然后在脚本执行的60秒内查看test2.sh进程的信息:

    [root@mariadb ~]# pstree -p | grep “test2.sh”
    | `-bash(2687)—test2.sh(2923)-+-sleep(2925)
    | `-test2.sh(2924)—sleep(2926)
    其中pid=2923的test2.sh进程是脚本自身进程,pid=2924的test2.sh进程是while开始运行后为while提供执行环境的子shell进程(为什么会生成这个进程,见我的另一篇文章)。

    所以,对于前面的test1.sh进程,杀掉了 $BASHPID 对应的test1.sh进程后,其实还有一个为while提供运行环境的test1.sh进程,且这个进程在 $BASHPID 结束后,会挂在init/systemd下。

    本文只是提供一种杀脚本的解决方案。很多情形并非如我这里所描述的,例如不是while循环放后台,而是循环内的sleep放后台,这时(脚本终止时)sleep会挂在init/systemd下,不过这很简单。相信读懂了本文,各位已经了解了一些trap的功能以及处理这类问题的逻辑,也知道其他各种情形如何处理。

    最后,有一种更方便更精确的自杀手段:man kill。在该man手册中解释了,如果kill的pid值为0,表示发送信号给当前进程组中所有进程,对shell脚本来说这意味着杀掉脚本中产生的所有进程。方案如下:

    #!/bin/bash

    trap “echo ‘signal_handled:’;kill 0” SIGINT SIGTERM

    while true;do
    sleep 5
    echo “hello world! hello world!”
    done &
    sleep 60

    `

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注