shell的GoodCoder的编写

本文介绍了runit,一个用于管理应用程序启动的工具,它依赖于Procfile来定义进程及其启动命令。Procfile中的进程名字由字母、数字和下划线组成,且不支持后台命令。runit默认使用当前目录下的Procfile,并可以从.env文件中读取环境变量。runit提供了检查文件格式、启动进程、打印帮助等选项。日志会按进程颜色区分,且runit能响应SIGINT和SIGTERM信号,确保在子进程结束时退出。代码展示部分提供了实现runit功能的shell脚本规范和限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

runit是一个应用(application)启动管理工具。通过Procfile文件启动相应的进程。

1 试题描述

1.1 Procfile

Procfile 包含进程名字和启动进程的命令,用:分隔。如:

web: python -m SimpleHTTPServer $PORT
date: date $DATE_FORMAT
web_2: while true ; do nc -l $PORT < index.html
  • 进程名字可以包含:字母, 数字, 下划线
  • Procfile中不可以写后台命令
  • runit将这些命令运行在后台
  • runit默认使用当前路径下的Procfile文件
  • 如果多次使用$PORT变量,则值递增。如第一个PORT的值是8080,则第二个PORT的值为8081,如果不在.env中设置PORT变量的值,则自动设置默认值为8080

1.2 环境变量

如果当前目录下存在.env文件,则从其中读取环境变量。这个文件由 键/值对 构成。如:

PORT=8080
DATE_FORMAT='+%Y-%m-%d|%H:%M:%S'

1.3 程序执行

  • runit 启动Procfile中的所有进程
  • runit -f procfile -e env_file
  • runit -c 检查Procfile, env_file文件格式的正确性
  • runit -h 打印帮助

1.4 其他要求

  • Procfile和.env文件中可存在#注释
  • usage内容第一行如下, 其余内容自由发挥
Usage: runit [-c] [-f procfile|Procfile] [-e envfile|.env]
  • 日志打印到屏幕,格式如下,不同的进程的日志输出不同的颜色(web, date, web_2分别是不同的颜色)
11:39:45 web    | python -m SimpleHTTPServer 8080 started with pid 781
11:39:45 date   | date +%Y-%m-%d|%H:%M:%S started with pid 790
11:39:45 web_2  | nc -l 8081 < index.html started with pid 801
  • runit按照Procfile的描述启动进程,例如web就是启动一个前台进程(非后台进程或daemon)
  • runit可以接收SIGINT和SIGTERM信号,杀掉已启动的进程。确保runit在子进程运行结束后才退出。(如果进程可瞬间完成或是后台进程,则这个功能不起作用)
  • 除grep外不允许使用其他外部命令,如:sed, awk, ps, bc
  • 遵循shell编程规范

二、代码展示

#!/bin/bash
#
#Copyright (c) 2016 Baidu.com,Inc. All Right Reserved
#
#Author:panlu@baidu.com
#Date:2016/08/17
#
#Brief:
#  process launcher
#Globals:
#  PORT COLOR_ARRAY
#Arguments:
#  -c check
#  -f procfile
#  -e envfile
#  -h help
#Returns:
#  succ:0
#  fail:1

set -o pipefail

# environment
SPORT=8080
COLOR_ARRAY=('32' '33' '34' '35' '36')

# variable
procfile=""
envfile=""

#####################################
#Brief:
#  usage
#Globals:
#  none
#Agruments:
#  none
#Returns:
#  none
#####################################

function usage() {
    echo "Usage: runit [-c] [-f procfile|Procfile] [-e envfile|.env]
             -c: check procfile and envfile
             -f: load the procfile
             -e: load the envfile
             -h: help information
             "
}

#####################################
#Brief:
#  verify the envfile
#Globals:
#  none
#Agruments:
#  envfile
#Returns:
#  succ:0
#  fail:1
#####################################

function verify_env() {
    local env_file="$1"
    local ret_val=0

    [[ ! -f "${env_file}" ]] && my_err "verify envfile not exists" && return 1
    while read nextline; do
        if echo "${nextline}" | grep -v "="; then
            my_err "no_equal_mark"
            continue
        fi
        key="${nextline%%=*}"
        value="${nextline#*=}"
        echo "${key}" | grep -q "[^a-zA-Z_]" && my_err "invalid_char" && ret_val=1
        echo "${value}" | grep -qE "[[:space:]]" && my_err "value_have_space" && ret_val=1
    done < <(grep -vE "^[[:space:]]*#" "${env_file}" | grep -v "^$")
    return ${ret_val}
}

#####################################
#Brief:
#  verify the procfile
#Globals:
#  none
#Agruments:
#  procfile
#Returns:
#  succ:0
#  fail:1
#####################################

