From e62f485df4c259abba5eb72b4f6848d930239f60 Mon Sep 17 00:00:00 2001 From: juewuy Date: Wed, 24 Dec 2025 11:09:28 +0800 Subject: [PATCH] =?UTF-8?q?~=E7=BB=A7=E7=BB=AD=E6=8B=86=E5=88=86=E8=84=9A?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/init.sh | 23 +- scripts/libs/README.md | 20 +- scripts/libs/compare.sh | 10 + scripts/libs/logger.sh | 59 ++++ scripts/menu.sh | 271 +++++------------- scripts/menus/1_start.sh | 54 ++++ .../menus/{2-settings.sh => 2_settings.sh} | 1 + scripts/menus/{4-setboot.sh => 4_setboot.sh} | 0 scripts/menus/{5-task.sh => 5_task.sh} | 0 .../{6-core_config.sh => 6_core_config.sh} | 49 ++-- scripts/menus/{7-gateway.sh => 7_gateway.sh} | 0 scripts/menus/{8-tools.sh => 8_tools.sh} | 0 scripts/menus/{9-upgrade.sh => 9_upgrade.sh} | 3 +- scripts/menus/uninstall.sh | 67 +++++ scripts/start.sh | 74 +---- scripts/starts/README.md | 4 +- 16 files changed, 315 insertions(+), 320 deletions(-) create mode 100644 scripts/libs/compare.sh create mode 100644 scripts/libs/logger.sh create mode 100644 scripts/menus/1_start.sh rename scripts/menus/{2-settings.sh => 2_settings.sh} (99%) rename scripts/menus/{4-setboot.sh => 4_setboot.sh} (100%) rename scripts/menus/{5-task.sh => 5_task.sh} (100%) rename scripts/menus/{6-core_config.sh => 6_core_config.sh} (95%) rename scripts/menus/{7-gateway.sh => 7_gateway.sh} (100%) rename scripts/menus/{8-tools.sh => 8_tools.sh} (100%) rename scripts/menus/{9-upgrade.sh => 9_upgrade.sh} (99%) create mode 100644 scripts/menus/uninstall.sh diff --git a/scripts/init.sh b/scripts/init.sh index 7bb41ff4..3bd03a2b 100644 --- a/scripts/init.sh +++ b/scripts/init.sh @@ -189,9 +189,8 @@ mkdir -p ${CRASHDIR}/configs [ -w /etc/systemd/system ] && sysdir=/etc/systemd/system if [ -f /etc/rc.common -a "$(cat /proc/1/comm)" = "procd" ]; then #设为init.d方式启动 - cp -f ${CRASHDIR}/shellcrash.procd /etc/init.d/shellcrash + cp -f ${CRASHDIR}/starts/shellcrash.procd /etc/init.d/shellcrash chmod 755 /etc/init.d/shellcrash - rm -rf ${CRASHDIR}/shellcrash.openrc elif [ -n "$sysdir" -a "$USER" = "root" -a "$(cat /proc/1/comm)" = "systemd" ]; then #创建shellcrash用户 userdel shellcrash 2>/dev/null @@ -204,23 +203,27 @@ elif [ -n "$sysdir" -a "$USER" = "root" -a "$(cat /proc/1/comm)" = "systemd" ]; echo "shellcrash:x:0:7890::/home/shellcrash:/bin/sh" >>/etc/passwd fi #配置systemd - mv -f ${CRASHDIR}/shellcrash.service $sysdir/shellcrash.service 2>/dev/null + mv -f ${CRASHDIR}/starts/shellcrash.service $sysdir/shellcrash.service 2>/dev/null sed -i "s%/etc/ShellCrash%$CRASHDIR%g" $sysdir/shellcrash.service systemctl daemon-reload + rm -rf ${CRASHDIR}/starts/shellcrash.procd elif rc-status -r >/dev/null 2>&1; then #设为openrc方式启动 - cp -f ${CRASHDIR}/shellcrash.openrc /etc/init.d/shellcrash + mv -f ${CRASHDIR}/starts/shellcrash.openrc /etc/init.d/shellcrash chmod 755 /etc/init.d/shellcrash - rm -rf ${CRASHDIR}/shellcrash.procd + rm -rf ${CRASHDIR}/starts/shellcrash.procd else #设为保守模式启动 setconfig start_old 已开启 + rm -rf ${CRASHDIR}/starts/shellcrash.procd fi +rm -rf ${CRASHDIR}/starts/shellcrash.service +rm -rf ${CRASHDIR}/starts/shellcrash.openrc #修饰文件及版本号 command -v bash >/dev/null 2>&1 && shtype=bash [ -x /bin/ash ] && shtype=ash -for file in start.sh menus/task.sh menu.sh; do +for file in start.sh menus/5-task.sh menu.sh; do sed -i "s|/bin/sh|/bin/$shtype|" ${CRASHDIR}/${file} 2>/dev/null chmod +x ${CRASHDIR}/${file} 2>/dev/null done @@ -279,16 +282,16 @@ fi [ -f "/etc/storage/started_script.sh" ] && mount -t tmpfs -o remount,rw,size=45M tmpfs /tmp #增加/tmp空间以适配新的内核压缩方式 #镜像化OpenWrt(snapshot)额外设置 if [ "$systype" = "mi_snapshot" -o "$systype" = "ng_snapshot" ]; then - chmod 755 ${CRASHDIR}/misnap_init.sh + chmod 755 ${CRASHDIR}/starts/snapshot_init.sh uci delete firewall.ShellClash 2>/dev/null uci delete firewall.ShellCrash 2>/dev/null uci set firewall.ShellCrash=include uci set firewall.ShellCrash.type='script' - uci set firewall.ShellCrash.path="$CRASHDIR/misnap_init.sh" + uci set firewall.ShellCrash.path="$CRASHDIR/starts/snapshot_init.sh" uci set firewall.ShellCrash.enabled='1' uci commit firewall else - rm -rf ${CRASHDIR}/misnap_init.sh + rm -rf ${CRASHDIR}/starts/snapshot_init.sh fi #华硕USB启动额外设置 [ "$usb_status" = "1" ] && { @@ -361,7 +364,7 @@ rm -rf /etc/init.d/clash rm -rf ${CRASHDIR}/rules rm -rf "$CRASHDIR/task/task.sh" [ "$systype" = "mi_snapshot" -a "$CRASHDIR" != '/data/clash' ] && rm -rf /data/clash -for file in CrashCore clash.sh getdate.sh core.new clashservice log shellcrash.service mark? mark.bak; do +for file in CrashCore clash.sh getdate.sh core.new clashservice log mark? mark.bak; do rm -rf "$CRASHDIR/$file" done #旧版变量改名 diff --git a/scripts/libs/README.md b/scripts/libs/README.md index 1454be14..380299a7 100644 --- a/scripts/libs/README.md +++ b/scripts/libs/README.md @@ -1,11 +1,11 @@ -用于存放脚本内置工具的脚本 - -引用方式必须为: - -```shell -. "$CRASHDIR"/libs/xxx.sh -``` - -返回码必须是return x而不能是exit x - +用于存放脚本内置工具的脚本 + +引用方式必须为: + +```shell +. "$CRASHDIR"/libs/xxx.sh +``` + +返回码必须是return x而不能是exit x + 此处脚本内容不应包含文字输出和log输出 \ No newline at end of file diff --git a/scripts/libs/compare.sh b/scripts/libs/compare.sh new file mode 100644 index 00000000..c0f3a684 --- /dev/null +++ b/scripts/libs/compare.sh @@ -0,0 +1,10 @@ +compare() { #对比文件 + if [ ! -f "$1" ] || [ ! -f "$2" ]; then + return 1 + elif ckcmd cmp; then + cmp -s "$1" "$2" + return $? + else + [ "$(cat "$1")" = "$(cat "$2")" ] && return 0 || return 1 + fi +} \ No newline at end of file diff --git a/scripts/libs/logger.sh b/scripts/libs/logger.sh new file mode 100644 index 00000000..7044586b --- /dev/null +++ b/scripts/libs/logger.sh @@ -0,0 +1,59 @@ +#日志工具 +#$1日志内容$2显示颜色$3是否推送 +logger() { + [ -n "$2" -a "$2" != 0 ] && echo -e "\033[$2m$1\033[0m" + log_text="$(date "+%G-%m-%d_%H:%M:%S")~$1" + echo "$log_text" >>"$TMPDIR"/ShellCrash.log + [ "$(wc -l "$TMPDIR"/ShellCrash.log | awk '{print $1}')" -gt 99 ] && sed -i '1,50d' "$TMPDIR"/ShellCrash.log + #推送工具 + webpush() { + [ -n "$(pidof CrashCore)" ] && { + [ -n "$authentication" ] && auth="$authentication@" + export https_proxy="http://${auth}127.0.0.1:$mix_port" + } + if curl --version >/dev/null 2>&1; then + curl -kfsSl -X POST --connect-timeout 3 -H "Content-Type: application/json; charset=utf-8" "$1" -d "$2" >/dev/null 2>&1 + elif wget --version >/dev/null 2>&1; then + wget -Y on -q --timeout=3 -O - --method=POST --header="Content-Type: application/json; charset=utf-8" --body-data="$2" "$1" >/dev/null 2>&1 + fi + } + [ -z "$3" ] && { + [ -n "$device_name" ] && log_text="$log_text($device_name)" + [ -n "$push_TG" ] && { + url="https://api.telegram.org/bot${push_TG}/sendMessage" + [ "$push_TG" = 'publictoken' ] && url='https://tgbot.jwsc.eu.org/publictoken/sendMessage' + content="{\"chat_id\":\"${chat_ID}\",\"text\":\"$log_text\"}" + webpush "$url" "$content" & + } + [ -n "$push_bark" ] && { + url="${push_bark}" + content="{\"body\":\"${log_text}\",\"title\":\"ShellCrash日志推送\",\"level\":\"passive\",\"badge\":\"1\"}" + webpush "$url" "$content" & + } + [ -n "$push_Deer" ] && { + url="https://api2.pushdeer.com/message/push" + content="{\"pushkey\":\"${push_Deer}\",\"text\":\"$log_text\"}" + webpush "$url" "$content" & + } + [ -n "$push_Po" ] && { + url="https://api.pushover.net/1/messages.json" + content="{\"token\":\"${push_Po}\",\"user\":\"${push_Po_key}\",\"title\":\"ShellCrash日志推送\",\"message\":\"$log_text\"}" + webpush "$url" "$content" & + } + [ -n "$push_PP" ] && { + url="http://www.pushplus.plus/send" + content="{\"token\":\"${push_PP}\",\"title\":\"ShellCrash日志推送\",\"content\":\"$log_text\"}" + webpush "$url" "$content" & + } + [ -n "$push_Gotify" ] && { + url="${push_Gotify}" + content="{\"title\":\"ShellCrash日志推送\",\"message\":\"$log_text\",\"priority\":5}" + webpush "$url" "$content" & + } + [ -n "$push_SynoChat" ] && { + url="${push_ChatURL}/webapi/entry.cgi?api=SYNO.Chat.External&method=chatbot&version=2&token=${push_ChatTOKEN}" + content="payload={\"text\":\"${log_text}\", \"user_ids\":[${push_ChatUSERID}]}" + webpush "$url" "$content" & + } + } & +} diff --git a/scripts/menu.sh b/scripts/menu.sh index c7888fbc..586cfb66 100644 --- a/scripts/menu.sh +++ b/scripts/menu.sh @@ -6,15 +6,13 @@ CRASHDIR=$( pwd ) CFG_PATH="$CRASHDIR"/configs/ShellCrash.cfg -YAMLSDIR="$CRASHDIR"/yamls -JSONSDIR="$CRASHDIR"/jsons #加载执行目录,失败则初始化 . "$CRASHDIR"/configs/command.env 2>/dev/null [ -z "$BINDIR" -o -z "$TMPDIR" -o -z "$COMMAND" ] && . "$CRASHDIR"/init.sh >/dev/null 2>&1 [ ! -f "$TMPDIR" ] && mkdir -p "$TMPDIR" [ -n "$(tar --help 2>&1 | grep -o 'no-same-owner')" ] && tar_para='--no-same-owner' #tar命令兼容 -#加载工具 +#通用工具 . "$CRASHDIR"/libs/set_config.sh . "$CRASHDIR"/libs/check_cmd.sh errornum() { @@ -28,7 +26,8 @@ checkrestart() { read -p "是否现在重启服务?(1/0) > " res [ "$res" = 1 ] && start_service } -checkport() { #自动检查端口冲突 + +checkport() { #检查端口冲突 for portx in $dns_port $mix_port $redir_port $((redir_port + 1)) $db_port; do if [ -n "$(netstat -ntul 2>&1 | grep ':$portx ')" ]; then echo "-----------------------------------------------" @@ -36,14 +35,13 @@ checkport() { #自动检查端口冲突 echo $(netstat -ntul | grep :$portx | head -n 1) echo -e "\033[0m-----------------------------------------------" echo -e "\033[36m请修改默认端口配置!\033[0m" - setport + . "$CRASHDIR"/menus/2_settings.sh && set_adv_config . "$CFG_PATH" >/dev/null checkport fi done } -#脚本启动前检查 -ckstatus() { +ckstatus() { #脚本启动前检查 #检查/读取脚本配置文件 if [ -f "$CFG_PATH" ]; then [ -n "$(awk 'a[$0]++' $CFG_PATH)" ] && awk '!a[$0]++' "$CFG_PATH" >"$CFG_PATH" #检查重复行并去除 @@ -59,10 +57,7 @@ ckstatus() { [ -z "$fwmark" ] && fwmark=$redir_port [ -z "$db_port" ] && db_port=9999 [ -z "$dns_port" ] && dns_port=1053 - [ -z "$multiport" ] && multiport='22,80,143,194,443,465,587,853,993,995,5222,8080,8443' [ -z "$redir_mod" ] && redir_mod=纯净模式 - #检查mac地址记录 - [ ! -f "$CRASHDIR"/configs/mac ] && touch "$CRASHDIR"/configs/mac #获取本机host地址 [ -z "$host" ] && host=$(ubus call network.interface.lan status 2>&1 | grep \"address\" | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') [ -z "$host" ] && host=$(ip a 2>&1 | grep -w 'inet' | grep 'global' | grep 'lan' | grep -E ' 1(92|0|72)\.' | sed 's/.*inet.//g' | sed 's/\/[0-9][0-9].*$//g' | head -n 1) @@ -132,7 +127,7 @@ ckstatus() { #检查新手引导 if [ -z "$userguide" ]; then setconfig userguide 1 - . "$CRASHDIR"/webget.sh && userguide + . "$CRASHDIR"/menus/8_tools.sh && userguide fi #检查执行权限 [ ! -x "$CRASHDIR"/start.sh ] && chmod +x "$CRASHDIR"/start.sh @@ -146,7 +141,7 @@ ckstatus() { core_v=$(/tmp/$file -v 2>/dev/null | head -n 1 | sed 's/ linux.*//;s/.* //') [ -z "$core_v" ] && core_v=$(/tmp/$file version 2>/dev/null | grep -Eo 'version .*' | sed 's/version //') if [ -n "$core_v" ]; then - . "$CRASHDIR"/webget.sh && setcoretype && + . "$CRASHDIR"/menus/9_upgrade.sh && setcoretype && mv -f /tmp/$file "$TMPDIR"/CrashCore && tar -zcf "$BINDIR"/CrashCore.tar.gz ${tar_para} -C "$TMPDIR" CrashCore && echo -e "\033[32m内核加载完成!\033[0m " && @@ -164,7 +159,7 @@ ckstatus() { echo "-----------------------------------------------" done #检查/tmp配置文件 - for file in $(ls /tmp | grep -v [/$] | grep -v ' ' | grep -iE '.yaml$|.yml$|config.json$'); do + for file in $(ls /tmp | grep -v [/$] | grep -v ' ' | grep -iE 'config.yaml$|config.yml$|config.json$'); do tmp_file=/tmp/$file echo -e "发现内核配置文件: \033[36m/tmp/$file\033[0m " read -p "是否加载为$crashcore的配置文件?(1/0) > " res @@ -186,121 +181,7 @@ ckstatus() { echo "-----------------------------------------------" } } -#启动相关 -startover() { - echo -ne " \r" - echo -e "\033[32m服务已启动!\033[0m" - echo -e "请使用 \033[4;36mhttp://$host$hostdir\033[0m 管理内置规则" - if [ "$redir_mod" = "纯净模式" ]; then - echo "-----------------------------------------------" - echo -e "其他设备可以使用PAC配置连接:\033[4;32mhttp://$host:$db_port/ui/pac\033[0m" - echo -e "或者使用HTTP/SOCK5方式连接:IP{\033[36m$host\033[0m}端口{\033[36m$mix_port\033[0m}" - fi - return 0 -} -start_core() { - if echo "$crashcore" | grep -q 'singbox'; then - core_config="$CRASHDIR"/jsons/config.json - else - core_config="$CRASHDIR"/yamls/config.yaml - fi - echo "-----------------------------------------------" - if [ ! -s $core_config -a -s "$CRASHDIR"/configs/providers.cfg ]; then - echo -e "\033[33m没有找到${crashcore}配置文件,尝试生成providers配置文件!\033[0m" - [ "$crashcore" = singboxr ] && coretype=singbox - [ "$crashcore" = meta -o "$crashcore" = clashpre ] && coretype=clash - . "$CRASHDIR"/webget.sh && gen_${coretype}_providers - elif [ -s $core_config -o -n "$Url" -o -n "$Https" ]; then - "$CRASHDIR"/start.sh start - #设置循环检测以判定服务启动是否成功 - i=1 - while [ -z "$test" -a "$i" -lt 30 ]; do - sleep 1 - if curl --version >/dev/null 2>&1; then - test=$(curl -s -H "Authorization: Bearer $secret" http://127.0.0.1:${db_port}/configs | grep -o port) - else - test=$(wget -q --header="Authorization: Bearer $secret" -O - http://127.0.0.1:${db_port}/configs | grep -o port) - fi - i=$((i + 1)) - done - [ -n "$test" -o -n "$(pidof CrashCore)" ] && startover - else - echo -e "\033[31m没有找到${crashcore}配置文件,请先导入配置文件!\033[0m" - . "$CRASHDIR"/webget.sh && set_core_config - fi -} -start_service() { - if [ "$firewall_area" = 5 ]; then - "$CRASHDIR"/start.sh start - echo -e "\033[32m已完成防火墙设置!\033[0m" - else - start_core - fi -} -#卸载 -uninstall() { - read -p "确认卸载ShellCrash?(警告:该操作不可逆!)[1/0] > " res - if [ "$res" = '1' ]; then - #停止服务 - "$CRASHDIR"/start.sh stop 2>/dev/null - "$CRASHDIR"/start.sh cronset "clash服务" 2>/dev/null - "$CRASHDIR"/start.sh cronset "订阅链接" 2>/dev/null - "$CRASHDIR"/start.sh cronset "ShellCrash初始化" 2>/dev/null - "$CRASHDIR"/start.sh cronset "task.sh" 2>/dev/null - #移除安装目录 - if [ -n "$CRASHDIR" ] && [ "$CRASHDIR" != '/' ]; then - read -p "是否保留脚本配置及订阅文件?[1/0] > " res - if [ "$res" = '1' ]; then - mv -f "$CRASHDIR"/configs /tmp/ShellCrash/configs_bak - mv -f "$CRASHDIR"/yamls /tmp/ShellCrash/yamls_bak - mv -f "$CRASHDIR"/jsons /tmp/ShellCrash/jsons_bak - rm -rf "$CRASHDIR"/* - mv -f /tmp/ShellCrash/configs_bak "$CRASHDIR"/configs - mv -f /tmp/ShellCrash/yamls_bak "$CRASHDIR"/yamls - mv -f /tmp/ShellCrash/jsons_bak "$CRASHDIR"/jsons - else - rm -rf "$CRASHDIR" - fi - else - echo -e "\033[31m环境变量配置有误,请尝试手动移除安装目录!\033[0m" - sleep 1 - fi - #移除其他内容 - sed -i "/alias $my_alias=*/"d /etc/profile 2>/dev/null - sed -i '/alias crash=*/'d /etc/profile 2>/dev/null - sed -i '/export CRASHDIR=*/'d /etc/profile 2>/dev/null - sed -i '/export crashdir=*/'d /etc/profile 2>/dev/null - [ -w ~/.zshrc ] && { - sed -i "/alias $my_alias=*/"d ~/.zshrc - sed -i '/export CRASHDIR=*/'d ~/.zshrc - } - sed -i '/all_proxy/'d $profile - sed -i '/ALL_PROXY/'d $profile - sed -i "/启用外网访问SSH服务/d" /etc/firewall.user 2>/dev/null - sed -i '/ShellCrash初始化/'d /etc/storage/started_script.sh 2>/dev/null - sed -i '/ShellCrash初始化/'d /jffs/.asusrouter 2>/dev/null - [ "$BINDIR" != "$CRASHDIR" ] && rm -rf "$BINDIR" - rm -rf /etc/init.d/shellcrash - rm -rf /etc/systemd/system/shellcrash.service - rm -rf /usr/lib/systemd/system/shellcrash.service - rm -rf /www/clash - rm -rf /tmp/ShellCrash - rm -rf /usr/bin/crash - sed -i '/0:7890/d' /etc/passwd - userdel -r shellcrash 2>/dev/null - nvram set script_usbmount="" 2>/dev/null - nvram commit 2>/dev/null - uci delete firewall.ShellCrash 2>/dev/null - uci commit firewall 2>/dev/null - echo "-----------------------------------------------" - echo -e "\033[36m已卸载ShellCrash相关文件!有缘再会!\033[0m" - echo -e "\033[33m请手动关闭当前窗口以重置环境变量!\033[0m" - echo "-----------------------------------------------" - exit - else - echo -e "\033[31m操作已取消!\033[0m" - fi -} + #主菜单 main_menu() { ############################# @@ -324,12 +205,12 @@ main_menu() { exit ;; 1) - start_service + . "$CRASHDIR"/menus/1_start.sh && start_service exit ;; 2) checkcfg=$(cat "$CFG_PATH") - . "$CRASHDIR"/menus/2-settings.sh && settings + . "$CRASHDIR"/menus/2_settings.sh && settings if [ -n "$PID" ]; then checkcfg_new=$(cat "$CFG_PATH") [ "$checkcfg" != "$checkcfg_new" ] && checkrestart @@ -344,22 +225,22 @@ main_menu() { main_menu ;; 4) - . "$CRASHDIR"/menus/4-setboot.sh && setboot + . "$CRASHDIR"/menus/4_setboot.sh && setboot main_menu ;; 5) - . "$CRASHDIR"/menus/5-task.sh && task_menu + . "$CRASHDIR"/menus/5_task.sh && task_menu main_menu ;; 6) - . "$CRASHDIR"/menus/6-core_config.sh && set_core_config + . "$CRASHDIR"/menus/6_core_config.sh && set_core_config main_menu ;; 7) GT_CFG_PATH="$CRASHDIR"/configs/gateway.cfg touch "$GT_CFG_PATH" checkcfg=$(cat $GT_CFG_PATH) - . "$CRASHDIR"/menus/7-gateway.sh && gateway + . "$CRASHDIR"/menus/7_gateway.sh && gateway if [ -n "$PID" ]; then checkcfg_new=$(cat $GT_CFG_PATH) [ "$checkcfg" != "$checkcfg_new" ] && checkrestart @@ -367,12 +248,12 @@ main_menu() { main_menu ;; 8) - . "$CRASHDIR"/menus/8-tools.sh && tools + . "$CRASHDIR"/menus/8_tools.sh && tools main_menu ;; 9) checkcfg=$(cat "$CFG_PATH") - . "$CRASHDIR"/menus/9-upgrade.sh && upgrade + . "$CRASHDIR"/menus/9_upgrade.sh && upgrade if [ -n "$PID" ]; then checkcfg_new=$(cat "$CFG_PATH") [ "$checkcfg" != "$checkcfg_new" ] && checkrestart @@ -386,79 +267,65 @@ main_menu() { esac } -[ -z "$CRASHDIR" ] && { - echo "环境变量配置有误!正在初始化~~~" - CRASHDIR=$( - cd $(dirname $0) - pwd - ) - . "$CRASHDIR"/init.sh - sleep 1 - echo "请重启SSH窗口以完成初始化!" - exit -} - -[ -z "$1" ] && main_menu - case "$1" in --h) - echo ----------------------------------------- - echo "欢迎使用ShellCrash" - echo ----------------------------------------- - echo " -t 测试模式" - echo " -h 帮助列表" - echo " -u 卸载脚本" - echo " -i 初始化脚本" - echo " -d 测试运行" - echo ----------------------------------------- - echo " crash -s start 启动服务" - echo " crash -s stop 停止服务" - echo " 安装目录/start.sh init 开机初始化" - echo ----------------------------------------- - echo "在线求助:t.me/ShellClash" - echo "官方博客:juewuy.github.io" - echo "发布页面:github.com/juewuy/ShellCrash" - echo ----------------------------------------- + "") + main_menu ;; --t) - shtype=sh && [ -n "$(ls -l /bin/sh | grep -o dash)" ] && shtype=bash - $shtype -x "$CRASHDIR"/menu.sh + -t) + shtype=sh && [ -n "$(ls -l /bin/sh | grep -o dash)" ] && shtype=bash + $shtype -x "$CRASHDIR"/menu.sh ;; --s) - "$CRASHDIR"/start.sh $2 $3 $4 $5 $6 + -s) + "$CRASHDIR"/start.sh $2 $3 $4 $5 $6 ;; --i) - . "$CRASHDIR"/init.sh + -i) + . "$CRASHDIR"/init.sh ;; --st) - shtype=sh && [ -n "$(ls -l /bin/sh | grep -o dash)" ] && shtype=bash - $shtype -x "$CRASHDIR"/start.sh $2 $3 $4 $5 $6 + -st) + shtype=sh && [ -n "$(ls -l /bin/sh | grep -o dash)" ] && shtype=bash + $shtype -x "$CRASHDIR"/start.sh $2 $3 $4 $5 $6 ;; --d) - shtype=sh && [ -n "$(ls -l /bin/sh | grep -o dash)" ] && shtype=bash - echo -e "正在测试运行!如发现错误请截图后前往\033[32;4mt.me/ShellClash\033[0m咨询" - $shtype "$CRASHDIR"/start.sh debug >/dev/null 2>"$TMPDIR"/debug_sh_bug.log - $shtype -x "$CRASHDIR"/start.sh debug >/dev/null 2>"$TMPDIR"/debug_sh.log - echo ----------------------------------------- - cat "$TMPDIR"/debug_sh_bug.log | grep 'start\.sh' >"$TMPDIR"/sh_bug - if [ -s "$TMPDIR"/sh_bug ]; then - while read line; do - echo -e "发现错误:\033[33;4m$line\033[0m" - grep -A 1 -B 3 "$line" "$TMPDIR"/debug_sh.log - echo ----------------------------------------- - done <"$TMPDIR"/sh_bug - rm -rf "$TMPDIR"/sh_bug - echo -e "\033[32m测试完成!\033[0m完整执行记录请查看:\033[36m$TMPDIR/debug_sh.log\033[0m" - else - echo -e "\033[32m测试完成!没有发现问题,请重新启动服务~\033[0m" - rm -rf "$TMPDIR"/debug_sh.log - fi - "$CRASHDIR"/start.sh stop + -d) + shtype=sh && [ -n "$(ls -l /bin/sh | grep -o dash)" ] && shtype=bash + echo -e "正在测试运行!如发现错误请截图后前往\033[32;4mt.me/ShellClash\033[0m咨询" + $shtype "$CRASHDIR"/start.sh debug >/dev/null 2>"$TMPDIR"/debug_sh_bug.log + $shtype -x "$CRASHDIR"/start.sh debug >/dev/null 2>"$TMPDIR"/debug_sh.log + echo ----------------------------------------- + cat "$TMPDIR"/debug_sh_bug.log | grep 'start\.sh' >"$TMPDIR"/sh_bug + if [ -s "$TMPDIR"/sh_bug ]; then + while read line; do + echo -e "发现错误:\033[33;4m$line\033[0m" + grep -A 1 -B 3 "$line" "$TMPDIR"/debug_sh.log + echo ----------------------------------------- + done <"$TMPDIR"/sh_bug + rm -rf "$TMPDIR"/sh_bug + echo -e "\033[32m测试完成!\033[0m完整执行记录请查看:\033[36m$TMPDIR/debug_sh.log\033[0m" + else + echo -e "\033[32m测试完成!没有发现问题,请重新启动服务~\033[0m" + rm -rf "$TMPDIR"/debug_sh.log + fi + "$CRASHDIR"/start.sh stop ;; --u) - uninstall + -u) + . "$CRASHDIR"/menus/uninstall.sh && uninstall ;; -*) - $0 -h + *) + echo ----------------------------------------- + echo "欢迎使用ShellCrash" + echo ----------------------------------------- + echo " -t 测试模式" + echo " -h 帮助列表" + echo " -u 卸载脚本" + echo " -i 初始化脚本" + echo " -d 测试运行" + echo ----------------------------------------- + echo " crash -s start 启动服务" + echo " crash -s stop 停止服务" + echo " $CRASHDIR/start.sh init 开机初始化" + echo ----------------------------------------- + echo "在线求助:t.me/ShellClash" + echo "官方博客:juewuy.github.io" + echo "发布页面:github.com/juewuy/ShellCrash" + echo ----------------------------------------- ;; esac diff --git a/scripts/menus/1_start.sh b/scripts/menus/1_start.sh new file mode 100644 index 00000000..a2230883 --- /dev/null +++ b/scripts/menus/1_start.sh @@ -0,0 +1,54 @@ +#!/bin/sh +# Copyright (C) Juewuy + +#启动相关 +startover() { + echo -ne " \r" + echo -e "\033[32m服务已启动!\033[0m" + echo -e "请使用 \033[4;36mhttp://$host$hostdir\033[0m 管理内置规则" + if [ "$redir_mod" = "纯净模式" ]; then + echo "-----------------------------------------------" + echo -e "其他设备可以使用PAC配置连接:\033[4;32mhttp://$host:$db_port/ui/pac\033[0m" + echo -e "或者使用HTTP/SOCK5方式连接:IP{\033[36m$host\033[0m}端口{\033[36m$mix_port\033[0m}" + fi + return 0 +} +start_core() { + if echo "$crashcore" | grep -q 'singbox'; then + core_config="$CRASHDIR"/jsons/config.json + else + core_config="$CRASHDIR"/yamls/config.yaml + fi + echo "-----------------------------------------------" + if [ ! -s $core_config -a -s "$CRASHDIR"/configs/providers.cfg ]; then + echo -e "\033[33m没有找到${crashcore}配置文件,尝试生成providers配置文件!\033[0m" + [ "$crashcore" = singboxr ] && coretype=singbox + [ "$crashcore" = meta -o "$crashcore" = clashpre ] && coretype=clash + . "$CRASHDIR"/webget.sh && gen_${coretype}_providers + elif [ -s $core_config -o -n "$Url" -o -n "$Https" ]; then + "$CRASHDIR"/start.sh start + #设置循环检测以判定服务启动是否成功 + i=1 + while [ -z "$test" -a "$i" -lt 30 ]; do + sleep 1 + if curl --version >/dev/null 2>&1; then + test=$(curl -s -H "Authorization: Bearer $secret" http://127.0.0.1:${db_port}/configs | grep -o port) + else + test=$(wget -q --header="Authorization: Bearer $secret" -O - http://127.0.0.1:${db_port}/configs | grep -o port) + fi + i=$((i + 1)) + done + [ -n "$test" -o -n "$(pidof CrashCore)" ] && startover + else + echo -e "\033[31m没有找到${crashcore}配置文件,请先导入配置文件!\033[0m" + . "$CRASHDIR"/webget.sh && set_core_config + fi +} +start_service() { + if [ "$firewall_area" = 5 ]; then + "$CRASHDIR"/start.sh start + echo -e "\033[32m已完成防火墙设置!\033[0m" + else + start_core + fi +} diff --git a/scripts/menus/2-settings.sh b/scripts/menus/2_settings.sh similarity index 99% rename from scripts/menus/2-settings.sh rename to scripts/menus/2_settings.sh index 5b9fe0c8..3ca9ba94 100644 --- a/scripts/menus/2-settings.sh +++ b/scripts/menus/2_settings.sh @@ -695,6 +695,7 @@ set_adv_config() { #端口设置 [ -z "$secret" ] && secret=未设置 [ -z "$table" ] && table=100 [ -z "$authentication" ] && auth=未设置 || auth=****** + [ -z "$multiport" ] && multiport='22,80,143,194,443,465,587,853,993,995,5222,8080,8443' inputport() { read -p "请输入端口号(1-65535) > " portx . "$CRASHDIR"/menus/check_port.sh #加载测试函数 diff --git a/scripts/menus/4-setboot.sh b/scripts/menus/4_setboot.sh similarity index 100% rename from scripts/menus/4-setboot.sh rename to scripts/menus/4_setboot.sh diff --git a/scripts/menus/5-task.sh b/scripts/menus/5_task.sh similarity index 100% rename from scripts/menus/5-task.sh rename to scripts/menus/5_task.sh diff --git a/scripts/menus/6-core_config.sh b/scripts/menus/6_core_config.sh similarity index 95% rename from scripts/menus/6-core_config.sh rename to scripts/menus/6_core_config.sh index d1d1ebc2..a4bf4e2a 100644 --- a/scripts/menus/6-core_config.sh +++ b/scripts/menus/6_core_config.sh @@ -1,6 +1,9 @@ #!/bin/sh # Copyright (C) Juewuy +YAMLSDIR="$CRASHDIR"/yamls +JSONSDIR="$CRASHDIR"/jsons + #导入订阅、配置文件相关 setrules(){ #自定义规则 set_rule_type(){ @@ -43,7 +46,7 @@ setrules(){ #自定义规则 rule_group_set=$(echo $rule_group|cut -d'#' -f$num) rule_all="- ${rule_type_set},${rule_state_set},${rule_group_set}" [ -n "$(echo IP-CIDR SRC-IP-CIDR IP-CIDR6|grep "$rule_type_set")" ] && rule_all="${rule_all},no-resolve" - echo $rule_all >> $YAMLSDIR/rules.yaml + echo "$rule_all" >> "$YAMLSDIR"/rules.yaml echo "-----------------------------------------------" echo -e "\033[32m添加成功!\033[0m" fi @@ -55,8 +58,8 @@ setrules(){ #自定义规则 } del_rule_type(){ echo -e "输入对应数字即可移除相应规则:" - sed -i '/^ *$/d; /^#/d' $YAMLSDIR/rules.yaml - cat $YAMLSDIR/rules.yaml | grep -Ev '^#' | awk -F "#" '{print " "NR" "$1$2$3}' + sed -i '/^ *$/d; /^#/d' "$YAMLSDIR"/rules.yaml + cat "$YAMLSDIR"/rules.yaml | grep -Ev '^#' | awk -F "#" '{print " "NR" "$1$2$3}' echo "-----------------------------------------------" echo -e " 0 返回上级菜单" read -p "请输入对应数字 > " num @@ -64,8 +67,8 @@ setrules(){ #自定义规则 0) ;; '') ;; *) - if [ "$num" -le "$(wc -l < $YAMLSDIR/rules.yaml)" ];then - sed -i "${num}d" $YAMLSDIR/rules.yaml + if [ "$num" -le "$(wc -l < "$YAMLSDIR"/rules.yaml)" ];then + sed -i "${num}d" "$YAMLSDIR"/rules.yaml sleep 1 del_rule_type else @@ -100,7 +103,7 @@ setrules(){ #自定义规则 ;; 2) echo "-----------------------------------------------" - if [ -s $YAMLSDIR/rules.yaml ];then + if [ -s "$YAMLSDIR"/rules.yaml ];then del_rule_type else echo -e "请先添加自定义规则!" @@ -110,7 +113,7 @@ setrules(){ #自定义规则 ;; 3) read -p "确认清空全部自定义规则?(1/0) > " res - [ "$res" = "1" ] && sed -i '/^\s*[^#]/d' $YAMLSDIR/rules.yaml + [ "$res" = "1" ] && sed -i '/^\s*[^#]/d' "$YAMLSDIR"/rules.yaml setrules ;; 4) @@ -154,7 +157,7 @@ setgroups(){ #自定义clash策略组 fi set_group_add #添加自定义策略组 - cat >> $YAMLSDIR/proxy-groups.yaml <> "$YAMLSDIR"/proxy-groups.yaml </dev/null | sed "/#自定义策略组开始/,/#自定义策略组结束/d" | grep -Ev '^#' | grep -o '\- name:.*' | sed 's/#.*//' | sed 's/- name: /#/g' | tr -d '\n' | sed 's/#//')" set_group_type setgroups ;; 2) echo "-----------------------------------------------" - cat $YAMLSDIR/proxy-groups.yaml + cat "$YAMLSDIR"/proxy-groups.yaml setgroups ;; 3) read -p "确认清空全部自定义策略组?(1/0) > " res - [ "$res" = "1" ] && echo '#用于添加自定义策略组' > $YAMLSDIR/proxy-groups.yaml + [ "$res" = "1" ] && echo '#用于添加自定义策略组' > "$YAMLSDIR"/proxy-groups.yaml setgroups ;; *) @@ -259,7 +262,7 @@ setproxies(){ #自定义clash节点 rule_group_add="${rule_group_add}#${rule_group_set}" done if [ -n "$rule_group_add" ];then - echo "- {$proxy_state_set}$rule_group_add" >> $YAMLSDIR/proxies.yaml + echo "- {$proxy_state_set}$rule_group_add" >> "$YAMLSDIR"/proxies.yaml echo "-----------------------------------------------" echo -e "\033[32m添加成功!\033[0m" unset rule_group_add @@ -284,21 +287,21 @@ setproxies(){ #自定义clash节点 ;; 1) proxy_type="DOMAIN-SUFFIX DOMAIN-KEYWORD IP-CIDR SRC-IP-CIDR DST-PORT SRC-PORT GEOIP GEOSITE IP-CIDR6 DOMAIN MATCH" - proxy_group="$(cat $YAMLSDIR/proxy-groups.yaml $YAMLSDIR/config.yaml 2>/dev/null | sed "/#自定义策略组开始/,/#自定义策略组结束/d" | grep -Ev '^#' | grep -o '\- name:.*' | sed 's/#.*//' | sed 's/- name: /#/g' | tr -d '\n' | sed 's/#//')" + proxy_group="$(cat "$YAMLSDIR"/proxy-groups.yaml "$YAMLSDIR"/config.yaml 2>/dev/null | sed "/#自定义策略组开始/,/#自定义策略组结束/d" | grep -Ev '^#' | grep -o '\- name:.*' | sed 's/#.*//' | sed 's/- name: /#/g' | tr -d '\n' | sed 's/#//')" set_proxy_type setproxies ;; 2) echo "-----------------------------------------------" - sed -i '/^ *$/d' $YAMLSDIR/proxies.yaml 2>/dev/null - if [ -s $YAMLSDIR/proxies.yaml ];then + sed -i '/^ *$/d' "$YAMLSDIR"/proxies.yaml 2>/dev/null + if [ -s "$YAMLSDIR"/proxies.yaml ];then echo -e "当前已添加的自定义节点为:" - cat $YAMLSDIR/proxies.yaml | grep -Ev '^#' | awk -F '[,,}]' '{print NR, $1, $NF}' | sed 's/- {//g' + cat "$YAMLSDIR"/proxies.yaml | grep -Ev '^#' | awk -F '[,,}]' '{print NR, $1, $NF}' | sed 's/- {//g' echo "-----------------------------------------------" echo -e "\033[33m输入节点对应数字可以移除对应节点\033[0m" read -p "请输入对应数字 > " num - if [ $num -le $(cat $YAMLSDIR/proxies.yaml | grep -Ev '^#' | wc -l) ];then - sed -i "$num{/^\s*[^#]/d}" $YAMLSDIR/proxies.yaml + if [ $num -le $(cat "$YAMLSDIR"/proxies.yaml | grep -Ev '^#' | wc -l) ];then + sed -i "$num{/^\s*[^#]/d}" "$YAMLSDIR"/proxies.yaml else errornum fi @@ -310,7 +313,7 @@ setproxies(){ #自定义clash节点 ;; 3) read -p "确认清空全部自定义节点?(1/0) > " res - [ "$res" = "1" ] && sed -i '/^\s*[^#]/d' $YAMLSDIR/proxies.yaml 2>/dev/null + [ "$res" = "1" ] && sed -i '/^\s*[^#]/d' "$YAMLSDIR"/proxies.yaml 2>/dev/null setproxies ;; 4) @@ -699,12 +702,12 @@ setproviders(){ #自定义providers } set_clash_adv(){ #自定义clash高级规则 - [ ! -f $YAMLSDIR/user.yaml ] && cat > $YAMLSDIR/user.yaml < "$YAMLSDIR"/user.yaml < $YAMLSDIR/others.yaml < "$YAMLSDIR"/others.yaml < " res + if [ "$res" = '1' ]; then + #停止服务 + "$CRASHDIR"/start.sh stop 2>/dev/null + "$CRASHDIR"/start.sh cronset "clash服务" 2>/dev/null + "$CRASHDIR"/start.sh cronset "订阅链接" 2>/dev/null + "$CRASHDIR"/start.sh cronset "ShellCrash初始化" 2>/dev/null + "$CRASHDIR"/start.sh cronset "task.sh" 2>/dev/null + #移除安装目录 + if [ -n "$CRASHDIR" ] && [ "$CRASHDIR" != '/' ]; then + read -p "是否保留脚本配置及订阅文件?[1/0] > " res + if [ "$res" = '1' ]; then + mv -f "$CRASHDIR"/configs /tmp/ShellCrash/configs_bak + mv -f "$CRASHDIR"/yamls /tmp/ShellCrash/yamls_bak + mv -f "$CRASHDIR"/jsons /tmp/ShellCrash/jsons_bak + rm -rf "$CRASHDIR"/* + mv -f /tmp/ShellCrash/configs_bak "$CRASHDIR"/configs + mv -f /tmp/ShellCrash/yamls_bak "$CRASHDIR"/yamls + mv -f /tmp/ShellCrash/jsons_bak "$CRASHDIR"/jsons + else + rm -rf "$CRASHDIR" + fi + else + echo -e "\033[31m环境变量配置有误,请尝试手动移除安装目录!\033[0m" + sleep 1 + fi + #移除其他内容 + sed -i "/alias $my_alias=*/"d /etc/profile 2>/dev/null + sed -i '/alias crash=*/'d /etc/profile 2>/dev/null + sed -i '/export CRASHDIR=*/'d /etc/profile 2>/dev/null + sed -i '/export crashdir=*/'d /etc/profile 2>/dev/null + [ -w ~/.zshrc ] && { + sed -i "/alias $my_alias=*/"d ~/.zshrc + sed -i '/export CRASHDIR=*/'d ~/.zshrc + } + sed -i '/all_proxy/'d $profile + sed -i '/ALL_PROXY/'d $profile + sed -i "/启用外网访问SSH服务/d" /etc/firewall.user 2>/dev/null + sed -i '/ShellCrash初始化/'d /etc/storage/started_script.sh 2>/dev/null + sed -i '/ShellCrash初始化/'d /jffs/.asusrouter 2>/dev/null + [ "$BINDIR" != "$CRASHDIR" ] && rm -rf "$BINDIR" + rm -rf /etc/init.d/shellcrash + rm -rf /etc/systemd/system/shellcrash.service + rm -rf /usr/lib/systemd/system/shellcrash.service + rm -rf /www/clash + rm -rf /tmp/ShellCrash + rm -rf /usr/bin/crash + sed -i '/0:7890/d' /etc/passwd + userdel -r shellcrash 2>/dev/null + nvram set script_usbmount="" 2>/dev/null + nvram commit 2>/dev/null + uci delete firewall.ShellCrash 2>/dev/null + uci commit firewall 2>/dev/null + echo "-----------------------------------------------" + echo -e "\033[36m已卸载ShellCrash相关文件!有缘再会!\033[0m" + echo -e "\033[33m请手动关闭当前窗口以重置环境变量!\033[0m" + echo "-----------------------------------------------" + exit + else + echo -e "\033[31m操作已取消!\033[0m" + fi +} diff --git a/scripts/start.sh b/scripts/start.sh index e5904b76..25b2aef2 100644 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -13,6 +13,8 @@ CRASHDIR=$( #加载工具 . "$CRASHDIR"/libs/set_config.sh . "$CRASHDIR"/libs/check_cmd.sh +. "$CRASHDIR"/libs/compare.sh +. "$CRASHDIR"/libs/logger.sh . "$CRASHDIR"/starts/fw_start.sh . "$CRASHDIR"/starts/fw_stop.sh @@ -86,77 +88,7 @@ ckgeo() { #查找及下载Geo数据文件 fi } } -compare() { #对比文件 - if [ ! -f "$1" ] || [ ! -f "$2" ]; then - return 1 - elif ckcmd cmp; then - cmp -s "$1" "$2" - else - [ "$(cat "$1")" = "$(cat "$2")" ] && return 0 || return 1 - fi -} -logger() { #日志工具 - #$1日志内容$2显示颜色$3是否推送 - [ -n "$2" -a "$2" != 0 ] && echo -e "\033[$2m$1\033[0m" - log_text="$(date "+%G-%m-%d_%H:%M:%S")~$1" - echo "$log_text" >>"$TMPDIR"/ShellCrash.log - [ "$(wc -l "$TMPDIR"/ShellCrash.log | awk '{print $1}')" -gt 99 ] && sed -i '1,50d' "$TMPDIR"/ShellCrash.log - #推送工具 - webpush() { - [ -n "$(pidof CrashCore)" ] && { - [ -n "$authentication" ] && auth="$authentication@" - export https_proxy="http://${auth}127.0.0.1:$mix_port" - } - if curl --version >/dev/null 2>&1; then - curl -kfsSl -X POST --connect-timeout 3 -H "Content-Type: application/json; charset=utf-8" "$1" -d "$2" >/dev/null 2>&1 - elif wget --version >/dev/null 2>&1; then - wget -Y on -q --timeout=3 -O - --method=POST --header="Content-Type: application/json; charset=utf-8" --body-data="$2" "$1" >/dev/null 2>&1 - else - echo "找不到有效的curl或wget应用,请先安装!" - fi - } - [ -z "$3" ] && { - [ -n "$device_name" ] && log_text="$log_text($device_name)" - [ -n "$push_TG" ] && { - url="https://api.telegram.org/bot${push_TG}/sendMessage" - [ "$push_TG" = 'publictoken' ] && url='https://tgbot.jwsc.eu.org/publictoken/sendMessage' - content="{\"chat_id\":\"${chat_ID}\",\"text\":\"$log_text\"}" - webpush "$url" "$content" & - } - [ -n "$push_bark" ] && { - url="${push_bark}" - content="{\"body\":\"${log_text}\",\"title\":\"ShellCrash日志推送\",\"level\":\"passive\",\"badge\":\"1\"}" - webpush "$url" "$content" & - } - [ -n "$push_Deer" ] && { - url="https://api2.pushdeer.com/message/push" - content="{\"pushkey\":\"${push_Deer}\",\"text\":\"$log_text\"}" - webpush "$url" "$content" & - } - [ -n "$push_Po" ] && { - url="https://api.pushover.net/1/messages.json" - content="{\"token\":\"${push_Po}\",\"user\":\"${push_Po_key}\",\"title\":\"ShellCrash日志推送\",\"message\":\"$log_text\"}" - webpush "$url" "$content" & - } - [ -n "$push_PP" ] && { - url="http://www.pushplus.plus/send" - content="{\"token\":\"${push_PP}\",\"title\":\"ShellCrash日志推送\",\"content\":\"$log_text\"}" - webpush "$url" "$content" & - } - # 新增Gotify推送 - [ -n "$push_Gotify" ] && { - url="${push_Gotify}" - content="{\"title\":\"ShellCrash日志推送\",\"message\":\"$log_text\",\"priority\":5}" - webpush "$url" "$content" & - } - [ -n "$push_SynoChat" ] && { - url="${push_ChatURL}/webapi/entry.cgi?api=SYNO.Chat.External&method=chatbot&version=2&token=${push_ChatTOKEN}" - content="payload={\"text\":\"${log_text}\", \"user_ids\":[${push_ChatUSERID}]}" - webpush "$url" "$content" & - #curl -X POST "${push_ChatURL}/webapi/entry.cgi?api=SYNO.Chat.External&method=chatbot&version=2&token=${push_ChatTOKEN}" -H 'content-Type: application/json' -d "payload={\"text\":\"${log_text}\", \"user_ids\":[${push_ChatUSERID}]}" >/dev/null 2>&1 - } - } & -} + croncmd() { #定时任务工具 if [ -n "$(crontab -h 2>&1 | grep '\-l')" ]; then crontab "$1" diff --git a/scripts/starts/README.md b/scripts/starts/README.md index 4a6cf025..b7e2b4d6 100644 --- a/scripts/starts/README.md +++ b/scripts/starts/README.md @@ -1,3 +1,3 @@ -用于存放负责启动和服务相关的脚本 - +用于存放负责启动和服务相关的脚本 + 此处脚本内容不应包含任何具体中文说明,log内容的文字应当从相关lang文件调用 \ No newline at end of file