Files
qinglong/shell/task.sh
T
whyour 5801f735bc fix: improve shell compatibility and process cleanup
Preserve literal paths and arguments, use portable command options, validate startup configuration, and retain scripts when downloads fail.

Ensure task watchdogs and process groups are cleaned up across Bash versions, with compatibility and process lifecycle regression coverage.
2026-10-15 23:20:15 +08:00

167 lines
4.1 KiB
Bash
Executable File

#!/usr/bin/env bash
dir_shell=$QL_DIR/shell
. "$dir_shell/share.sh"
. "$dir_shell/api.sh"
. "$dir_shell/task-timeout.sh"
trap 'single_hanle SIGINT' INT
trap 'single_hanle SIGTERM' TERM
trap 'single_hanle SIGHUP' HUP
trap 'single_hanle SIGALRM' ALRM
trap 'single_hanle SIGTSTP' TSTP
trap 'single_hanle SIGQUIT' QUIT
single_hanle() {
_task_exit_code="${_task_exit_code:-$?}"
[[ "$_task_exit_code" == "0" ]] && _task_exit_code="1"
eval MANUAL=true handle_task_end "$@"
exit 1
}
run_shell_script() {
# Preserve inherited variables/functions, but contain exit, exec and shell
# options so the task wrapper can run after hooks and report completion.
( . "$@" )
}
define_program() {
local file_param=$1
if [[ $file_param == *.js ]] || [[ $file_param == *.mjs ]]; then
which_program="node"
elif [[ $file_param == *.py ]] || [[ $file_param == *.pyc ]]; then
which_program="python3"
elif [[ $file_param == *.sh ]]; then
which_program="run_shell_script"
elif [[ $file_param == *.ts ]]; then
which_program="ts-node-transpile-only"
else
which_program=""
fi
}
handle_log_path() {
local file_param=$1
if [[ -z $file_param ]]; then
file_param="task"
fi
if [[ -z ${ID:=} ]]; then
ID=$(cat "$list_crontab_user" | grep -E "$cmd_task.* $file_param" | perl -pe "s|.*ID=(.*) $cmd_task.* $file_param\.*|\1|" | head -1 | awk -F " " '{print $1}')
fi
local suffix=""
if [[ ! -z $ID ]]; then
if [[ "$ID" -gt 0 ]] 2>/dev/null; then
suffix="_${ID}"
else
ID=""
fi
fi
if [[ $is_macos -ne 1 && $mtime_format == '%Y-%m-%d %H:%M:%S.%3N' ]]; then
# Render both representations from one clock snapshot, without parsing it
# again in a second date process.
IFS='|' read -r time log_time < <(date "+$mtime_format|%Y-%m-%d-%H-%M-%S-%3N")
else
time=$(date "+$mtime_format")
log_time=$(format_log_time "$mtime_format" "$time")
fi
if [[ -z $log_name ]]; then
log_dir_tmp="${file_param##*/}"
if [[ $file_param =~ "/" ]]; then
if [[ $file_param == /* ]]; then
log_dir_tmp_path="${file_param:1}"
else
log_dir_tmp_path="${file_param}"
fi
fi
log_dir_tmp_path="${log_dir_tmp_path%/*}"
log_dir_tmp_path="${log_dir_tmp_path##*/}"
[[ $log_dir_tmp_path ]] && log_dir_tmp="${log_dir_tmp_path}_${log_dir_tmp}"
log_dir="${log_dir_tmp%.*}${suffix}"
else
log_dir="$log_name"
fi
log_path="$log_dir/$log_time.log"
if [[ ${real_log_path:=} ]]; then
log_path="$real_log_path"
fi
printf -v cmd '2>&1 | tee -a %q' "$dir_log/$log_path"
make_dir "$dir_log/$log_dir"
if [[ "${no_tee:=}" == "true" ]]; then
printf -v cmd '>> %q 2>&1' "$dir_log/$log_path"
fi
if [[ "${real_time:=}" == "true" ]]; then
cmd=""
fi
if [[ "${log_dir:=}" == "/dev/null" ]]; then
cmd=">> /dev/null"
log_path="/dev/null"
fi
}
format_params() {
time_format="%Y-%m-%d %H:%M:%S"
if [[ $is_macos -eq 1 ]]; then
mtime_format=$time_format
else
mtime_format="%Y-%m-%d %H:%M:%S.%3N"
fi
timeoutCmd=""
# params=$(echo "$@" | sed -E 's/([^ ])&([^ ])/\1\\\&\2/g')
# 分割 task 内置参数和脚本参数
task_shell_params=()
script_params=()
found_double_dash=false
for arg in "$@"; do
if $found_double_dash; then
script_params+=("$arg")
elif [ "$arg" == "--" ]; then
found_double_dash=true
else
task_shell_params+=("$arg")
fi
done
}
init_begin_time() {
if [[ $is_macos -ne 1 && $mtime_format == '%Y-%m-%d %H:%M:%S.%3N' && $time_format == '%Y-%m-%d %H:%M:%S' ]]; then
begin_time=${time%.*}
else
begin_time=$(format_time "$time_format" "$time")
fi
begin_timestamp=$(format_timestamp "$time_format" "$time")
}
import_config "$@"
while getopts ":lm:" opt; do
case $opt in
l)
show_log="true"
;;
m)
max_time="$OPTARG"
;;
esac
done
[[ ${show_log:=} ]] && shift $(($OPTIND - 1))
if [[ ${max_time:=} ]]; then
shift $(($OPTIND - 1))
command_timeout_time="$max_time"
fi
format_params "$@"
define_program "${task_shell_params[@]}"
handle_log_path "${task_shell_params[@]}"
init_begin_time
eval '. "$dir_shell/otask.sh"' "$cmd"
exit 0