function verify_proc() {
    local proc_file="$1"
    local ret_val=0

    [[ ! -f "${proc_file}" ]] && my_err "verify procfile not exists" && return 1

    while read nextline ; do
        if echo "${nextline}" | grep -v ":"; then
            my_err "no_colon_command"
            continue
        fi
        key="${nextline%%:*}"
        value="${nextline#*:}"
        echo "${key}" | grep -q [^a-zA-Z_] && my_err "invalid_char" && ret_val=1
    done < <(grep -vE "^[[:space:]]*#" "${proc_file}")
    return ${ret_val}
}

#####################################
#Brief:
#  echo the error information
#Globals:
#  none
#Agruments:
#  errinfo
#Returns:
#  none
#####################################

function my_err() {
    errinfo="$1"
    echo "${errinfo}"
}

#####################################
#Brief:
#  echo the log information
#Globals:
#  COLOR_ARRAY 
#Agruments:
#  proc_name command
#Returns:
#  succ:0
#  fail:1
#####################################

function log() {
    local name="$1"
    local command="$2"
    local color="$3"
    cur_time=$(date +%H:%M:%s)

    printf "\E[${color}m${cur_time} %-7s] | " "${name}"

    tput sgr0
    echo "${command}"
    return 0
}

#####################################
#Brief:
#  execute the command
#Globals:
#  COLOR_ARRAY  PORT
#Agruments:
#  proc_name command
#Returns:
#  succ:0
#  fail:1
#####################################

function run_command() {
    local number="1"
    local proc_name="$1"
    local command="$2"
    local cur_pid=$!
    local cur_color="${COLOR_ARRAY[$number]}"
    local comm_port=$(echo "${command}" | grep -e "\$PORT")

    [[ -n "${comm_port}" ]] && [[ -z "${PORT}" ]] && PORT=8080
    bash -c "${command}" > >(
        while read result; do
            log "${proc_name}" "${result}" "${COLOR}"
        done
    ) 2>&1 &

    local output="$(eval echo \"${command}\")"
    log "${proc_name}" "${output} start with pid ${cur_pid}" "${cur_color}"
    [[ $? -ne 0 ]] && return 1

    [[ -n "${comm_port}" ]] && PORT=$((${PORT} + 1))

    (( number ++ ))

    return 0
}

#####################################
#Brief:
#  load the env_file
#Globals:
#  none
#Agruments:
#  envfile
#Returns:
#  succ:0
#  fail:1
#####################################

function load_env_file() {
    set -a
    local env_lists="$1"
    for flag in $(echo "${env_lists}"); do
        [[ -f "${flag}" ]] && source "${flag}"
    done
    return 0
}

#####################################
#Brief:
#  run procfile
#Globals:
#  none
#Agruments:
#  procfile
#Returns:
#  succ:0
#  fail:1
#####################################

function run_procfile() {
    local proc_file="$1"
    [[ ! -f "${proc_file}" ]] && my_err "this procfile is not exists" && return 1
    while read nextline; do
        if echo "${nextline}" | grep -qv ":"; then
            my_err "no_colon_command"
            continue
        fi
        local key="${nextline%%:*}"
        local value="${nextline#*:}"
        [[ -n "${key}" ]] && [[ -n "${value}" ]] && run_command "${key}" "${value}"
        [[ $? -ne 0 ]] && return 1
    done < <(grep "" "${proc_file}" | grep -vE "[[:space:]]*#" | grep -v "^$" )
    wait
    return 0
}

#####################################
#Brief:
#  main procedure
#Globals:
#  procfile encfile
#Agruments:
#  none
#Returns:
#  none
#####################################

function main() {
    local check=false
    while getopts "f:e:ch" flag
    do
        case ${flag} in
            c) check=true ;;
            f) procfile="${OPTARG}" ;;
            e) envfile="${OPTARG}" ;;
            *) usage ;;
        esac
    done

    if ${check}; then
        if [[ -n "${procfile}" ]]; then
            verify_proc "${procfile}"
            PROC_RET_VALUE=$?
            [[ ${PROC_RET_VALUE} -ne 0 ]] && exit 1
        else
            my_err "The procfile is null"
            exit 1
        fi

        if [[ -z "${envfile}" ]];then
            envfile="./.env"
        fi
        verify_env  "${envfile}"
        ENV_RET_VALUE=$?
        [[ ${ENV_RET_VALUE} -ne 0 ]] && exit 1

    else
        if [[ -z "${envfile}" ]]; then
            envfile="./.env"
        fi

        load_env_file "${envfile}"
        LOAD_ENV_RET_VALUE=$?
        [[ ${LOAD_ENV_RET_VALUE} -ne 0 ]] && exit 1

        if [[ -z "${procfile}" ]]; then
            procfile="./Procfile"
        fi

        run_procfile "${procfile}"
        RUN_RET_VALUE=$?
        [[ RUN_RET_VALUE -ne 0 ]] && exit 1
    fi
    exit 0
} 

main "$@"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值