#!/bin/sh # Lucky 安装管理脚本 - 读取版本号和子目录 # 作者: 古大羊 # 日期: 2025-02-17 echo='echo -e' && [ -n "$(echo -e | grep e)" ] && echo=echo luckPathSuff='lucky.daji' download_base_url=$1 # 下载链接前缀(由第一个启动参数提供) version="" subdir="" cpucore="" # URL 解码函数 decode_url() { echo "$1" | sed 's/%/\\x/g' | while read -r line; do printf "%b\n" "$line"; done } # dir_avail 查询路径可用空间 dir_avail() { df -h $1 | awk '{ for(i=1;i<=NF;i++){ if(NR==1){ arr[i]=$i; }else{ arr[i]=arr[i]" "$i; } } } END{ for(i=1;i<=NF;i++){ print arr[i]; } }' | grep Ava | awk '{print $2}' } webget() { #参数【$1】代表下载目录,【$2】代表在线地址 #参数【$3】代表输出显示,【$4】不启用重定向 if curl --version >/dev/null 2>&1; then [ "$3" = "echooff" ] && progress='-s' || progress='-#' [ -z "$4" ] && redirect='-L' || redirect='' result=$(curl -w %{http_code} --connect-timeout 5 $progress $redirect -ko $1 $2) [ -n "$(echo $result | grep -e ^2)" ] && result="200" else if wget --version >/dev/null 2>&1; then [ "$3" = "echooff" ] && progress='-q' || progress='-q --show-progress' [ "$4" = "rediroff" ] && redirect='--max-redirect=0' || redirect='' certificate='--no-check-certificate' timeout='--timeout=3' fi [ "$3" = "echoon" ] && progress='' [ "$3" = "echooff" ] && progress='-q' wget $progress $redirect $certificate $timeout -O $1 $2 [ $? -eq 0 ] && result="200" fi } getLinuxSysType() { [ -f "/etc/storage/started_script.sh" ] && systype=Padavan && initdir='/etc/storage/started_script.sh' [ -d "/jffs" ] && systype="asusrouter" && { [ -f "/jffs/.asusrouter" ] && initdir='/jffs/.asusrouter' [ -d "/jffs/scripts" ] && initdir='/jffs/scripts/init-start' [ -z "$initdir" ] && initdir='/jffs/scripts/init-start' && mkdir -p '/jffs/scripts' } [ -f "/data/etc/crontabs/root" -a "$(dir_avail /etc)" = 0 ] && systype=mi_snapshot } installStartDaemon() { echo "设为保守模式启动" type nohup >/dev/null 2>&1 && nohup=nohup $nohup $luckydir/lucky -c "$luckydir/lucky.conf" >/dev/null 2>&1 & cronset '#lucky保守模式守护进程' "*/1 * * * * test -z \"\$(pidof lucky)\" && $luckydir/lucky -c $luckydir/lucky.conf & #lucky保守模式守护进程" } installSetInit() { #判断系统类型写入不同的启动文件 if [ -f /etc/rc.common ]; then #设为init.d方式启动 echo "设为init.d方式启动" cp -f $luckydir/scripts/luckyservice /etc/init.d/$luckPathSuff chmod 755 /etc/init.d/$luckPathSuff /etc/init.d/$luckPathSuff enable /etc/init.d/$luckPathSuff start else [ -w /etc/systemd/system ] && sysdir=/etc/systemd/system [ -w /usr/lib/systemd/system ] && sysdir=/usr/lib/systemd/system if [ -n "$sysdir" ]; then #设为systemd方式启动 echo "设为systemd方式启动" echo "sysdir:"$sysdir mv $luckydir/scripts/lucky.service $sysdir/$luckPathSuff.service sed -i "s%/etc/lucky%$luckydir%g" $sysdir/$luckPathSuff.service chmod 000 $sysdir/$luckPathSuff.service systemctl daemon-reload if [ ! $? = 0 ]; then echo "systemctl daemon-reload 出错, 转为保守模式..." installStartDaemon else systemctl enable $luckPathSuff.service systemctl start $luckPathSuff.service fi else #设为保守模式启动 installStartDaemon fi fi #华硕/Padavan额外设置,开机启动 [ -n "$initdir" ] && { echo "华硕/Padavan开机启动额外设置"$initdir sed -i '/Lucky启动/'d $initdir >/dev/null 2>&1 if [ ! -e $initdir ]; then touch $initdir echo "#!/bin/sh" >>$initdir fi sed -i '/Lucky启动/'d $initdir 2>/dev/null echo "$luckydir/lucky -c $luckydir/lucky.conf >/dev/null& #Lucky启动脚本" >>$initdir chmod +x $initdir } #小米镜像化OpenWrt额外设置 if [ "$systype" = "mi_snapshot" ]; then echo '执行小米镜像化OpenWrt额外设置' # 替换 misnap_init.sh 里的默认路径 sed -i "s|luckydir=/data/lucky.daji|luckydir=$luckydir|g" "$luckydir/scripts/misnap_init.sh" chmod 755 $luckydir/scripts/misnap_init.sh uci set firewall.Lucky=include uci set firewall.Lucky.type='script' uci set firewall.Lucky.path="$luckydir/scripts/misnap_init.sh" uci set firewall.Lucky.enabled='1' uci commit firewall fi } installSetProfile() { [ -w /opt/etc/profile ] && profile=/opt/etc/profile [ -w /jffs/configs/profile.add ] && profile=/jffs/configs/profile.add [ -w ~/.bashrc ] && profile=~/.bashrc [ -w /etc/profile ] && profile=/etc/profile if [ -n "$profile" ]; then sed -i '/alias lucky=*/'d $profile echo "alias lucky=\"$luckydir/lucky\"" >>$profile #设置快捷命令环境变量 sed -i '/export luckydir=*/'d $profile echo "export luckydir=\"$luckydir\"" >>$profile #设置快捷命令环境变量 else echo 无法写入环境变量!请检查安装权限! exit 1 fi echo 'Profile:'$profile } checkRoot() { #检查root权限 if [ "$USER" != "root" -a -z "$systype" ]; then echo 当前用户:$USER $echo "\033[31m请尽量使用root用户(不要直接使用sudo命令!)执行安装!\033[0m" echo ----------------------------------------------- read -r -p "仍要安装?可能会产生未知错误!(1/0) > " res [ "$res" != "1" ] && echo "Lukcy 安装脚本中止运行" && exit 1 fi } croncmd() { if [ -n "$(crontab -h 2>&1 | grep '\-l')" ]; then crontab $1 else crondir="$(crond -h 2>&1 | grep -oE 'Default:.*' | awk -F ":" '{print $2}')" [ ! -w "$crondir" ] && crondir="/etc/storage/cron/crontabs" [ ! -w "$crondir" ] && crondir="/var/spool/cron/crontabs" [ ! -w "$crondir" ] && crondir="/var/spool/cron" [ ! -w "$crondir" ] && echo "你的设备不支持定时任务配置" [ "$1" = "-l" ] && cat $crondir/$USER 2>/dev/null [ -f "$1" ] && cat $1 >$crondir/$USER fi } cronset() { # 参数1代表要移除的关键字,参数2代表要添加的任务语句 tmpcron=/tmp/cron_$USER croncmd -l >$tmpcron sed -i "/$1/d" $tmpcron sed -i '/^$/d' $tmpcron echo "$2" >>$tmpcron croncmd $tmpcron rm -f $tmpcron } # 获取可用的 Lucky 版本号 get_versions() { echo "正在获取可用的 Lucky 版本列表..." if curl --version >/dev/null 2>&1; then versions=$(curl -s "$download_base_url/" | sed -n 's/.*href="\.\///p' | sed -E 's/\/.*//g' | grep '^v' | sort -u) elif wget --version >/dev/null 2>&1; then versions=$(wget -qO- "$download_base_url/" | sed -n 's/.*href="\.\///p' | sed -E 's/\/.*//g' | grep '^v' | sort -u) else echo "无法获取版本列表,请检查下载地址是否正确!" exit 1 fi if [ -z "$versions" ]; then echo "未能获取可用的版本,请检查下载地址是否正确!" exit 1 fi echo "-----------------------------------------------" echo "当前可下载安装的 Lucky 版本:" echo "$versions" | awk '{print NR" "$0}' echo "-----------------------------------------------" } # 选择 Lucky 版本 select_version() { get_versions read -p "请输入对应的版本号编号 > " num version=$(echo "$versions" | awk "NR==$num") if [ -z "$version" ]; then echo -e "\033[31m输入错误,请重新选择!\033[0m" select_version fi } getcpucore() { cputype=$(uname -ms | tr ' ' '_' | tr '[A-Z]' '[a-z]') [ -n "$(echo $cputype | grep -E "linux.*armv.*")" ] && cpucore="armv5" [ -n "$(echo $cputype | grep -E "linux.*armv7.*")" ] && [ -n "$(cat /proc/cpuinfo | grep vfp)" ] && [ ! -d /jffs/clash ] && cpucore="armv7" [ -n "$(echo $cputype | grep -E "linux.*aarch64.*|linux.*armv8.*")" ] && cpucore="arm64" [ -n "$(echo $cputype | grep -E "linux.*86.*")" ] && cpucore="i386" [ -n "$(echo $cputype | grep -E "linux.*86_64.*")" ] && cpucore="x86_64" if [ -n "$(echo $cputype | grep -E "linux.*mips.*")" ]; then mipstype=$(echo -n I | hexdump -o 2>/dev/null | awk '{ print substr($2,6,1); exit}') #通过判断大小端判断mips或mipsle [ "$mipstype" = "0" ] && cpucore="mips_softfloat" || cpucore="mipsle_softfloat" fi } # 获取选定版本的子目录 get_subdirs() { echo "正在获取 $version 版本的可选子目录..." echo "万吉(全能):包含所有模块,全功能版本" echo "Lucky(适中):比万吉少FileBrowser模块、CorazaWAF模块以及不支持GRPC反向代理(>2.14.1)" if curl --version >/dev/null 2>&1; then subdirs=$(curl -s "$download_base_url/$version/" | sed -n 's/.*href="\.\///p' | sed -E 's/\/.*//g' | grep '^[0-9].*_' | sort -u | grep -v '^$') elif wget --version >/dev/null 2>&1; then subdirs=$(wget -qO- "$download_base_url/$version/" | sed -n 's/.*href="\.\///p' | sed -E 's/\/.*//g' | grep '^[0-9].*_' | sort -u | grep -v '^$') else echo "无法获取子目录列表,请检查下载地址是否正确!" exit 1 fi if [ -z "$subdirs" ]; then echo "未能获取可用的子目录,请检查下载地址是否正确!" exit 1 fi # 对获取到的子目录进行 URL 解码 decoded_subdirs=$(decode_url "$subdirs") echo "-----------------------------------------------" echo "可用的子版本:" echo "$decoded_subdirs" | grep -v '^$' | awk '{print NR" "$0}' echo "-----------------------------------------------" # 更新 subdirs 变量 subdirs=$(echo "$decoded_subdirs" | grep -v '^$') } # 选择子目录 select_subdir() { get_subdirs read -p "请输入对应的子目录编号 > " num subdir=$(echo "$subdirs" | awk "NR==$num") if [ -z "$subdir" ]; then echo -e "\033[31m输入错误,请重新选择!\033[0m" select_subdir fi } checkdir() { if [ -n "$systype" ]; then [ "$systype" = "Padavan" ] && dir=/etc/storage [ "$systype" = "asusrouter" ] && dir=/jffs [ "$systype" = "mi_snapshot" ] && dir=/data echo "当前处于路由器系统环境:"+$systype fi } setdir() { echo ----------------------------------------------- $echo "\033[33m安装lucky至少需要预留约10MB的磁盘空间\033[0m" $echo " 1 在\033[32m/etc\033[0m 目录下安装(适合root用户)" $echo " 2 在\033[32m/usr/share\033[0m目录下安装(适合Linux设备)" $echo " 3 在\033[32m当前用户目录\033[0m下安装(适合非root用户)" $echo " 4 在\033[32m/data\033[0m目录(小米官方路由系统)" $echo " 5 在\033[32m/data/userdisk \033[0m目录(小米官方路由系统.万兆)" $echo " 6 在\033[32m/jffs\033[0m目录(华硕/梅林)" $echo " 7 在\033[32m/etc/storage目录\033[0m(Padavan)" $echo " 8 手动设置安装目录" if [ -n "$dir" ];then $echo " 5 在\033[32m""$dir""\033[0m(当前路由器环境推荐目录下安装)" fi $echo " 0 退出安装" echo ----------------------------------------------- read -p "请输入相应数字 > " num #设置目录 if [ -z $num ];then echo 安装已取消 exit 1; elif [ "$num" = "1" ];then dir=/etc elif [ "$num" = "2" ];then dir=/usr/share elif [ "$num" = "3" ];then dir=~/.local/share elif [ "$num" = "4" ];then dir=/data elif [ "$num" = "5" ];then dir=/data/userdisk elif [ "$num" = "6" ];then dir=/jffs elif [ "$num" = "7" ];then dir=/jffs elif [ "$num" = "8" ];then echo ----------------------------------------------- echo '可用路径 剩余空间:' df -h | awk '{print $6,$4}'| sed 1d echo '路径是必须带 / 的格式,注意写入虚拟内存(/tmp,/opt,/sys...)的文件会在重启后消失!!!' read -p "请输入自定义路径 > " dir if [ -z "$dir" ];then $echo "\033[31m路径错误!请重新设置!\033[0m" setdir fi elif [ "$num" = "5" ];then echo "" else echo 安装已取消!!! exit 1; fi if [ ! -w $dir ]; then $echo "\033[31m没有$dir目录写入权限!请重新设置!\033[0m" && sleep 1 && setdir else $echo "目标目录\033[32m$dir\033[0m空间剩余:$(dir_avail $dir)" read -p "确认安装?(1/0) > " res if [ -z $res ]; then echo 安装已取消 exit 1 elif [ "$res" = "1" ]; then luckydir=$dir/$luckPathSuff echo "luckdir:"$luckydir else echo 安装已取消 exit 1 fi fi } # 生成最终下载链接 getTargetFileURL() { echo "正在获取 $download_base_url/$version/$subdir/ 目录下的文件列表..." # 获取文件列表并筛选 if curl --version >/dev/null 2>&1; then file_list=$(curl -s "$download_base_url/$version/$subdir/" | sed -n 's/.*href="\([^"]*\)".*/\1/p' | grep -iE 'tar\.gz' | grep "$cpucore" | grep "Linux" | sort -u) elif wget --version >/dev/null 2>&1; then file_list=$(wget -qO- "$download_base_url/$version/$subdir/" | sed -n 's/.*href="\([^"]*\)".*/\1/p' | grep -iE 'tar\.gz' | grep "$cpucore" | grep "Linux" | sort -u) else echo "无法获取文件列表,请检查下载地址是否正确!" exit 1 fi # 过滤掉空行 file_list=$(echo "$file_list" | grep -v '^$') if [ -z "$file_list" ]; then echo "未找到符合当前架构 ($cpucore) 的安装包 (tar.gz)!请检查目录结构!" exit 1 fi # 直接判断安装包数量 file_count=$(echo "$file_list" | wc -l) if [ "$file_count" -eq 1 ]; then selected_file=$(echo "$file_list") echo "自动选择唯一安装包: $selected_file" else echo "-----------------------------------------------" echo "可用的安装包文件:" echo "$file_list" | awk '{print NR" "$0}' echo "-----------------------------------------------" # 选择安装包 read -p "请输入对应的文件编号 > " num selected_file=$(echo "$file_list" | awk "NR==$num") if [ -z "$selected_file" ]; then echo -e "\033[31m输入错误,请重新选择!\033[0m" getTargetFileURL return fi fi # 拼接下载链接 download_url="$download_base_url/$version/$subdir/$selected_file" echo "目标文件下载链接: $download_url" } # 下载文件 webget() { if curl --version >/dev/null 2>&1; then result=$(curl -w %{http_code} --connect-timeout 5 -L -o $1 $2) [ -n "$(echo $result | grep -e ^2)" ] && result="200" else if wget --version >/dev/null 2>&1; then wget -q --show-progress --no-check-certificate --timeout=3 -O $1 $2 fi [ $? -eq 0 ] && result="200" fi } # 下载并解压文件 getFilesFromNetwork() { webget /tmp/lucky.tar.gz $download_url [ "$result" != "200" ] && echo "文件下载失败,请检查下载地址是否正确!" && exit 1 echo ----------------------------------------------- echo "开始解压文件!" mkdir -p $luckydir >/dev/null tar -zxvf '/tmp/lucky.tar.gz' -C $luckydir/ [ $? -ne 0 ] && echo "文件解压失败!" && rm -rf /tmp/lucky.tar.gz && exit 1 echo "已解压到 $luckydir" chmod +x $luckydir/lucky chmod +x $luckydir/scripts/* rm -rf /tmp/lucky.tar.gz } # 交互式菜单 luckyshMenu() { echo "*************************************************" echo "** 欢迎使用 **" echo "** Lucky 管理脚本 **" echo "** by 古大羊 **" echo "** 2025.02.17 **" echo "*************************************************" echo -e " 1 \033[32m安装\033[0m Lucky" echo -e " 2 \033[32m卸载\033[0m Lucky" echo ----------------------------------------------- echo -e " 0 \033[0m退出脚本\033[0m" read -p "请输入对应数字 > " num case "$num" in 0) echo "退出脚本" exit 0 ;; 1) echo "安装 Lucky..." install ;; 2) echo "卸载 Lucky..." uninstall ;; *) echo -e "\033[31m请输入正确的数字!\033[0m" luckyshMenu ;; esac } downloadLucky(){ echo "正在下载 Lucky 安装包..." webget /tmp/lucky.tar.gz $download_url if [ "$result" != "200" ]; then echo "文件下载失败, 请检查下载地址是否正确!" exit 1 fi echo "Lucky 安装包下载完成!" } extractLucky(){ echo "正在检查可用磁盘空间..." # 获取 tar.gz 文件大小(单位:字节) tar_file_size=$(stat -c %s /tmp/lucky.tar.gz) # 确保安装目录存在,若不存在,则使用其父目录 if [ ! -d "$luckydir" ]; then luckydir_parent=$(dirname "$luckydir") else luckydir_parent="$luckydir" fi # 获取安装目录所在的挂载点 mount_point=$(df "$luckydir_parent" 2>/dev/null | awk 'NR==2 {print $NF}') if [ -z "$mount_point" ]; then echo "错误:无法确定 $luckydir 的挂载点,请检查路径是否正确。" exit 1 fi # 获取该挂载点的可用空间(单位:字节),兼容 BusyBox 版本 df available_space=$(( $(df -k "$mount_point" | awk 'NR==2 {print $4}') * 1024 )) # 计算解压后可能的大小(假设解压后是压缩包的 1.5 倍) estimated_size=$(( tar_file_size * 3 / 2 )) echo "压缩包大小: $tar_file_size 字节" echo "可用空间: $available_space 字节" echo "预估解压后大小: $estimated_size 字节" # 判断空间是否足够 if [ "$available_space" -lt "$estimated_size" ]; then echo "磁盘空间不足!" echo "至少需要 $estimated_size 字节可用空间,但当前仅有 $available_space 字节。" exit 1 fi echo "磁盘空间检查通过,开始解压文件..." mkdir -p "$luckydir" > /dev/null tar -zxvf '/tmp/lucky.tar.gz' -C "$luckydir/" if [ $? -ne 0 ]; then echo "文件解压失败!" rm -rf /tmp/lucky.tar.gz exit 1 fi echo "已解压到 $luckydir" chmod +x "$luckydir/lucky" chmod +x "$luckydir/scripts/"* rm -rf /tmp/lucky.tar.gz } # 安装流程 install() { if [ -z "$download_base_url" ]; then echo -e "\033[31m错误: 未提供下载地址前缀!\033[0m" echo "使用方法: bash $0 <下载地址前缀>" exit 1 fi select_version select_subdir getTargetFileURL setdir #设置安装路径 downloadLucky # 下载 Lucky 安装包 extractLucky # 检查空间并解压安装包 installSetProfile #设置环境变量 installSetInit #设置开机启动 echo "Lucky 安装完成!" } uninstallgetdir() { [ -w /opt/etc/profile ] && profile=/opt/etc/profile [ -w /jffs/configs/profile.add ] && profile=/jffs/configs/profile.add [ -w ~/.bashrc ] && profile=~/.bashrc [ -w /etc/profile ] && profile=/etc/profile luckydir=$(cat $profile | grep luckydir | awk -F "\"" '{print $2}') if [ -n "$luckydir" ]; then return fi if [ -n "$systype" ]; then [ "$systype" = "Padavan" ] && dir=/etc/storage [ "$systype" = "asusrouter" ] && dir=/jffs [ "$systype" = "mi_snapshot" ] && dir=/data luckydir=$dir/$luckPathSuff fi } # 卸载流程 uninstall() { uninstallgetdir #获取lucky安装路径 systemctl disable $luckPathSuff.service >/dev/null 2>&1 & systemctl stop $luckPathSuff.service >/dev/null 2>&1 & #结束进程 PID=$(pidof lucky) && [ -n "$PID" ] && kill -9 $PID >/dev/null 2>&1 if [ -z $luckydir ]; then echo "找不到lucky文件夹,卸载中止" exit 0 fi echo "删除 "$luckydir" 所有文件" read -p "确认删除?"$luckydir"所有文件(1/0) > " res if [ -z $res ]; then echo 卸载已取消 exit 1 elif [ "$res" = "1" ]; then rm -rf $luckydir else echo 卸载已取消 exit 1 fi [ -n "$initdir" ] && { #删除开机启动 sed -i '/Lucky启动/'d $initdir 2>/dev/null } if [ "$systype" = "mi_snapshot" ]; then ##删除开机启动 uci del firewall.Lucky uci commit firewall fi /etc/init.d/$luckPathSuff stop >/dev/null 2>&1 rm -rf /etc/init.d/$luckPathSuff sed -i '/alias lucky=*/'d $profile sed -i '/export luckydir=*/'d $profile cronset '#lucky保守模式守护进程' #删除保守模式定时 } #获取CPU结构 getcpucore if [ -z "$cpucore" ]; then echo '未能正确识别当前系统CPU架构,请与作者联系' exit 1 fi echo "当前CPU架构:"$cpucore #判断linux环境, $systype ,$initdir getLinuxSysType [ -n "$initdir" ] && echo 'initdir:'$initdir [ -n "$systype" ] && echo '当前处于路由器运行环境[' $systype']!' # 运行菜单 luckyshMenu