{"id":51,"date":"2026-05-09T22:31:30","date_gmt":"2026-05-09T14:31:30","guid":{"rendered":"https:\/\/hi.tionmon.de\/?p=51"},"modified":"2026-05-10T00:17:32","modified_gmt":"2026-05-09T16:17:32","slug":"51","status":"publish","type":"post","link":"https:\/\/hi.tionmon.de\/?p=51","title":{"rendered":"\u8f7b\u91cf\u7ea7 sing-box \u6d41\u91cf\u5206\u6d41\u7ba1\u7406\u5668"},"content":{"rendered":"<article class=\"blog-post\">\n<h1>SBRoute \u2014 \u8f7b\u91cf\u7ea7 sing-box \u6d41\u91cf\u5206\u6d41\u7ba1\u7406\u5668<\/h1>\n<p>\u719f\u6089 <code>sing-box<\/code> \u7684\u670b\u53cb\u90fd\u77e5\u9053\uff0c\u5b83\u7684\u529f\u80fd\u6781\u5176\u5f3a\u5927\uff0c\u4f46\u5168 JSON \u683c\u5f0f\u7684\u914d\u7f6e\u6587\u4ef6\u5bf9\u4e8e\u7ecf\u5e38\u9700\u8981\u4fee\u6539\u8282\u70b9\u548c\u8def\u7531\u89c4\u5219\u7684\u7528\u6237\u6765\u8bf4\uff0c\u624b\u5199\u4f53\u9a8c\u5e76\u4e0d\u53cb\u597d\u3002\u4e3a\u4e86\u89e3\u51b3\u8fd9\u4e2a\u75db\u70b9\uff0c\u6211\u7f16\u5199\/\u6574\u7406\u4e86 <strong>SBRoute<\/strong> \u2014\u2014 \u4e00\u4e2a\u9002\u7528\u4e8e Debian \u7cfb\u7edf\u7684\u8f7b\u91cf\u7ea7 VPS \u6d41\u91cf\u5206\u6d41\u7ba1\u7406\u811a\u672c\u3002<\/p>\n<p>\u8fd9\u7bc7\u6587\u7ae0\u5c06\u5e26\u4f60\u6df1\u5165\u62c6\u89e3 <code>sbroute.sh<\/code> \u7684\u5e95\u5c42\u903b\u8f91\uff0c\u770b\u770b\u5b83\u662f\u5982\u4f55\u7528\u533a\u533a\u4e00\u5343\u591a\u884c Shell \u4ee3\u7801\uff0c\u4f18\u96c5\u5730\u7ba1\u7406 <code>sing-box<\/code> \u590d\u6742\u8def\u7531\u7684\u3002<\/p>\n<h2>\ud83c\udf1f \u6838\u5fc3\u8bbe\u8ba1\u7406\u5ff5<\/h2>\n<p>SBRoute \u7684\u6838\u5fc3\u8bbe\u8ba1\u601d\u60f3\u662f<strong>\u201c\u914d\u7f6e\u788e\u7247\u5316\u4e0e\u52a8\u6001\u7f16\u8bd1\u201d<\/strong>\u548c<strong>\u201c\u670d\u52a1\u9694\u79bb\u201d<\/strong>\u3002<\/p>\n<ol>\n<li><strong>\u670d\u52a1\u9694\u79bb<\/strong>\uff1a\u811a\u672c\u5e76\u6ca1\u6709\u53bb\u52a8\u7cfb\u7edf\u539f\u6709\u7684 <code>sing-box<\/code> \u670d\u52a1\uff0c\u800c\u662f\u521b\u5efa\u4e86\u4e00\u4e2a\u5b8c\u5168\u72ec\u7acb\u7684 Systemd \u670d\u52a1 <code>sbroute.service<\/code>\u3002\u8fd9\u610f\u5473\u4f60\u53ef\u4ee5\u628a\u5b83\u5f53\u4f5c\u4e00\u4e2a\u65c1\u8def\u5206\u6d41\u5de5\u5177\uff0c\u6216\u8005\u4e0e\u73b0\u6709\u7684\u670d\u52a1\u5171\u5b58\uff0c\u4e92\u4e0d\u5e72\u6270\u3002<\/li>\n<li><strong>\u788e\u7247\u5316\u914d\u7f6e<\/strong>\uff1a\u629b\u5f03\u4e86\u7ef4\u62a4\u5355\u4e00\u5e9e\u5927 <code>config.json<\/code> \u7684\u505a\u6cd5\u3002\u811a\u672c\u5728 <code>\/etc\/sbroute\/<\/code> \u76ee\u5f55\u4e0b\u7ef4\u62a4\u4e86\u56db\u4e2a\u72ec\u7acb\u7684\u5c0f\u578b JSON \u6587\u4ef6\uff1a\n<ul>\n<li><code>settings.json<\/code>\uff1a\u5168\u5c40\u8bbe\u7f6e\uff08DNS\u6a21\u5f0f\u3001\u9ed8\u8ba4\u51fa\u7ad9\u7b49\uff09<\/li>\n<li><code>outbounds.json<\/code>\uff1a\u5b58\u50a8\u6240\u6709\u8282\u70b9\uff08Shadowsocks, VLESS, VMess \u7b49\uff09<\/li>\n<li><code>routes.json<\/code>\uff1a\u5b58\u50a8\u5206\u6d41\u89c4\u5219\uff08\u6309\u57df\u540d\u3001IP\u3001\u8fdb\u7a0b\u7b49\uff09<\/li>\n<li><code>rulesets.json<\/code>\uff1a\u7ba1\u7406\u5916\u90e8\u7684 Rule Set\uff08\u5982 GeoIP\u3001GeoSite \u89c4\u5219\u96c6\uff09<\/li>\n<\/ul>\n<\/li>\n<li><strong>\u52a8\u6001\u7f16\u8bd1 (<code>generate_config<\/code>)<\/strong>\uff1a\u6bcf\u6b21\u5e94\u7528\u914d\u7f6e\u65f6\uff0c\u811a\u672c\u4f1a\u5229\u7528 <code>jq<\/code> \u5de5\u5177\uff0c\u5c06\u4e0a\u8ff0\u56db\u4e2a\u6587\u4ef6\u52a8\u6001\u62fc\u88c5\uff0c\u6700\u7ec8\u201c\u7f16\u8bd1\u201d\u51fa\u7b26\u5408 <code>sing-box<\/code> \u6807\u51c6\u7684\u5b8c\u6574 <code>config.json<\/code>\u3002<\/li>\n<\/ol>\n<h2>\ud83d\ude80 \u6838\u5fc3\u529f\u80fd\u4ee3\u7801\u62c6\u89e3<\/h2>\n<h3>1. \u6781\u5176\u4fbf\u5229\u7684\u5206\u4eab\u94fe\u63a5\u89e3\u6790 (<code>_parse_vless_url<\/code>, <code>_parse_ss_url<\/code>)<\/h3>\n<p>\u7ed9\u547d\u4ee4\u884c\u5de5\u5177\u6dfb\u52a0\u8282\u70b9\u6700\u70e6\u7684\u5c31\u662f\u6328\u4e2a\u8f93\u5165\u53c2\u6570\u3002SBRoute \u5b9e\u73b0\u4e86\u76f4\u63a5\u89e3\u6790 <code>ss:\/\/<\/code> \u548c <code>vless:\/\/<\/code> \u5206\u4eab\u94fe\u63a5\u7684\u529f\u80fd\uff01<\/p>\n<p>\u5728\u4ee3\u7801\u4e2d\uff0c<code>_parse_vless_url<\/code> \u51fd\u6570\u901a\u8fc7\u5b57\u7b26\u4e32\u622a\u53d6\u548c\u89e3\u7801\uff0c\u7cbe\u5999\u5730\u5c06\u5206\u4eab\u94fe\u63a5\u62c6\u89e3\uff1a<\/p>\n<div class=\"xun-code-wrapper\">\n<div class=\"xun-code-header\">\n<div class=\"xun-code-header-left\"><span class=\"xun-code-dots\"><span><\/span><span><\/span><span><\/span><\/span><\/div>\n<div class=\"xun-code-header-right\"><span class=\"xun-code-lang\">Bash\/Shell<\/span><button type=\"button\" class=\"xun-code-copy\" title=\"\u590d\u5236\u4ee3\u7801\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"><\/path><\/svg><\/button><\/div>\n<\/div>\n<pre class=\"xun-code-block line-numbers\" ><code class=\"language-bash\"># \u63d0\u53d6 uuid@server:port\r\nlocal uuid=&quot;${body%%@*}&quot;\r\nlocal server_part=&quot;${body##*@}&quot;\r\n\r\n# \u89e3\u6790\u590d\u6742\u7684 query \u53c2\u6570 (\u4f20\u8f93\u5c42\u3001TLS\u3001Reality \u7b49)\r\n_parse_query &quot;$query&quot;\r\nlocal security=&quot;${params[security]:-}&quot;\r\nlocal sni=&quot;${params[sni]:-}&quot;\r\nlocal fp=&quot;${params[fp]:-}&quot;\r\nlocal pbk=&quot;${params[pbk]:-}&quot;\r\n# ... \u7ed3\u5408 jq \u52a8\u6001\u751f\u6210 VLESS outbounds JSON \u5bf9\u8c61<\/code><\/pre>\n<\/div>\n<p>\u6709\u4e86\u5b83\uff0c\u4f60\u53ef\u4ee5\u76f4\u63a5\u8fd0\u884c <code>sbroute out url \"vless:\/\/...\"<\/code>\uff0c\u811a\u672c\u4f1a\u81ea\u52a8\u5b8c\u6210\u6240\u6709\u7684 JSON \u7ec4\u88c5\u3002<\/p>\n<h3>2. \u5f3a\u5927\u7684\u8def\u7531\u89c4\u5219\u7ec4\u5408 (<code>route_add<\/code>)<\/h3>\n<p>\u5728\u8def\u7531\u90e8\u5206\uff0c\u811a\u672c\u63d0\u4f9b\u4e86\u582a\u6bd4\u4e13\u4e1a GUI \u5ba2\u6237\u7aef\u7684\u7075\u6d3b\u6027\u3002\u652f\u6301\u901a\u8fc7\u4f20\u53c2\u7684\u65b9\u5f0f\u7ec4\u5408\u89c4\u5219\u3002\u5c24\u5176\u662f\u5bf9 <code>GeoIP<\/code> \u548c <code>GeoSite<\/code> \u7684\u652f\u6301\u975e\u5e38\u667a\u80fd\uff1a<\/p>\n<div class=\"xun-code-wrapper\">\n<div class=\"xun-code-header\">\n<div class=\"xun-code-header-left\"><span class=\"xun-code-dots\"><span><\/span><span><\/span><span><\/span><\/span><\/div>\n<div class=\"xun-code-header-right\"><span class=\"xun-code-lang\">Bash\/Shell<\/span><button type=\"button\" class=\"xun-code-copy\" title=\"\u590d\u5236\u4ee3\u7801\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"><\/path><\/svg><\/button><\/div>\n<\/div>\n<pre class=\"xun-code-block line-numbers\" ><code class=\"language-bash\">--geoip)\r\n    shift;\r\n    # \u81ea\u52a8\u8865\u5145 srs \u683c\u5f0f\u7684\u89c4\u5219\u96c6\u4e0b\u8f7d\u94fe\u63a5\r\n    local geo_tag=&quot;geoip-${_geo_name}&quot;\r\n    local geo_url=&quot;https:\/\/raw.githubusercontent.com\/SagerNet\/sing-geoip\/rule-set\/geoip-${_geo_name}.srs&quot;\r\n    _add_ruleset_if_missing &quot;$geo_tag&quot; &quot;$geo_url&quot; &quot;binary&quot;\r\n    rule=$(echo &quot;$rule&quot; | jq --arg rs &quot;$geo_tag&quot; &#039;if .rule_set then .rule_set += [$rs] else . + {rule_set:[$rs]} end&#039;)\r\n    ;;<\/code><\/pre>\n<\/div>\n<p>\u5f53\u4f60\u8f93\u5165 <code>--geoip cn<\/code> \u65f6\uff0c\u811a\u672c\u4e0d\u4ec5\u4f1a\u6dfb\u52a0\u8def\u7531\u89c4\u5219\uff0c\u8fd8\u4f1a\u81ea\u52a8\u5728 <code>rulesets.json<\/code> \u4e2d\u914d\u7f6e\u597d\u5bf9\u5e94\u7684 <code>SagerNet\/sing-geoip<\/code> \u8fdc\u7a0b\u89c4\u5219\u96c6\uff0c\u975e\u5e38\u7701\u5fc3\u3002<\/p>\n<h3>3. \u914d\u7f6e\u751f\u6210\u5f15\u64ce (<code>generate_config<\/code>)<\/h3>\n<p>\u8fd9\u662f\u6574\u4e2a\u811a\u672c\u7684\u7075\u9b42\u51fd\u6570\u3002\u5b83\u4f7f\u7528 <code>jq<\/code> \u7684\u5f3a\u608d\u80fd\u529b\uff0c\u5c06\u5404\u4e2a\u914d\u7f6e\u5757\u7c98\u5408\u5728\u4e00\u8d77\u3002<\/p>\n<div class=\"xun-code-wrapper\">\n<div class=\"xun-code-header\">\n<div class=\"xun-code-header-left\"><span class=\"xun-code-dots\"><span><\/span><span><\/span><span><\/span><\/span><\/div>\n<div class=\"xun-code-header-right\"><span class=\"xun-code-lang\">Bash\/Shell<\/span><button type=\"button\" class=\"xun-code-copy\" title=\"\u590d\u5236\u4ee3\u7801\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"><\/path><\/svg><\/button><\/div>\n<\/div>\n<pre class=\"xun-code-block line-numbers\" ><code class=\"language-bash\"># \u7ec4\u88c5\u5b8c\u6574\u914d\u7f6e\r\nlocal config\r\nconfig=$(jq -n \\\r\n    --argjson dns &quot;$dns_config&quot; \\\r\n    --argjson inb &quot;$inbounds&quot; \\\r\n    --argjson out &quot;$all_outs&quot; \\\r\n    --argjson rules &quot;$all_rules&quot; \\\r\n    --argjson rsets &quot;$rule_sets&quot; \\\r\n    --arg final &quot;$default_out&quot; \\\r\n    &#039;{\r\n        log:{level:&quot;warn&quot;,timestamp:true},\r\n        dns:$dns,\r\n        inbounds:$inb,\r\n        outbounds:$out,\r\n        route:{\r\n            rules:$rules,\r\n            rule_set:$rsets,\r\n            final:$final,\r\n            auto_detect_interface:true,\r\n            default_domain_resolver:&quot;google-dns&quot;\r\n        }\r\n    }&#039;)<\/code><\/pre>\n<\/div>\n<p>\u901a\u8fc7\u8fd9\u79cd\u65b9\u5f0f\uff0c\u811a\u672c\u5f7b\u5e95\u9694\u79bb\u4e86\u4eba\u7c7b\u8f93\u5165\u548c\u673a\u5668\u6267\u884c\u7684\u9e3f\u6c9f\u3002\u65e0\u8bba\u4f60\u600e\u4e48\u6298\u817e\u8def\u7531\u548c\u8282\u70b9\uff0c\u6700\u7ec8\u751f\u6210\u7684 <code>config.json<\/code> \u6c38\u8fdc\u662f\u4e25\u683c\u7b26\u5408\u89c4\u8303\u7684\u3002<\/p>\n<h3>4. TUI \u4ea4\u4e92\u5f0f\u83dc\u5355\u4e0e CLI \u5b8c\u7f8e\u878d\u5408<\/h3>\n<p>\u811a\u672c\u63d0\u4f9b\u4e86\u4e00\u4e2a\u7f8e\u89c2\u7684\u547d\u4ee4\u884c UI\u3002\u5982\u679c\u4f60\u76f4\u63a5\u6267\u884c <code>sbroute<\/code>\uff08\u4e0d\u5e26\u4efb\u4f55\u53c2\u6570\uff09\uff0c\u5b83\u4f1a\u8fdb\u5165 <code>interactive_menu()<\/code> \u51fd\u6570\uff0c\u6e32\u67d3\u4e00\u4e2a\u5f69\u8272\u63a7\u5236\u53f0\u83dc\u5355\u3002<br \/>\n\u4f46\u5982\u679c\u4f60\u559c\u6b22\u811a\u672c\u5316\u64cd\u4f5c\uff0c\u5b83\u540c\u6837\u652f\u6301\u7c7b\u4f3c\u4e8e <code>kubectl<\/code> \u6216 <code>git<\/code> \u7684\u5b50\u547d\u4ee4\u6a21\u5f0f\uff0c\u975e\u5e38\u9002\u5408\u96c6\u6210\u5230\u81ea\u52a8\u5316\u7684\u90e8\u7f72\u6d41\u6c34\u7ebf\u4e2d\u3002<\/p>\n<h2>\ud83d\udca1 \u5178\u578b\u4f7f\u7528\u573a\u666f\u793a\u4f8b<\/h2>\n<p>\u60f3\u8981\u5411\u8bfb\u8005\u5c55\u793a\u5b83\u7684\u4fbf\u5229\u6027\uff0c\u770b\u8fd9\u51e0\u4e2a\u5178\u578b\u7684\u64cd\u4f5c\u5c31\u660e\u767d\u4e86\uff1a<\/p>\n<p><strong>\u573a\u666f\u4e00\uff1a\u6dfb\u52a0\u4e00\u4e2a VLESS \u8282\u70b9\u5e76\u4f5c\u4e3a\u9ed8\u8ba4\u51fa\u53e3<\/strong><\/p>\n<div class=\"xun-code-wrapper\">\n<div class=\"xun-code-header\">\n<div class=\"xun-code-header-left\"><span class=\"xun-code-dots\"><span><\/span><span><\/span><span><\/span><\/span><\/div>\n<div class=\"xun-code-header-right\"><span class=\"xun-code-lang\">Bash\/Shell<\/span><button type=\"button\" class=\"xun-code-copy\" title=\"\u590d\u5236\u4ee3\u7801\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"><\/path><\/svg><\/button><\/div>\n<\/div>\n<pre class=\"xun-code-block line-numbers\" ><code class=\"language-bash\"># \u76f4\u63a5\u7c98\u8d34\u5206\u4eab\u94fe\u63a5\r\nsbroute out add url &quot;vless:\/\/xxx@xxx.com:443?security=reality...&quot;\r\n# \u5047\u8bbe\u751f\u6210\u7684 tag \u53eb node1\uff0c\u8bbe\u7f6e\u4e3a\u9ed8\u8ba4\u51fa\u7ad9\r\nsbroute route default node1<\/code><\/pre>\n<\/div>\n<p><strong>\u573a\u666f\u4e8c\uff1a\u8bbe\u7f6e\u56fd\u5185\u5916\u5206\u6d41<\/strong><\/p>\n<div class=\"xun-code-wrapper\">\n<div class=\"xun-code-header\">\n<div class=\"xun-code-header-left\"><span class=\"xun-code-dots\"><span><\/span><span><\/span><span><\/span><\/span><\/div>\n<div class=\"xun-code-header-right\"><span class=\"xun-code-lang\">Bash\/Shell<\/span><button type=\"button\" class=\"xun-code-copy\" title=\"\u590d\u5236\u4ee3\u7801\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"><\/path><\/svg><\/button><\/div>\n<\/div>\n<pre class=\"xun-code-block line-numbers\" ><code class=\"language-bash\"># \u4e00\u952e\u5e94\u7528\u56fd\u5185\u76f4\u8fde\u9884\u8bbe\uff08\u56fd\u5185 IP \u548c\u57df\u540d\u76f4\u8fde\uff0c\u5176\u4f59\u8d70\u9ed8\u8ba4\u8282\u70b9\uff09\r\nsbroute preset cn-direct<\/code><\/pre>\n<\/div>\n<p><strong>\u573a\u666f\u4e09\uff1a\u7279\u5b9a\u57df\u540d\u6216 IP \u8d70\u76f4\u8fde\u6216\u62e6\u622a<\/strong><\/p>\n<div class=\"xun-code-wrapper\">\n<div class=\"xun-code-header\">\n<div class=\"xun-code-header-left\"><span class=\"xun-code-dots\"><span><\/span><span><\/span><span><\/span><\/span><\/div>\n<div class=\"xun-code-header-right\"><span class=\"xun-code-lang\">Bash\/Shell<\/span><button type=\"button\" class=\"xun-code-copy\" title=\"\u590d\u5236\u4ee3\u7801\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"><\/path><\/svg><\/button><\/div>\n<\/div>\n<pre class=\"xun-code-block line-numbers\" ><code class=\"language-bash\"># \u62e6\u622a\u6240\u6709\u5e7f\u544a\r\nsbroute preset ads-block\r\n\r\n# \u8ba9\u6307\u5b9a\u7684\u57df\u540d\u548c IP \u8d70\u76f4\u8fde (direct)\r\nsbroute route add direct --domain openai.com --ip-cidr 8.8.8.8\/32<\/code><\/pre>\n<\/div>\n<p>\u6700\u540e\uff0c\u8fd0\u884c <code>sbroute apply<\/code>\uff0c\u811a\u672c\u5c31\u4f1a\u81ea\u52a8\u5408\u5e76\u914d\u7f6e\u3001\u6821\u9a8c\u8bed\u6cd5\u7684\u6b63\u786e\u6027\uff0c\u5e76\u91cd\u542f\u670d\u52a1\u4f7f\u914d\u7f6e\u751f\u6548\u3002<br \/>\n<div class=\"xun-code-wrapper\"><div class=\"xun-code-header\"><div class=\"xun-code-header-left\"><span class=\"xun-code-dots\"><span><\/span><span><\/span><span><\/span><\/span><span class=\"xun-code-filename\">sbroute.sh<\/span><\/div><div class=\"xun-code-header-right\"><span class=\"xun-code-lang\">YAML<\/span><button type=\"button\" class=\"xun-code-copy\" title=\"\u590d\u5236\u4ee3\u7801\"><svg fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\" width=\"16\" height=\"16\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\"><\/path><\/svg><\/button><\/div><\/div><pre class=\"xun-code-block line-numbers\" ><code class=\"language-yaml\">#!\/bin\/bash\r\n#\r\n# SBRoute \u2014 VPS \u6d41\u91cf\u5206\u6d41\u7ba1\u7406\u5de5\u5177\r\n# \u57fa\u4e8e sing-box \u7684\u8f7b\u91cf\u7ea7\u51fa\u7ad9\u4ee3\u7406\u548c\u8def\u7531\u89c4\u5219\u7ba1\u7406\u5668\r\n# \u9002\u7528\u4e8e Debian 12\r\n#\r\nset -euo pipefail\r\n\r\n# ==================== \u5168\u5c40\u53d8\u91cf ====================\r\nSBROUTE_DIR=&quot;\/etc\/sbroute&quot;\r\nSINGBOX_CONFIG=&quot;$SBROUTE_DIR\/config.json&quot;\r\nOUTBOUNDS_FILE=&quot;$SBROUTE_DIR\/outbounds.json&quot;\r\nROUTES_FILE=&quot;$SBROUTE_DIR\/routes.json&quot;\r\nRULESETS_FILE=&quot;$SBROUTE_DIR\/rulesets.json&quot;\r\nSETTINGS_FILE=&quot;$SBROUTE_DIR\/settings.json&quot;\r\nBACKUP_DIR=&quot;$SBROUTE_DIR\/backups&quot;\r\nSERVICE_NAME=&quot;sbroute&quot;\r\nSERVICE_FILE=&quot;\/etc\/systemd\/system\/${SERVICE_NAME}.service&quot;\r\nVERSION=&quot;1.1.0&quot;\r\n\r\n# ==================== \u989c\u8272\u5b9a\u4e49 ====================\r\nRED=&#039;\\033[0;31m&#039;; GREEN=&#039;\\033[0;32m&#039;; YELLOW=&#039;\\033[1;33m&#039;\r\nBLUE=&#039;\\033[0;34m&#039;; CYAN=&#039;\\033[0;36m&#039;; BOLD=&#039;\\033[1m&#039;; NC=&#039;\\033[0m&#039;\r\n\r\n# ==================== \u5de5\u5177\u51fd\u6570 ====================\r\ninfo()  { echo -e &quot;${GREEN}[\u2713]${NC} $*&quot;; }\r\nwarn()  { echo -e &quot;${YELLOW}[!]${NC} $*&quot;; }\r\nerr()   { echo -e &quot;${RED}[\u2717]${NC} $*&quot;; }\r\ndie()   { err &quot;$@&quot;; exit 1; }\r\ntitle() { echo -e &quot;\\n${BOLD}${CYAN}\u2550\u2550 $* \u2550\u2550${NC}\\n&quot;; }\r\n\r\ncheck_root() {\r\n    [[ $EUID -eq 0 ]] || die &quot;\u8bf7\u4ee5 root \u6743\u9650\u8fd0\u884c\u6b64\u811a\u672c&quot;\r\n}\r\n\r\ncheck_jq() {\r\n    if ! command -v jq &amp;&gt;\/dev\/null; then\r\n        warn &quot;jq \u672a\u5b89\u88c5\uff0c\u6b63\u5728\u5b89\u88c5...&quot;\r\n        apt-get update -qq &amp;&amp; apt-get install -y -qq jq &gt;\/dev\/null 2&gt;&amp;1\r\n        info &quot;jq \u5b89\u88c5\u5b8c\u6210&quot;\r\n    fi\r\n}\r\n\r\nensure_files() {\r\n    mkdir -p &quot;$SBROUTE_DIR&quot; &quot;$BACKUP_DIR&quot;\r\n    [[ -f &quot;$OUTBOUNDS_FILE&quot; ]] || echo &#039;[]&#039; &gt; &quot;$OUTBOUNDS_FILE&quot;\r\n    [[ -f &quot;$ROUTES_FILE&quot; ]]    || echo &#039;[]&#039; &gt; &quot;$ROUTES_FILE&quot;\r\n    [[ -f &quot;$RULESETS_FILE&quot; ]]  || echo &#039;[]&#039; &gt; &quot;$RULESETS_FILE&quot;\r\n    [[ -f &quot;$SETTINGS_FILE&quot; ]]  || cat &gt; &quot;$SETTINGS_FILE&quot; &lt;&lt;&#039;EJSON&#039;\r\n{&quot;default_outbound&quot;:&quot;direct&quot;,&quot;dns_mode&quot;:&quot;basic&quot;,&quot;tun_enabled&quot;:false}\r\nEJSON\r\n}\r\n\r\n# ==================== \u5b89\u88c5\u6a21\u5757 ====================\r\n_create_service() {\r\n    local singbox_bin\r\n    singbox_bin=$(command -v sing-box 2&gt;\/dev\/null) || die &quot;sing-box \u4e8c\u8fdb\u5236\u672a\u627e\u5230&quot;\r\n    cat &gt; &quot;$SERVICE_FILE&quot; &lt;&lt;ESERVICE\r\n[Unit]\r\nDescription=SBRoute sing-box Service\r\nAfter=network.target nss-lookup.target\r\nWants=network-online.target\r\n\r\n[Service]\r\nType=simple\r\nExecStart=${singbox_bin} run -c ${SINGBOX_CONFIG}\r\nRestart=on-failure\r\nRestartSec=10\r\nLimitNOFILE=infinity\r\n\r\n[Install]\r\nWantedBy=multi-user.target\r\nESERVICE\r\n    systemctl daemon-reload\r\n    info &quot;\u5df2\u521b\u5efa\u72ec\u7acb\u670d\u52a1: $SERVICE_NAME&quot;\r\n}\r\n\r\ncmd_install() {\r\n    title &quot;\u5b89\u88c5 SBRoute&quot;\r\n    check_root; check_jq\r\n\r\n    # \u68c0\u67e5 sing-box \u4e8c\u8fdb\u5236\r\n    if ! command -v sing-box &amp;&gt;\/dev\/null; then\r\n        info &quot;sing-box \u672a\u5b89\u88c5\uff0c\u6b63\u5728\u5b89\u88c5...&quot;\r\n        apt-get update -qq &amp;&amp; apt-get install -y -qq curl gpg &gt;\/dev\/null 2&gt;&amp;1\r\n        curl -fsSL https:\/\/sing-box.app\/gpg.key | gpg --dearmor -o \/usr\/share\/keyrings\/sagernet-archive-keyring.gpg 2&gt;\/dev\/null\r\n        echo &quot;deb [arch=$(dpkg --print-architecture) signed-by=\/usr\/share\/keyrings\/sagernet-archive-keyring.gpg] https:\/\/deb.sagernet.org\/ * *&quot; \\\r\n            &gt; \/etc\/apt\/sources.list.d\/sagernet.list\r\n        apt-get update -qq &amp;&amp; apt-get install -y -qq sing-box &gt;\/dev\/null 2&gt;&amp;1\r\n        command -v sing-box &amp;&gt;\/dev\/null || die &quot;sing-box \u5b89\u88c5\u5931\u8d25&quot;\r\n    fi\r\n    info &quot;sing-box \u7248\u672c: $(sing-box version 2&gt;\/dev\/null | head -1)&quot;\r\n\r\n    # \u68c0\u6d4b\u5df2\u6709 sing-box \u670d\u52a1\r\n    if systemctl is-active sing-box &amp;&gt;\/dev\/null; then\r\n        info &quot;\u68c0\u6d4b\u5230\u5df2\u6709 sing-box \u670d\u52a1\u6b63\u5728\u8fd0\u884c\uff08\u4e0d\u4f1a\u5f71\u54cd\u5b83\uff09&quot;\r\n    fi\r\n\r\n    ensure_files\r\n\r\n    # \u521b\u5efa\u72ec\u7acb systemd \u670d\u52a1\r\n    _create_service\r\n    generate_config\r\n    systemctl enable &quot;$SERVICE_NAME&quot; &gt;\/dev\/null 2&gt;&amp;1 || true\r\n    systemctl restart &quot;$SERVICE_NAME&quot; &gt;\/dev\/null 2&gt;&amp;1 || true\r\n    info &quot;SBRoute \u5df2\u542f\u52a8\u5e76\u8bbe\u4e3a\u5f00\u673a\u81ea\u542f&quot;\r\n    info &quot;\u914d\u7f6e\u6587\u4ef6: $SINGBOX_CONFIG&quot;\r\n    info &quot;\u670d\u52a1\u540d\u79f0: $SERVICE_NAME (\u4e0e\u539f sing-box \u670d\u52a1\u4e92\u4e0d\u5f71\u54cd)&quot;\r\n}\r\n\r\ncmd_uninstall() {\r\n    title &quot;\u5378\u8f7d SBRoute&quot;\r\n    check_root\r\n    read -rp &quot;\u786e\u5b9a\u8981\u5378\u8f7d SBRoute \u5417\uff1f(\u4e0d\u4f1a\u5f71\u54cd\u539f sing-box \u670d\u52a1) (y\/N): &quot; confirm\r\n    [[ &quot;$confirm&quot; =~ ^[Yy]$ ]] || { warn &quot;\u53d6\u6d88\u5378\u8f7d&quot;; return; }\r\n\r\n    systemctl stop &quot;$SERVICE_NAME&quot; 2&gt;\/dev\/null || true\r\n    systemctl disable &quot;$SERVICE_NAME&quot; 2&gt;\/dev\/null || true\r\n    rm -f &quot;$SERVICE_FILE&quot;\r\n    systemctl daemon-reload\r\n    info &quot;SBRoute \u670d\u52a1\u5df2\u79fb\u9664&quot;\r\n\r\n    read -rp &quot;\u662f\u5426\u540c\u65f6\u5220\u9664 SBRoute \u914d\u7f6e\u548c\u6570\u636e? (y\/N): &quot; del_conf\r\n    [[ &quot;$del_conf&quot; =~ ^[Yy]$ ]] &amp;&amp; rm -rf &quot;$SBROUTE_DIR&quot; &amp;&amp; info &quot;\u914d\u7f6e\u5df2\u5220\u9664&quot;\r\n    info &quot;\u5378\u8f7d\u5b8c\u6210\uff08\u539f sing-box \u670d\u52a1\u672a\u53d7\u5f71\u54cd\uff09&quot;\r\n}\r\n\r\n# ==================== \u51fa\u7ad9\u7ba1\u7406 ====================\r\nout_add() {\r\n    local type=&quot;${1:-}&quot;; shift 2&gt;\/dev\/null || true\r\n    case &quot;$type&quot; in\r\n        ss)     out_add_ss &quot;$@&quot; ;;\r\n        socks)  out_add_socks &quot;$@&quot; ;;\r\n        http)   out_add_http &quot;$@&quot; ;;\r\n        vless)  out_add_vless &quot;$@&quot; ;;\r\n        vmess)  out_add_vmess &quot;$@&quot; ;;\r\n        direct) out_add_simple &quot;direct&quot; &quot;${1:-direct}&quot; ;;\r\n        block)  out_add_simple &quot;block&quot; &quot;${1:-block}&quot; ;;\r\n        url)    out_add_url &quot;$@&quot; ;;\r\n        *)      die &quot;\u672a\u77e5\u51fa\u7ad9\u7c7b\u578b: $type\\n\u652f\u6301: ss, socks, http, vless, vmess, direct, block, url&quot; ;;\r\n    esac\r\n}\r\n\r\nout_add_ss() {\r\n    [[ $# -ge 5 ]] || die &quot;\u7528\u6cd5: sbroute out add ss &lt;tag&gt; &lt;server&gt; &lt;port&gt; &lt;method&gt; &lt;password&gt;&quot;\r\n    local tag=&quot;$1&quot; server=&quot;$2&quot; port=&quot;$3&quot; method=&quot;$4&quot; password=&quot;$5&quot;\r\n    _check_tag_unique &quot;$tag&quot;\r\n    local obj\r\n    obj=$(jq -n --arg t &quot;$tag&quot; --arg s &quot;$server&quot; --argjson p &quot;$port&quot; \\\r\n        --arg m &quot;$method&quot; --arg pw &quot;$password&quot; \\\r\n        &#039;{type:&quot;shadowsocks&quot;,tag:$t,server:$s,server_port:$p,method:$m,password:$pw}&#039;)\r\n    _append_outbound &quot;$obj&quot;\r\n    info &quot;\u5df2\u6dfb\u52a0 Shadowsocks \u51fa\u7ad9: $tag ($server:$port, $method)&quot;\r\n}\r\n\r\nout_add_socks() {\r\n    [[ $# -ge 3 ]] || die &quot;\u7528\u6cd5: sbroute out add socks &lt;tag&gt; &lt;server&gt; &lt;port&gt; [user] [pass]&quot;\r\n    local tag=&quot;$1&quot; server=&quot;$2&quot; port=&quot;$3&quot; user=&quot;${4:-}&quot; pass=&quot;${5:-}&quot;\r\n    _check_tag_unique &quot;$tag&quot;\r\n    local obj\r\n    obj=$(jq -n --arg t &quot;$tag&quot; --arg s &quot;$server&quot; --argjson p &quot;$port&quot; \\\r\n        &#039;{type:&quot;socks&quot;,tag:$t,server:$s,server_port:$p,version:&quot;5&quot;}&#039;)\r\n    [[ -n &quot;$user&quot; ]] &amp;&amp; obj=$(echo &quot;$obj&quot; | jq --arg u &quot;$user&quot; &#039;. + {username:$u}&#039;)\r\n    [[ -n &quot;$pass&quot; ]] &amp;&amp; obj=$(echo &quot;$obj&quot; | jq --arg p &quot;$pass&quot; &#039;. + {password:$p}&#039;)\r\n    _append_outbound &quot;$obj&quot;\r\n    info &quot;\u5df2\u6dfb\u52a0 SOCKS5 \u51fa\u7ad9: $tag ($server:$port)&quot;\r\n}\r\n\r\nout_add_http() {\r\n    [[ $# -ge 3 ]] || die &quot;\u7528\u6cd5: sbroute out add http &lt;tag&gt; &lt;server&gt; &lt;port&gt; [user] [pass]&quot;\r\n    local tag=&quot;$1&quot; server=&quot;$2&quot; port=&quot;$3&quot; user=&quot;${4:-}&quot; pass=&quot;${5:-}&quot;\r\n    _check_tag_unique &quot;$tag&quot;\r\n    local obj\r\n    obj=$(jq -n --arg t &quot;$tag&quot; --arg s &quot;$server&quot; --argjson p &quot;$port&quot; \\\r\n        &#039;{type:&quot;http&quot;,tag:$t,server:$s,server_port:$p}&#039;)\r\n    [[ -n &quot;$user&quot; ]] &amp;&amp; obj=$(echo &quot;$obj&quot; | jq --arg u &quot;$user&quot; &#039;. + {username:$u}&#039;)\r\n    [[ -n &quot;$pass&quot; ]] &amp;&amp; obj=$(echo &quot;$obj&quot; | jq --arg p &quot;$pass&quot; &#039;. + {password:$p}&#039;)\r\n    _append_outbound &quot;$obj&quot;\r\n    info &quot;\u5df2\u6dfb\u52a0 HTTP \u51fa\u7ad9: $tag ($server:$port)&quot;\r\n}\r\n\r\nout_add_vless() {\r\n    [[ $# -ge 4 ]] || die &quot;\u7528\u6cd5: sbroute out add vless &lt;tag&gt; &lt;server&gt; &lt;port&gt; &lt;uuid&gt; [flow] [sni]&quot;\r\n    local tag=&quot;$1&quot; server=&quot;$2&quot; port=&quot;$3&quot; uuid=&quot;$4&quot; flow=&quot;${5:-}&quot; sni=&quot;${6:-}&quot;\r\n    _check_tag_unique &quot;$tag&quot;\r\n    local obj\r\n    obj=$(jq -n --arg t &quot;$tag&quot; --arg s &quot;$server&quot; --argjson p &quot;$port&quot; --arg u &quot;$uuid&quot; \\\r\n        &#039;{type:&quot;vless&quot;,tag:$t,server:$s,server_port:$p,uuid:$u}&#039;)\r\n    if [[ -n &quot;$flow&quot; ]]; then\r\n        obj=$(echo &quot;$obj&quot; | jq --arg f &quot;$flow&quot; &#039;. + {flow:$f}&#039;)\r\n    fi\r\n    if [[ -n &quot;$sni&quot; ]]; then\r\n        obj=$(echo &quot;$obj&quot; | jq --arg sn &quot;$sni&quot; &#039;. + {tls:{enabled:true,server_name:$sn,reality:{enabled:true}}}&#039;)\r\n    fi\r\n    _append_outbound &quot;$obj&quot;\r\n    info &quot;\u5df2\u6dfb\u52a0 VLESS \u51fa\u7ad9: $tag ($server:$port)&quot;\r\n}\r\n\r\nout_add_vmess() {\r\n    [[ $# -ge 4 ]] || die &quot;\u7528\u6cd5: sbroute out add vmess &lt;tag&gt; &lt;server&gt; &lt;port&gt; &lt;uuid&gt; [security]&quot;\r\n    local tag=&quot;$1&quot; server=&quot;$2&quot; port=&quot;$3&quot; uuid=&quot;$4&quot; security=&quot;${5:-auto}&quot;\r\n    _check_tag_unique &quot;$tag&quot;\r\n    local obj\r\n    obj=$(jq -n --arg t &quot;$tag&quot; --arg s &quot;$server&quot; --argjson p &quot;$port&quot; \\\r\n        --arg u &quot;$uuid&quot; --arg sec &quot;$security&quot; \\\r\n        &#039;{type:&quot;vmess&quot;,tag:$t,server:$s,server_port:$p,uuid:$u,security:$sec}&#039;)\r\n    _append_outbound &quot;$obj&quot;\r\n    info &quot;\u5df2\u6dfb\u52a0 VMess \u51fa\u7ad9: $tag ($server:$port)&quot;\r\n}\r\n\r\nout_add_simple() {\r\n    local type=&quot;$1&quot; tag=&quot;$2&quot;\r\n    _check_tag_unique &quot;$tag&quot;\r\n    local obj\r\n    obj=$(jq -n --arg t &quot;$type&quot; --arg tag &quot;$tag&quot; &#039;{type:$t,tag:$tag}&#039;)\r\n    _append_outbound &quot;$obj&quot;\r\n    info &quot;\u5df2\u6dfb\u52a0 $type \u51fa\u7ad9: $tag&quot;\r\n}\r\n\r\n# ==================== \u5206\u4eab\u94fe\u63a5\u89e3\u6790 ====================\r\n_urldecode() {\r\n    local url_encoded=&quot;${1\/\/+\/ }&quot;\r\n    printf &#039;%b&#039; &quot;${url_encoded\/\/%\/\\\\x}&quot;\r\n}\r\n\r\n_parse_query() {\r\n    # \u89e3\u6790 query string \u4e3a\u5173\u8054\u6570\u7ec4\uff0c\u8c03\u7528\u524d\u9700 declare -A params\r\n    local query=&quot;$1&quot;\r\n    local IFS=&#039;&amp;&#039;\r\n    for kv in $query; do\r\n        local key=&quot;${kv%%=*}&quot;\r\n        local val=&quot;${kv#*=}&quot;\r\n        val=$(_urldecode &quot;$val&quot;)\r\n        eval &quot;params[\\&quot;$key\\&quot;]=\\&quot;$val\\&quot;&quot;\r\n    done\r\n}\r\n\r\nout_add_url() {\r\n    [[ $# -ge 1 ]] || die &quot;\u7528\u6cd5: sbroute out url &lt;\u5206\u4eab\u94fe\u63a5&gt;\\n\u652f\u6301: ss:\/\/, vless:\/\/&quot;\r\n    local url=&quot;$1&quot;\r\n    if [[ &quot;$url&quot; == ss:\/\/* ]]; then\r\n        _parse_ss_url &quot;$url&quot;\r\n    elif [[ &quot;$url&quot; == vless:\/\/* ]]; then\r\n        _parse_vless_url &quot;$url&quot;\r\n    elif [[ &quot;$url&quot; == vmess:\/\/* ]]; then\r\n        die &quot;VMess \u5206\u4eab\u94fe\u63a5\u6682\u4e0d\u652f\u6301\uff0c\u8bf7\u624b\u52a8\u6dfb\u52a0&quot;\r\n    else\r\n        die &quot;\u4e0d\u652f\u6301\u7684\u94fe\u63a5\u683c\u5f0f\uff0c\u76ee\u524d\u652f\u6301: ss:\/\/, vless:\/\/&quot;\r\n    fi\r\n}\r\n\r\n_parse_ss_url() {\r\n    local url=&quot;$1&quot;\r\n    # ss:\/\/base64(method:password)@server:port#tag\r\n    # \u6216 ss:\/\/base64(method:password@server:port)#tag (SIP002)\r\n    local body=&quot;${url#ss:\/\/}&quot;\r\n\r\n    # \u63d0\u53d6 tag (fragment)\r\n    local tag=&quot;&quot;\r\n    if [[ &quot;$body&quot; == *&quot;#&quot;* ]]; then\r\n        tag=$(_urldecode &quot;${body##*#}&quot;)\r\n        body=&quot;${body%%#*}&quot;\r\n    fi\r\n    [[ -n &quot;$tag&quot; ]] || die &quot;SS \u94fe\u63a5\u7f3a\u5c11 tag (# \u540e\u7684\u540d\u79f0)&quot;\r\n\r\n    local server port method password\r\n\r\n    if [[ &quot;$body&quot; == *&quot;@&quot;* ]]; then\r\n        # \u683c\u5f0f: base64(method:password)@server:port\r\n        local userinfo_b64=&quot;${body%%@*}&quot;\r\n        local server_part=&quot;${body##*@}&quot;\r\n        # \u5904\u7406 base64 padding\r\n        local padded=&quot;$userinfo_b64&quot;\r\n        local mod=$((${#padded} % 4))\r\n        if [[ $mod -eq 2 ]]; then padded+=&quot;==&quot;; elif [[ $mod -eq 3 ]]; then padded+=&quot;=&quot;; fi\r\n        local userinfo\r\n        userinfo=$(echo -n &quot;$padded&quot; | base64 -d 2&gt;\/dev\/null) || die &quot;SS \u94fe\u63a5 base64 \u89e3\u7801\u5931\u8d25&quot;\r\n        method=&quot;${userinfo%%:*}&quot;\r\n        password=&quot;${userinfo#*:}&quot;\r\n        server=&quot;${server_part%%:*}&quot;\r\n        port=&quot;${server_part##*:}&quot;\r\n    else\r\n        # \u6574\u4f53 base64 \u7f16\u7801\r\n        local padded=&quot;$body&quot;\r\n        local mod=$((${#padded} % 4))\r\n        if [[ $mod -eq 2 ]]; then padded+=&quot;==&quot;; elif [[ $mod -eq 3 ]]; then padded+=&quot;=&quot;; fi\r\n        local decoded\r\n        decoded=$(echo -n &quot;$padded&quot; | base64 -d 2&gt;\/dev\/null) || die &quot;SS \u94fe\u63a5 base64 \u89e3\u7801\u5931\u8d25&quot;\r\n        method=&quot;${decoded%%:*}&quot;\r\n        local rest=&quot;${decoded#*:}&quot;\r\n        password=&quot;${rest%%@*}&quot;\r\n        local server_part=&quot;${rest##*@}&quot;\r\n        server=&quot;${server_part%%:*}&quot;\r\n        port=&quot;${server_part##*:}&quot;\r\n    fi\r\n\r\n    [[ -n &quot;$server&quot; &amp;&amp; -n &quot;$port&quot; &amp;&amp; -n &quot;$method&quot; &amp;&amp; -n &quot;$password&quot; ]] || die &quot;SS \u94fe\u63a5\u89e3\u6790\u5931\u8d25&quot;\r\n    info &quot;\u89e3\u6790 SS \u94fe\u63a5: $tag ($server:$port, $method)&quot;\r\n    out_add_ss &quot;$tag&quot; &quot;$server&quot; &quot;$port&quot; &quot;$method&quot; &quot;$password&quot;\r\n}\r\n\r\n_parse_vless_url() {\r\n    local url=&quot;$1&quot;\r\n    # vless:\/\/uuid@server:port?params#tag\r\n    local body=&quot;${url#vless:\/\/}&quot;\r\n\r\n    # \u63d0\u53d6 tag\r\n    local tag=&quot;&quot;\r\n    if [[ &quot;$body&quot; == *&quot;#&quot;* ]]; then\r\n        tag=$(_urldecode &quot;${body##*#}&quot;)\r\n        body=&quot;${body%%#*}&quot;\r\n    fi\r\n    [[ -n &quot;$tag&quot; ]] || die &quot;VLESS \u94fe\u63a5\u7f3a\u5c11 tag (# \u540e\u7684\u540d\u79f0)&quot;\r\n\r\n    # \u63d0\u53d6 query\r\n    local query=&quot;&quot;\r\n    if [[ &quot;$body&quot; == *&quot;?&quot;* ]]; then\r\n        query=&quot;${body##*?}&quot;\r\n        body=&quot;${body%%\\?*}&quot;\r\n    fi\r\n\r\n    # \u63d0\u53d6 uuid@server:port\r\n    local uuid=&quot;${body%%@*}&quot;\r\n    local server_part=&quot;${body##*@}&quot;\r\n    local server=&quot;${server_part%%:*}&quot;\r\n    local port=&quot;${server_part##*:}&quot;\r\n\r\n    [[ -n &quot;$uuid&quot; &amp;&amp; -n &quot;$server&quot; &amp;&amp; -n &quot;$port&quot; ]] || die &quot;VLESS \u94fe\u63a5\u89e3\u6790\u5931\u8d25&quot;\r\n\r\n    # \u89e3\u6790 query \u53c2\u6570\r\n    declare -A params\r\n    _parse_query &quot;$query&quot;\r\n\r\n    local flow=&quot;${params[flow]:-}&quot;\r\n    local security=&quot;${params[security]:-}&quot;\r\n    local sni=&quot;${params[sni]:-}&quot;\r\n    local fp=&quot;${params[fp]:-}&quot;\r\n    local pbk=&quot;${params[pbk]:-}&quot;\r\n    local sid=&quot;${params[sid]:-}&quot;\r\n    local net_type=&quot;${params[type]:-tcp}&quot;\r\n    local alpn=&quot;${params[alpn]:-}&quot;\r\n\r\n    _check_tag_unique &quot;$tag&quot;\r\n\r\n    # \u6784\u5efa\u57fa\u7840\u5bf9\u8c61\r\n    local obj\r\n    obj=$(jq -n --arg t &quot;$tag&quot; --arg s &quot;$server&quot; --argjson p &quot;$port&quot; --arg u &quot;$uuid&quot; \\\r\n        &#039;{type:&quot;vless&quot;,tag:$t,server:$s,server_port:$p,uuid:$u}&#039;)\r\n\r\n    # flow\r\n    [[ -n &quot;$flow&quot; ]] &amp;&amp; obj=$(echo &quot;$obj&quot; | jq --arg f &quot;$flow&quot; &#039;. + {flow:$f}&#039;)\r\n\r\n    # TLS \/ Reality\r\n    if [[ &quot;$security&quot; == &quot;reality&quot; ]]; then\r\n        local tls_obj=&#039;{&quot;enabled&quot;:true}&#039;\r\n        [[ -n &quot;$sni&quot; ]] &amp;&amp; tls_obj=$(echo &quot;$tls_obj&quot; | jq --arg s &quot;$sni&quot; &#039;. + {server_name:$s}&#039;)\r\n        local reality_obj=&#039;{&quot;enabled&quot;:true}&#039;\r\n        [[ -n &quot;$pbk&quot; ]] &amp;&amp; reality_obj=$(echo &quot;$reality_obj&quot; | jq --arg k &quot;$pbk&quot; &#039;. + {public_key:$k}&#039;)\r\n        [[ -n &quot;$sid&quot; ]] &amp;&amp; reality_obj=$(echo &quot;$reality_obj&quot; | jq --arg s &quot;$sid&quot; &#039;. + {short_id:$s}&#039;)\r\n        tls_obj=$(echo &quot;$tls_obj&quot; | jq --argjson r &quot;$reality_obj&quot; &#039;. + {reality:$r}&#039;)\r\n        [[ -n &quot;$fp&quot; ]] &amp;&amp; tls_obj=$(echo &quot;$tls_obj&quot; | jq --arg u &quot;$fp&quot; &#039;. + {utls:{enabled:true,fingerprint:$u}}&#039;)\r\n        [[ -n &quot;$alpn&quot; ]] &amp;&amp; tls_obj=$(echo &quot;$tls_obj&quot; | jq --arg a &quot;$alpn&quot; &#039;. + {alpn:($a|split(&quot;,&quot;))}&#039;)\r\n        obj=$(echo &quot;$obj&quot; | jq --argjson tls &quot;$tls_obj&quot; &#039;. + {tls:$tls}&#039;)\r\n    elif [[ &quot;$security&quot; == &quot;tls&quot; ]]; then\r\n        local tls_obj=&#039;{&quot;enabled&quot;:true}&#039;\r\n        [[ -n &quot;$sni&quot; ]] &amp;&amp; tls_obj=$(echo &quot;$tls_obj&quot; | jq --arg s &quot;$sni&quot; &#039;. + {server_name:$s}&#039;)\r\n        [[ -n &quot;$fp&quot; ]] &amp;&amp; tls_obj=$(echo &quot;$tls_obj&quot; | jq --arg u &quot;$fp&quot; &#039;. + {utls:{enabled:true,fingerprint:$u}}&#039;)\r\n        [[ -n &quot;$alpn&quot; ]] &amp;&amp; tls_obj=$(echo &quot;$tls_obj&quot; | jq --arg a &quot;$alpn&quot; &#039;. + {alpn:($a|split(&quot;,&quot;))}&#039;)\r\n        obj=$(echo &quot;$obj&quot; | jq --argjson tls &quot;$tls_obj&quot; &#039;. + {tls:$tls}&#039;)\r\n    fi\r\n\r\n    # transport (ws\/grpc\/h2)\r\n    if [[ &quot;$net_type&quot; == &quot;ws&quot; ]]; then\r\n        local ws_path=&quot;${params[path]:-\/}&quot;\r\n        local ws_host=&quot;${params[host]:-}&quot;\r\n        local transport=&#039;{&quot;type&quot;:&quot;ws&quot;}&#039;\r\n        transport=$(echo &quot;$transport&quot; | jq --arg p &quot;$ws_path&quot; &#039;. + {path:$p}&#039;)\r\n        [[ -n &quot;$ws_host&quot; ]] &amp;&amp; transport=$(echo &quot;$transport&quot; | jq --arg h &quot;$ws_host&quot; &#039;. + {headers:{Host:$h}}&#039;)\r\n        obj=$(echo &quot;$obj&quot; | jq --argjson t &quot;$transport&quot; &#039;. + {transport:$t}&#039;)\r\n    elif [[ &quot;$net_type&quot; == &quot;grpc&quot; ]]; then\r\n        local sn=&quot;${params[serviceName]:-}&quot;\r\n        local transport=&#039;{&quot;type&quot;:&quot;grpc&quot;}&#039;\r\n        [[ -n &quot;$sn&quot; ]] &amp;&amp; transport=$(echo &quot;$transport&quot; | jq --arg s &quot;$sn&quot; &#039;. + {service_name:$s}&#039;)\r\n        obj=$(echo &quot;$obj&quot; | jq --argjson t &quot;$transport&quot; &#039;. + {transport:$t}&#039;)\r\n    fi\r\n\r\n    _append_outbound &quot;$obj&quot;\r\n    info &quot;\u5df2\u6dfb\u52a0 VLESS \u51fa\u7ad9: $tag ($server:$port, security=$security)&quot;\r\n}\r\n\r\n_check_tag_unique() {\r\n    local tag=&quot;$1&quot;\r\n    local exists\r\n    exists=$(jq -r --arg t &quot;$tag&quot; &#039;[.[]|select(.tag==$t)]|length&#039; &quot;$OUTBOUNDS_FILE&quot;)\r\n    [[ &quot;$exists&quot; -eq 0 ]] || die &quot;Tag &#039;$tag&#039; \u5df2\u5b58\u5728\uff0c\u8bf7\u4f7f\u7528\u5176\u4ed6\u540d\u79f0&quot;\r\n}\r\n\r\n_append_outbound() {\r\n    local obj=&quot;$1&quot;\r\n    local tmp; tmp=$(mktemp)\r\n    jq --argjson o &quot;$obj&quot; &#039;. + [$o]&#039; &quot;$OUTBOUNDS_FILE&quot; &gt; &quot;$tmp&quot; &amp;&amp; mv &quot;$tmp&quot; &quot;$OUTBOUNDS_FILE&quot;\r\n}\r\n\r\nout_list() {\r\n    title &quot;\u51fa\u7ad9\u5217\u8868&quot;\r\n    local count\r\n    count=$(jq &#039;length&#039; &quot;$OUTBOUNDS_FILE&quot;)\r\n    if [[ &quot;$count&quot; -eq 0 ]]; then\r\n        warn &quot;\u6682\u65e0\u81ea\u5b9a\u4e49\u51fa\u7ad9\uff08direct \u548c dns \u4e3a\u5185\u7f6e\u51fa\u7ad9\uff09&quot;\r\n        return\r\n    fi\r\n    printf &quot;${BOLD}%-4s %-20s %-12s %-30s${NC}\\n&quot; &quot;#&quot; &quot;Tag&quot; &quot;\u7c7b\u578b&quot; &quot;\u670d\u52a1\u5668&quot;\r\n    echo &quot;\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&quot;\r\n    jq -r &#039;to_entries[]|[.key+1,.value.tag,.value.type,\r\n        (if .value.server then &quot;\\(.value.server):\\(.value.server_port)&quot; else &quot;-&quot; end)]\r\n        |@tsv&#039; &quot;$OUTBOUNDS_FILE&quot; | while IFS=$&#039;\\t&#039; read -r idx tag type srv; do\r\n        printf &quot;%-4s %-20s %-12s %-30s\\n&quot; &quot;$idx&quot; &quot;$tag&quot; &quot;$type&quot; &quot;$srv&quot;\r\n    done\r\n}\r\n\r\nout_del() {\r\n    [[ $# -ge 1 ]] || die &quot;\u7528\u6cd5: sbroute out del &lt;tag&gt;&quot;\r\n    local tag=&quot;$1&quot;\r\n    local exists\r\n    exists=$(jq -r --arg t &quot;$tag&quot; &#039;[.[]|select(.tag==$t)]|length&#039; &quot;$OUTBOUNDS_FILE&quot;)\r\n    [[ &quot;$exists&quot; -gt 0 ]] || die &quot;\u51fa\u7ad9 &#039;$tag&#039; \u4e0d\u5b58\u5728&quot;\r\n    local tmp; tmp=$(mktemp)\r\n    jq --arg t &quot;$tag&quot; &#039;[.[]|select(.tag!=$t)]&#039; &quot;$OUTBOUNDS_FILE&quot; &gt; &quot;$tmp&quot; &amp;&amp; mv &quot;$tmp&quot; &quot;$OUTBOUNDS_FILE&quot;\r\n    # \u540c\u65f6\u6e05\u7406\u5f15\u7528\u8be5 tag \u7684\u8def\u7531\u89c4\u5219\r\n    tmp=$(mktemp)\r\n    jq --arg t &quot;$tag&quot; &#039;[.[]|select(.outbound!=$t)]&#039; &quot;$ROUTES_FILE&quot; &gt; &quot;$tmp&quot; &amp;&amp; mv &quot;$tmp&quot; &quot;$ROUTES_FILE&quot;\r\n    info &quot;\u5df2\u5220\u9664\u51fa\u7ad9: $tag\uff08\u5173\u8054\u8def\u7531\u89c4\u5219\u5df2\u6e05\u7406\uff09&quot;\r\n}\r\n\r\nout_show() {\r\n    [[ $# -ge 1 ]] || die &quot;\u7528\u6cd5: sbroute out show &lt;tag&gt;&quot;\r\n    local tag=&quot;$1&quot;\r\n    jq --arg t &quot;$tag&quot; &#039;.[]|select(.tag==$t)&#039; &quot;$OUTBOUNDS_FILE&quot; | jq &#039;.&#039; || die &quot;\u51fa\u7ad9 &#039;$tag&#039; \u4e0d\u5b58\u5728&quot;\r\n}\r\n\r\n# ==================== \u8def\u7531\u89c4\u5219\u7ba1\u7406 ====================\r\nroute_add() {\r\n    [[ $# -ge 2 ]] || die &quot;\u7528\u6cd5: sbroute route add &lt;outbound_tag&gt; --domain\/--suffix\/--keyword\/--geoip\/--geosite\/... &lt;values&gt;&quot;\r\n    local outbound=&quot;$1&quot;; shift\r\n    # \u9a8c\u8bc1 outbound tag \u5b58\u5728\r\n    local tag_ok\r\n    tag_ok=$(jq -r --arg t &quot;$outbound&quot; &#039;[.[]|select(.tag==$t)]|length&#039; &quot;$OUTBOUNDS_FILE&quot;)\r\n    if [[ &quot;$tag_ok&quot; -eq 0 &amp;&amp; &quot;$outbound&quot; != &quot;direct&quot; &amp;&amp; &quot;$outbound&quot; != &quot;block&quot; ]]; then\r\n        die &quot;\u51fa\u7ad9 &#039;$outbound&#039; \u4e0d\u5b58\u5728\uff0c\u8bf7\u5148\u6dfb\u52a0\u51fa\u7ad9&quot;\r\n    fi\r\n\r\n    local rule=&#039;{&quot;action&quot;:&quot;route&quot;}&#039;\r\n    rule=$(echo &quot;$rule&quot; | jq --arg o &quot;$outbound&quot; &#039;. + {outbound:$o}&#039;)\r\n\r\n    while [[ $# -gt 0 ]]; do\r\n        case &quot;$1&quot; in\r\n            --domain)\r\n                shift; [[ $# -gt 0 ]] || die &quot;--domain \u9700\u8981\u53c2\u6570&quot;\r\n                rule=$(echo &quot;$rule&quot; | jq --arg v &quot;$1&quot; &#039;. + {domain:($v|split(&quot;,&quot;))}&#039;)\r\n                ;;\r\n            --suffix)\r\n                shift; [[ $# -gt 0 ]] || die &quot;--suffix \u9700\u8981\u53c2\u6570&quot;\r\n                rule=$(echo &quot;$rule&quot; | jq --arg v &quot;$1&quot; &#039;. + {domain_suffix:($v|split(&quot;,&quot;))}&#039;)\r\n                ;;\r\n            --keyword)\r\n                shift; [[ $# -gt 0 ]] || die &quot;--keyword \u9700\u8981\u53c2\u6570&quot;\r\n                rule=$(echo &quot;$rule&quot; | jq --arg v &quot;$1&quot; &#039;. + {domain_keyword:($v|split(&quot;,&quot;))}&#039;)\r\n                ;;\r\n            --regex)\r\n                shift; [[ $# -gt 0 ]] || die &quot;--regex \u9700\u8981\u53c2\u6570&quot;\r\n                rule=$(echo &quot;$rule&quot; | jq --arg v &quot;$1&quot; &#039;. + {domain_regex:($v|split(&quot;,&quot;))}&#039;)\r\n                ;;\r\n            --ip-cidr)\r\n                shift; [[ $# -gt 0 ]] || die &quot;--ip-cidr \u9700\u8981\u53c2\u6570&quot;\r\n                rule=$(echo &quot;$rule&quot; | jq --arg v &quot;$1&quot; &#039;. + {ip_cidr:($v|split(&quot;,&quot;))}&#039;)\r\n                ;;\r\n            --port)\r\n                shift; [[ $# -gt 0 ]] || die &quot;--port \u9700\u8981\u53c2\u6570&quot;\r\n                rule=$(echo &quot;$rule&quot; | jq --arg v &quot;$1&quot; &#039;. + {port:($v|split(&quot;,&quot;)|map(tonumber))}&#039;)\r\n                ;;\r\n            --process)\r\n                shift; [[ $# -gt 0 ]] || die &quot;--process \u9700\u8981\u53c2\u6570&quot;\r\n                rule=$(echo &quot;$rule&quot; | jq --arg v &quot;$1&quot; &#039;. + {process_name:($v|split(&quot;,&quot;))}&#039;)\r\n                ;;\r\n            --geoip)\r\n                shift; [[ $# -gt 0 ]] || die &quot;--geoip \u9700\u8981\u53c2\u6570 (\u5982: cn, us, jp \u7b49)&quot;\r\n                local IFS_OLD=&quot;$IFS&quot;; IFS=&#039;,&#039;\r\n                for _geo_name in $1; do\r\n                    IFS=&quot;$IFS_OLD&quot;\r\n                    # \u907f\u514d\u91cd\u590d\u524d\u7f00: \u82e5\u7528\u6237\u8f93\u5165\u5df2\u542b geoip- \u524d\u7f00\u5219\u53bb\u6389\r\n                    _geo_name=&quot;${_geo_name#geoip-}&quot;\r\n                    local geo_tag=&quot;geoip-${_geo_name}&quot;\r\n                    local geo_url=&quot;https:\/\/raw.githubusercontent.com\/SagerNet\/sing-geoip\/rule-set\/geoip-${_geo_name}.srs&quot;\r\n                    _add_ruleset_if_missing &quot;$geo_tag&quot; &quot;$geo_url&quot; &quot;binary&quot;\r\n                    rule=$(echo &quot;$rule&quot; | jq --arg rs &quot;$geo_tag&quot; \\\r\n                        &#039;if .rule_set then .rule_set += [$rs] else . + {rule_set:[$rs]} end&#039;)\r\n                done\r\n                IFS=&quot;$IFS_OLD&quot;\r\n                ;;\r\n            --geosite)\r\n                shift; [[ $# -gt 0 ]] || die &quot;--geosite \u9700\u8981\u53c2\u6570 (\u5982: google, cn, netflix, category-ads-all \u7b49)&quot;\r\n                local IFS_OLD=&quot;$IFS&quot;; IFS=&#039;,&#039;\r\n                for _geo_name in $1; do\r\n                    IFS=&quot;$IFS_OLD&quot;\r\n                    # \u907f\u514d\u91cd\u590d\u524d\u7f00: \u82e5\u7528\u6237\u8f93\u5165\u5df2\u542b geosite- \u524d\u7f00\u5219\u53bb\u6389\r\n                    _geo_name=&quot;${_geo_name#geosite-}&quot;\r\n                    local geo_tag=&quot;geosite-${_geo_name}&quot;\r\n                    local geo_url=&quot;https:\/\/raw.githubusercontent.com\/SagerNet\/sing-geosite\/rule-set\/geosite-${_geo_name}.srs&quot;\r\n                    _add_ruleset_if_missing &quot;$geo_tag&quot; &quot;$geo_url&quot; &quot;binary&quot;\r\n                    rule=$(echo &quot;$rule&quot; | jq --arg rs &quot;$geo_tag&quot; \\\r\n                        &#039;if .rule_set then .rule_set += [$rs] else . + {rule_set:[$rs]} end&#039;)\r\n                done\r\n                IFS=&quot;$IFS_OLD&quot;\r\n                ;;\r\n            --ruleset)\r\n                shift; [[ $# -gt 0 ]] || die &quot;--ruleset \u9700\u8981\u53c2\u6570&quot;\r\n                local url=&quot;$1&quot;\r\n                local rs_tag; rs_tag=&quot;rs-$(echo &quot;$url&quot; | md5sum | head -c 8)&quot;\r\n                # \u6dfb\u52a0\u5230 rulesets\r\n                local rs_exists\r\n                rs_exists=$(jq -r --arg u &quot;$url&quot; &#039;[.[]|select(.url==$u)]|length&#039; &quot;$RULESETS_FILE&quot;)\r\n                if [[ &quot;$rs_exists&quot; -eq 0 ]]; then\r\n                    local fmt=&quot;binary&quot;\r\n                    [[ &quot;$url&quot; == *.json ]] &amp;&amp; fmt=&quot;source&quot;\r\n                    local detour; detour=$(_get_download_detour)\r\n                    local rs_obj\r\n                    rs_obj=$(jq -n --arg tag &quot;$rs_tag&quot; --arg u &quot;$url&quot; --arg f &quot;$fmt&quot; --arg d &quot;$detour&quot; \\\r\n                        &#039;{type:&quot;remote&quot;,tag:$tag,format:$f,url:$u,download_detour:$d}&#039;)\r\n                    local tmp; tmp=$(mktemp)\r\n                    jq --argjson o &quot;$rs_obj&quot; &#039;. + [$o]&#039; &quot;$RULESETS_FILE&quot; &gt; &quot;$tmp&quot; &amp;&amp; mv &quot;$tmp&quot; &quot;$RULESETS_FILE&quot;\r\n                else\r\n                    rs_tag=$(jq -r --arg u &quot;$url&quot; &#039;.[]|select(.url==$u)|.tag&#039; &quot;$RULESETS_FILE&quot;)\r\n                fi\r\n                rule=$(echo &quot;$rule&quot; | jq --arg rs &quot;$rs_tag&quot; \\\r\n                    &#039;if .rule_set then .rule_set += [$rs] else . + {rule_set:[$rs]} end&#039;)\r\n                ;;\r\n            *) die &quot;\u672a\u77e5\u8def\u7531\u53c2\u6570: $1&quot; ;;\r\n        esac\r\n        shift\r\n    done\r\n\r\n    local tmp; tmp=$(mktemp)\r\n    jq --argjson r &quot;$rule&quot; &#039;. + [$r]&#039; &quot;$ROUTES_FILE&quot; &gt; &quot;$tmp&quot; &amp;&amp; mv &quot;$tmp&quot; &quot;$ROUTES_FILE&quot;\r\n    info &quot;\u5df2\u6dfb\u52a0\u8def\u7531\u89c4\u5219 \u2192 $outbound&quot;\r\n    echo &quot;$rule&quot; | jq &#039;.&#039;\r\n}\r\n\r\nroute_list() {\r\n    title &quot;\u8def\u7531\u89c4\u5219\u5217\u8868&quot;\r\n    local count\r\n    count=$(jq &#039;length&#039; &quot;$ROUTES_FILE&quot;)\r\n    if [[ &quot;$count&quot; -eq 0 ]]; then\r\n        warn &quot;\u6682\u65e0\u81ea\u5b9a\u4e49\u8def\u7531\u89c4\u5219&quot;\r\n        return\r\n    fi\r\n    printf &quot;${BOLD}%-4s %-15s %-50s${NC}\\n&quot; &quot;#&quot; &quot;\u51fa\u7ad9&quot; &quot;\u5339\u914d\u6761\u4ef6&quot;\r\n    echo &quot;\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500&quot;\r\n    jq -r &#039;to_entries[]|[.key+1, .value.outbound,\r\n        ([if .value.domain then &quot;domain:\\(.value.domain|join(&quot;,&quot;))&quot; else empty end,\r\n          if .value.domain_suffix then &quot;suffix:\\(.value.domain_suffix|join(&quot;,&quot;))&quot; else empty end,\r\n          if .value.domain_keyword then &quot;keyword:\\(.value.domain_keyword|join(&quot;,&quot;))&quot; else empty end,\r\n          if .value.domain_regex then &quot;regex:\\(.value.domain_regex|join(&quot;,&quot;))&quot; else empty end,\r\n          if .value.ip_cidr then &quot;ip:\\(.value.ip_cidr|join(&quot;,&quot;))&quot; else empty end,\r\n          if .value.port then &quot;port:\\(.value.port|map(tostring)|join(&quot;,&quot;))&quot; else empty end,\r\n          if .value.process_name then &quot;proc:\\(.value.process_name|join(&quot;,&quot;))&quot; else empty end,\r\n          if .value.rule_set then &quot;ruleset:\\(.value.rule_set|join(&quot;,&quot;))&quot; else empty end\r\n        ]|join(&quot; | &quot;))]|@tsv&#039; &quot;$ROUTES_FILE&quot; | while IFS=$&#039;\\t&#039; read -r idx out conds; do\r\n        printf &quot;%-4s %-15s %-50s\\n&quot; &quot;$idx&quot; &quot;$out&quot; &quot;$conds&quot;\r\n    done\r\n}\r\n\r\nroute_del() {\r\n    [[ $# -ge 1 ]] || die &quot;\u7528\u6cd5: sbroute route del &lt;index&gt;&quot;\r\n    local idx=&quot;$1&quot;\r\n    local count; count=$(jq &#039;length&#039; &quot;$ROUTES_FILE&quot;)\r\n    [[ &quot;$idx&quot; -ge 1 &amp;&amp; &quot;$idx&quot; -le &quot;$count&quot; ]] 2&gt;\/dev\/null || die &quot;\u7d22\u5f15\u8d85\u51fa\u8303\u56f4 (1-$count)&quot;\r\n    local tmp; tmp=$(mktemp)\r\n    jq --argjson i &quot;$((idx-1))&quot; &#039;del(.[$i])&#039; &quot;$ROUTES_FILE&quot; &gt; &quot;$tmp&quot; &amp;&amp; mv &quot;$tmp&quot; &quot;$ROUTES_FILE&quot;\r\n    info &quot;\u5df2\u5220\u9664\u8def\u7531\u89c4\u5219 #$idx&quot;\r\n}\r\n\r\nroute_default() {\r\n    [[ $# -ge 1 ]] || die &quot;\u7528\u6cd5: sbroute route default &lt;outbound_tag&gt;&quot;\r\n    local tag=&quot;$1&quot; tmp\r\n    tmp=$(mktemp)\r\n    jq --arg t &quot;$tag&quot; &#039;.default_outbound=$t&#039; &quot;$SETTINGS_FILE&quot; &gt; &quot;$tmp&quot; &amp;&amp; mv &quot;$tmp&quot; &quot;$SETTINGS_FILE&quot;\r\n    info &quot;\u9ed8\u8ba4\u51fa\u7ad9\u5df2\u8bbe\u7f6e\u4e3a: $tag&quot;\r\n}\r\n\r\n# ==================== DNS \u6a21\u5757 ====================\r\ncmd_dns() {\r\n    local mode=&quot;${1:-}&quot;\r\n    case &quot;$mode&quot; in\r\n        basic)\r\n            local tmp; tmp=$(mktemp)\r\n            jq &#039;.dns_mode=&quot;basic&quot;&#039; &quot;$SETTINGS_FILE&quot; &gt; &quot;$tmp&quot; &amp;&amp; mv &quot;$tmp&quot; &quot;$SETTINGS_FILE&quot;\r\n            info &quot;DNS \u6a21\u5f0f: \u57fa\u7840 (Google DNS 8.8.8.8)&quot;\r\n            ;;\r\n        split)\r\n            local tmp; tmp=$(mktemp)\r\n            jq &#039;.dns_mode=&quot;split&quot;&#039; &quot;$SETTINGS_FILE&quot; &gt; &quot;$tmp&quot; &amp;&amp; mv &quot;$tmp&quot; &quot;$SETTINGS_FILE&quot;\r\n            info &quot;DNS \u6a21\u5f0f: \u56fd\u5185\u5206\u6d41 (AliDNS + Google DNS)&quot;\r\n            ;;\r\n        *)\r\n            echo &quot;\u7528\u6cd5: sbroute dns &lt;basic|split&gt;&quot;\r\n            echo &quot;  basic  \u2014 Google DNS (8.8.8.8)&quot;\r\n            echo &quot;  split  \u2014 \u56fd\u5185\u57df\u540d\u7528 AliDNS\uff0c\u5176\u4f59\u7528 Google DNS&quot;\r\n            ;;\r\n    esac\r\n}\r\n\r\n# ==================== \u9884\u8bbe\u6a21\u677f ====================\r\ncmd_preset() {\r\n    local preset=&quot;${1:-}&quot;\r\n    case &quot;$preset&quot; in\r\n        cn-direct)\r\n            title &quot;\u5e94\u7528\u9884\u8bbe: \u56fd\u5185\u76f4\u8fde&quot;\r\n            _preset_ensure_direct\r\n            # geoip-cn\r\n            _add_ruleset_if_missing &quot;geoip-cn&quot; \\\r\n                &quot;https:\/\/raw.githubusercontent.com\/SagerNet\/sing-geoip\/rule-set\/geoip-cn.srs&quot; &quot;binary&quot;\r\n            # geosite-cn\r\n            _add_ruleset_if_missing &quot;geosite-cn&quot; \\\r\n                &quot;https:\/\/raw.githubusercontent.com\/SagerNet\/sing-geosite\/rule-set\/geosite-cn.srs&quot; &quot;binary&quot;\r\n            # \u6dfb\u52a0\u8def\u7531\u89c4\u5219\r\n            local rule\r\n            rule=$(jq -n &#039;{action:&quot;route&quot;,outbound:&quot;direct&quot;,rule_set:[&quot;geoip-cn&quot;,&quot;geosite-cn&quot;]}&#039;)\r\n            _add_route_if_missing &quot;$rule&quot; &quot;geoip-cn&quot;\r\n            info &quot;\u5df2\u6dfb\u52a0: \u56fd\u5185 IP + \u56fd\u5185\u57df\u540d \u2192 direct&quot;\r\n            ;;\r\n        ads-block)\r\n            title &quot;\u5e94\u7528\u9884\u8bbe: \u5e7f\u544a\u62e6\u622a&quot;\r\n            _add_ruleset_if_missing &quot;geosite-category-ads-all&quot; \\\r\n                &quot;https:\/\/raw.githubusercontent.com\/SagerNet\/sing-geosite\/rule-set\/geosite-category-ads-all.srs&quot; &quot;binary&quot;\r\n            # \u786e\u4fdd block \u51fa\u7ad9\u5b58\u5728\r\n            local block_exists\r\n            block_exists=$(jq &#039;[.[]|select(.tag==&quot;block&quot;)]|length&#039; &quot;$OUTBOUNDS_FILE&quot;)\r\n            [[ &quot;$block_exists&quot; -gt 0 ]] || out_add_simple &quot;block&quot; &quot;block&quot;\r\n            local rule\r\n            rule=$(jq -n &#039;{action:&quot;route&quot;,outbound:&quot;block&quot;,rule_set:[&quot;geosite-category-ads-all&quot;]}&#039;)\r\n            _add_route_if_missing &quot;$rule&quot; &quot;geosite-category-ads-all&quot;\r\n            info &quot;\u5df2\u6dfb\u52a0: \u5e7f\u544a\u57df\u540d \u2192 block&quot;\r\n            ;;\r\n        *)\r\n            echo &quot;\u7528\u6cd5: sbroute preset &lt;cn-direct|ads-block&gt;&quot;\r\n            echo &quot;  cn-direct  \u2014 \u56fd\u5185 IP + \u57df\u540d \u76f4\u8fde&quot;\r\n            echo &quot;  ads-block  \u2014 \u5e7f\u544a\u57df\u540d \u62e6\u622a&quot;\r\n            ;;\r\n    esac\r\n}\r\n\r\n_preset_ensure_direct() {\r\n    local exists\r\n    exists=$(jq &#039;[.[]|select(.tag==&quot;direct&quot;)]|length&#039; &quot;$OUTBOUNDS_FILE&quot;)\r\n    [[ &quot;$exists&quot; -gt 0 ]] || out_add_simple &quot;direct&quot; &quot;direct&quot;\r\n}\r\n\r\n# \u83b7\u53d6 rule-set \u4e0b\u8f7d\u4f7f\u7528\u7684 detour: \u4f18\u5148\u7528\u7b2c\u4e00\u4e2a\u4ee3\u7406\u51fa\u7ad9\uff0c\u5426\u5219\u7528 direct\r\n_get_download_detour() {\r\n    local first_proxy\r\n    first_proxy=$(jq -r &#039;[.[]|select(.type!=&quot;direct&quot; and .type!=&quot;block&quot;)][0].tag \/\/ empty&#039; &quot;$OUTBOUNDS_FILE&quot; 2&gt;\/dev\/null)\r\n    if [[ -n &quot;$first_proxy&quot; ]]; then\r\n        echo &quot;$first_proxy&quot;\r\n    else\r\n        echo &quot;direct&quot;\r\n    fi\r\n}\r\n\r\n_add_ruleset_if_missing() {\r\n    local tag=&quot;$1&quot; url=&quot;$2&quot; fmt=&quot;$3&quot;\r\n    local exists\r\n    exists=$(jq -r --arg t &quot;$tag&quot; &#039;[.[]|select(.tag==$t)]|length&#039; &quot;$RULESETS_FILE&quot;)\r\n    if [[ &quot;$exists&quot; -eq 0 ]]; then\r\n        local detour; detour=$(_get_download_detour)\r\n        local obj; obj=$(jq -n --arg t &quot;$tag&quot; --arg u &quot;$url&quot; --arg f &quot;$fmt&quot; --arg d &quot;$detour&quot; \\\r\n            &#039;{type:&quot;remote&quot;,tag:$t,format:$f,url:$u,download_detour:$d}&#039;)\r\n        local tmp; tmp=$(mktemp)\r\n        jq --argjson o &quot;$obj&quot; &#039;. + [$o]&#039; &quot;$RULESETS_FILE&quot; &gt; &quot;$tmp&quot; &amp;&amp; mv &quot;$tmp&quot; &quot;$RULESETS_FILE&quot;\r\n    fi\r\n}\r\n\r\n_add_route_if_missing() {\r\n    local rule=&quot;$1&quot; check_key=&quot;$2&quot;\r\n    local exists\r\n    exists=$(jq -r --arg k &quot;$check_key&quot; &#039;[.[]|select(.rule_set? and (.rule_set[]|select(.==$k)))]|length&#039; &quot;$ROUTES_FILE&quot;)\r\n    if [[ &quot;$exists&quot; -eq 0 ]]; then\r\n        local tmp; tmp=$(mktemp)\r\n        jq --argjson r &quot;$rule&quot; &#039;. + [$r]&#039; &quot;$ROUTES_FILE&quot; &gt; &quot;$tmp&quot; &amp;&amp; mv &quot;$tmp&quot; &quot;$ROUTES_FILE&quot;\r\n    fi\r\n}\r\n\r\n# ==================== \u914d\u7f6e\u751f\u6210 ====================\r\ngenerate_config() {\r\n    ensure_files\r\n    local dns_mode default_out tun_enabled\r\n    dns_mode=$(jq -r &#039;.dns_mode \/\/ &quot;basic&quot;&#039; &quot;$SETTINGS_FILE&quot;)\r\n    default_out=$(jq -r &#039;.default_outbound \/\/ &quot;direct&quot;&#039; &quot;$SETTINGS_FILE&quot;)\r\n    tun_enabled=$(jq -r &#039;.tun_enabled \/\/ true&#039; &quot;$SETTINGS_FILE&quot;)\r\n\r\n    local dns_config\r\n    case &quot;$dns_mode&quot; in\r\n        split)\r\n            dns_config=$(cat &lt;&lt;&#039;EDNS&#039;\r\n{\r\n  &quot;servers&quot;: [\r\n    {&quot;tag&quot;:&quot;google-dns&quot;,&quot;type&quot;:&quot;tls&quot;,&quot;server&quot;:&quot;8.8.8.8&quot;},\r\n    {&quot;tag&quot;:&quot;ali-dns&quot;,&quot;type&quot;:&quot;udp&quot;,&quot;server&quot;:&quot;223.5.5.5&quot;}\r\n  ],\r\n  &quot;rules&quot;: [\r\n    {&quot;rule_set&quot;:[&quot;geosite-cn&quot;],&quot;server&quot;:&quot;ali-dns&quot;}\r\n  ],\r\n  &quot;strategy&quot;:&quot;ipv4_only&quot;\r\n}\r\nEDNS\r\n)\r\n            ;;\r\n        *)\r\n            dns_config=$(cat &lt;&lt;&#039;EDNS&#039;\r\n{\r\n  &quot;servers&quot;: [\r\n    {&quot;tag&quot;:&quot;google-dns&quot;,&quot;type&quot;:&quot;tls&quot;,&quot;server&quot;:&quot;8.8.8.8&quot;},\r\n    {&quot;tag&quot;:&quot;ali-dns&quot;,&quot;type&quot;:&quot;udp&quot;,&quot;server&quot;:&quot;223.5.5.5&quot;}\r\n  ],\r\n  &quot;strategy&quot;:&quot;ipv4_only&quot;\r\n}\r\nEDNS\r\n)\r\n            ;;\r\n    esac\r\n\r\n    # \u6784\u5efa\u5165\u7ad9\r\n    local inbounds=&#039;[]&#039;\r\n    if [[ &quot;$tun_enabled&quot; == &quot;true&quot; ]]; then\r\n        inbounds=$(cat &lt;&lt;&#039;EINB&#039;\r\n[{&quot;type&quot;:&quot;tun&quot;,&quot;tag&quot;:&quot;tun-in&quot;,&quot;address&quot;:[&quot;172.19.0.1\/30&quot;],&quot;auto_route&quot;:true,&quot;strict_route&quot;:true}]\r\nEINB\r\n)\r\n    fi\r\n\r\n    # \u6784\u5efa\u51fa\u7ad9: \u5185\u7f6e direct + dns + \u7528\u6237\u81ea\u5b9a\u4e49\r\n    local builtin_outs=&#039;[{&quot;type&quot;:&quot;direct&quot;,&quot;tag&quot;:&quot;direct&quot;},{&quot;type&quot;:&quot;block&quot;,&quot;tag&quot;:&quot;block&quot;}]&#039;\r\n    local user_outs; user_outs=$(cat &quot;$OUTBOUNDS_FILE&quot;)\r\n    # \u79fb\u9664\u7528\u6237\u5b9a\u4e49\u4e2d\u4e0e\u5185\u7f6e\u91cd\u540d\u7684\r\n    user_outs=$(echo &quot;$user_outs&quot; | jq &#039;[.[]|select(.tag!=&quot;direct&quot; and .tag!=&quot;block&quot; and .tag!=&quot;dns-out&quot;)]&#039;)\r\n    local all_outs\r\n    all_outs=$(jq -n --argjson b &quot;$builtin_outs&quot; --argjson u &quot;$user_outs&quot; &#039;$u + $b&#039;)\r\n\r\n    # \u6784\u5efa\u8def\u7531\u89c4\u5219: \u5185\u7f6e\u89c4\u5219 + \u7528\u6237\u89c4\u5219\r\n    local builtin_rules=&#039;[{&quot;action&quot;:&quot;sniff&quot;},{&quot;protocol&quot;:&quot;dns&quot;,&quot;action&quot;:&quot;hijack-dns&quot;},{&quot;ip_is_private&quot;:true,&quot;action&quot;:&quot;route&quot;,&quot;outbound&quot;:&quot;direct&quot;}]&#039;\r\n    local user_rules; user_rules=$(cat &quot;$ROUTES_FILE&quot;)\r\n    local all_rules\r\n    all_rules=$(jq -n --argjson b &quot;$builtin_rules&quot; --argjson u &quot;$user_rules&quot; &#039;$b + $u&#039;)\r\n\r\n    # \u89c4\u5219\u96c6\r\n    local rule_sets; rule_sets=$(cat &quot;$RULESETS_FILE&quot;)\r\n\r\n    # \u66f4\u65b0 rule_set \u4e2d\u7684 download_detour \u4e3a\u5f53\u524d\u53ef\u7528\u4ee3\u7406\r\n    local detour; detour=$(_get_download_detour)\r\n    rule_sets=$(echo &quot;$rule_sets&quot; | jq --arg d &quot;$detour&quot; &#039;[.[]|.download_detour=$d]&#039;)\r\n\r\n    # \u7ec4\u88c5\u5b8c\u6574\u914d\u7f6e\r\n    local config\r\n    config=$(jq -n \\\r\n        --argjson dns &quot;$dns_config&quot; \\\r\n        --argjson inb &quot;$inbounds&quot; \\\r\n        --argjson out &quot;$all_outs&quot; \\\r\n        --argjson rules &quot;$all_rules&quot; \\\r\n        --argjson rsets &quot;$rule_sets&quot; \\\r\n        --arg final &quot;$default_out&quot; \\\r\n        &#039;{\r\n            log:{level:&quot;warn&quot;,timestamp:true},\r\n            dns:$dns,\r\n            inbounds:$inb,\r\n            outbounds:$out,\r\n            route:{\r\n                rules:$rules,\r\n                rule_set:$rsets,\r\n                final:$final,\r\n                auto_detect_interface:true,\r\n                default_domain_resolver:&quot;google-dns&quot;\r\n            },\r\n            experimental:{\r\n                cache_file:{enabled:true}\r\n            }\r\n        }&#039;)\r\n\r\n    # \u5982\u679c dns_mode \u662f split \u4f46 geosite-cn \u89c4\u5219\u96c6\u4e0d\u5b58\u5728\u4e8e rulesets\uff0cdns \u89c4\u5219\u4e2d\u79fb\u9664 rule_set \u5f15\u7528\r\n    if [[ &quot;$dns_mode&quot; == &quot;split&quot; ]]; then\r\n        local has_geosite\r\n        has_geosite=$(echo &quot;$rule_sets&quot; | jq &#039;[.[]|select(.tag==&quot;geosite-cn&quot;)]|length&#039;)\r\n        if [[ &quot;$has_geosite&quot; -eq 0 ]]; then\r\n            config=$(echo &quot;$config&quot; | jq &#039;.dns.rules=[]&#039;)\r\n        fi\r\n    fi\r\n\r\n    mkdir -p &quot;$(dirname &quot;$SINGBOX_CONFIG&quot;)&quot;\r\n    echo &quot;$config&quot; | jq &#039;.&#039; &gt; &quot;$SINGBOX_CONFIG&quot;\r\n}\r\n\r\ncmd_apply() {\r\n    title &quot;\u5e94\u7528\u914d\u7f6e&quot;\r\n    check_root\r\n    generate_config\r\n    info &quot;\u914d\u7f6e\u5df2\u751f\u6210: $SINGBOX_CONFIG&quot;\r\n\r\n    # \u6821\u9a8c\r\n    if command -v sing-box &amp;&gt;\/dev\/null; then\r\n        if sing-box check -c &quot;$SINGBOX_CONFIG&quot; 2&gt;\/dev\/null; then\r\n            info &quot;\u914d\u7f6e\u6821\u9a8c\u901a\u8fc7 \u2713&quot;\r\n        else\r\n            err &quot;\u914d\u7f6e\u6821\u9a8c\u5931\u8d25\uff0c\u8bf7\u68c0\u67e5\u914d\u7f6e&quot;\r\n            sing-box check -c &quot;$SINGBOX_CONFIG&quot; 2&gt;&amp;1 || true\r\n            return 1\r\n        fi\r\n        # \u786e\u4fdd\u670d\u52a1\u6587\u4ef6\u5b58\u5728\r\n        [[ -f &quot;$SERVICE_FILE&quot; ]] || _create_service\r\n        systemctl restart &quot;$SERVICE_NAME&quot; 2&gt;\/dev\/null &amp;&amp; info &quot;$SERVICE_NAME \u5df2\u91cd\u542f&quot; || warn &quot;\u91cd\u542f\u5931\u8d25&quot;\r\n    else\r\n        warn &quot;sing-box \u672a\u5b89\u88c5\uff0c\u4ec5\u751f\u6210\u4e86\u914d\u7f6e\u6587\u4ef6&quot;\r\n    fi\r\n}\r\n\r\n# ==================== \u5907\u4efd\/\u6062\u590d ====================\r\ncmd_backup() {\r\n    local file=&quot;${1:-$BACKUP_DIR\/sbroute-$(date +%Y%m%d-%H%M%S).tar.gz}&quot;\r\n    tar -czf &quot;$file&quot; -C \/ &quot;etc\/sbroute&quot; 2&gt;\/dev\/null\r\n    info &quot;\u5907\u4efd\u5df2\u4fdd\u5b58: $file&quot;\r\n    echo &quot;  \u5927\u5c0f: $(du -h &quot;$file&quot; | cut -f1)&quot;\r\n}\r\n\r\ncmd_restore() {\r\n    [[ $# -ge 1 ]] || die &quot;\u7528\u6cd5: sbroute restore &lt;file.tar.gz&gt;&quot;\r\n    local file=&quot;$1&quot;\r\n    [[ -f &quot;$file&quot; ]] || die &quot;\u6587\u4ef6\u4e0d\u5b58\u5728: $file&quot;\r\n    check_root\r\n    tar -xzf &quot;$file&quot; -C \/ 2&gt;\/dev\/null\r\n    info &quot;\u914d\u7f6e\u5df2\u6062\u590d\u81ea: $file&quot;\r\n    warn &quot;\u8bf7\u8fd0\u884c &#039;sbroute apply&#039; \u4f7f\u914d\u7f6e\u751f\u6548&quot;\r\n}\r\n\r\ncmd_export() {\r\n    if [[ -f &quot;$SINGBOX_CONFIG&quot; ]]; then\r\n        jq &#039;.&#039; &quot;$SINGBOX_CONFIG&quot;\r\n    else\r\n        generate_config\r\n        jq &#039;.&#039; &quot;$SINGBOX_CONFIG&quot;\r\n    fi\r\n}\r\n\r\ncmd_import() {\r\n    [[ $# -ge 1 ]] || die &quot;\u7528\u6cd5: sbroute import &lt;config.json&gt;&quot;\r\n    local file=&quot;$1&quot;\r\n    [[ -f &quot;$file&quot; ]] || die &quot;\u6587\u4ef6\u4e0d\u5b58\u5728: $file&quot;\r\n    check_root; ensure_files\r\n\r\n    # \u4ece\u5b8c\u6574\u914d\u7f6e\u4e2d\u63d0\u53d6\u51fa\u7ad9\u548c\u8def\u7531\r\n    local outs routes rsets\r\n    outs=$(jq &#039;[.outbounds[]|select(.type!=&quot;direct&quot; and .type!=&quot;block&quot; and .type!=&quot;dns&quot;)]&#039; &quot;$file&quot; 2&gt;\/dev\/null) || outs=&#039;[]&#039;\r\n    routes=$(jq &#039;[.route.rules[]|select(.action==&quot;route&quot; and .outbound and .outbound!=&quot;direct&quot; and .protocol==null and .ip_is_private==null)]&#039; &quot;$file&quot; 2&gt;\/dev\/null) || routes=&#039;[]&#039;\r\n    rsets=$(jq &#039;.route.rule_set \/\/ []&#039; &quot;$file&quot; 2&gt;\/dev\/null) || rsets=&#039;[]&#039;\r\n\r\n    echo &quot;$outs&quot; | jq &#039;.&#039; &gt; &quot;$OUTBOUNDS_FILE&quot;\r\n    echo &quot;$routes&quot; | jq &#039;.&#039; &gt; &quot;$ROUTES_FILE&quot;\r\n    echo &quot;$rsets&quot; | jq &#039;.&#039; &gt; &quot;$RULESETS_FILE&quot;\r\n\r\n    local out_count route_count\r\n    out_count=$(echo &quot;$outs&quot; | jq &#039;length&#039;)\r\n    route_count=$(echo &quot;$routes&quot; | jq &#039;length&#039;)\r\n    info &quot;\u5df2\u5bfc\u5165: $out_count \u4e2a\u51fa\u7ad9, $route_count \u6761\u8def\u7531\u89c4\u5219&quot;\r\n    warn &quot;\u8bf7\u8fd0\u884c &#039;sbroute apply&#039; \u4f7f\u914d\u7f6e\u751f\u6548&quot;\r\n}\r\n\r\n# ==================== \u670d\u52a1\u7ba1\u7406 ====================\r\ncmd_status() {\r\n    title &quot;SBRoute \u72b6\u6001&quot;\r\n    if command -v sing-box &amp;&gt;\/dev\/null; then\r\n        echo -e &quot;sing-box \u7248\u672c: ${CYAN}$(sing-box version 2&gt;\/dev\/null | head -1)${NC}&quot;\r\n    else\r\n        warn &quot;sing-box \u672a\u5b89\u88c5&quot;\r\n        return\r\n    fi\r\n    echo -e &quot;\u914d\u7f6e\u6587\u4ef6: ${CYAN}$SINGBOX_CONFIG${NC}&quot;\r\n    echo -e &quot;\u670d\u52a1\u540d\u79f0: ${CYAN}$SERVICE_NAME${NC}&quot;\r\n    echo &quot;&quot;\r\n    systemctl status &quot;$SERVICE_NAME&quot; --no-pager -l 2&gt;\/dev\/null || warn &quot;SBRoute \u670d\u52a1\u672a\u8fd0\u884c&quot;\r\n    # \u663e\u793a\u539f sing-box \u72b6\u6001\r\n    if systemctl is-active sing-box &amp;&gt;\/dev\/null 2&gt;&amp;1; then\r\n        echo &quot;&quot;\r\n        echo -e &quot;${GREEN}[i]${NC} \u539f sing-box \u670d\u52a1\u4e5f\u5728\u8fd0\u884c\u4e2d\uff08\u4e92\u4e0d\u5f71\u54cd\uff09&quot;\r\n    fi\r\n}\r\n\r\ncmd_log() {\r\n    local lines=&quot;${1:-50}&quot;\r\n    journalctl -u &quot;$SERVICE_NAME&quot; -n &quot;$lines&quot; --no-pager 2&gt;\/dev\/null || warn &quot;\u65e0\u6cd5\u8bfb\u53d6\u65e5\u5fd7&quot;\r\n}\r\n\r\ncmd_test() {\r\n    local tag=&quot;${1:-}&quot; url=&quot;${2:-https:\/\/www.google.com}&quot;\r\n    [[ -n &quot;$tag&quot; ]] || die &quot;\u7528\u6cd5: sbroute test &lt;outbound_tag&gt; [url]&quot;\r\n    info &quot;\u6d4b\u8bd5\u51fa\u7ad9 &#039;$tag&#039; \u8fde\u63a5\u5230 $url ...&quot;\r\n    # \u67e5\u627e\u51fa\u7ad9\u7684 server \u548c port\r\n    local out_info\r\n    out_info=$(jq --arg t &quot;$tag&quot; &#039;.[]|select(.tag==$t)&#039; &quot;$OUTBOUNDS_FILE&quot;)\r\n    [[ -n &quot;$out_info&quot; ]] || die &quot;\u51fa\u7ad9 &#039;$tag&#039; \u4e0d\u5b58\u5728&quot;\r\n    local type server port\r\n    type=$(echo &quot;$out_info&quot; | jq -r &#039;.type&#039;)\r\n    server=$(echo &quot;$out_info&quot; | jq -r &#039;.server \/\/ empty&#039;)\r\n    port=$(echo &quot;$out_info&quot; | jq -r &#039;.server_port \/\/ empty&#039;)\r\n\r\n    if [[ &quot;$type&quot; == &quot;socks&quot; &amp;&amp; -n &quot;$server&quot; ]]; then\r\n        curl -x &quot;socks5:\/\/$server:$port&quot; -o \/dev\/null -s -w &quot;HTTP %{http_code} | \u8017\u65f6 %{time_total}s\\n&quot; &quot;$url&quot; \\\r\n            &amp;&amp; info &quot;\u8fde\u63a5\u6210\u529f&quot; || err &quot;\u8fde\u63a5\u5931\u8d25&quot;\r\n    elif [[ &quot;$type&quot; == &quot;http&quot; &amp;&amp; -n &quot;$server&quot; ]]; then\r\n        curl -x &quot;http:\/\/$server:$port&quot; -o \/dev\/null -s -w &quot;HTTP %{http_code} | \u8017\u65f6 %{time_total}s\\n&quot; &quot;$url&quot; \\\r\n            &amp;&amp; info &quot;\u8fde\u63a5\u6210\u529f&quot; || err &quot;\u8fde\u63a5\u5931\u8d25&quot;\r\n    else\r\n        warn &quot;\u4ec5\u652f\u6301 SOCKS5\/HTTP \u51fa\u7ad9\u7684\u76f4\u63a5\u8fde\u901a\u6027\u6d4b\u8bd5&quot;\r\n        warn &quot;\u5176\u4ed6\u7c7b\u578b\u8bf7\u5728 apply \u540e\u68c0\u67e5 sing-box \u65e5\u5fd7&quot;\r\n    fi\r\n}\r\n\r\n# ==================== \u4ea4\u4e92\u5f0f\u83dc\u5355 ====================\r\ninteractive_menu() {\r\n    while true; do\r\n        echo &quot;&quot;\r\n        echo -e &quot;${BOLD}${CYAN}\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557${NC}&quot;\r\n        echo -e &quot;${BOLD}${CYAN}\u2551   SBRoute \u2014 VPS \u6d41\u91cf\u5206\u6d41\u7ba1\u7406 v${VERSION}  \u2551${NC}&quot;\r\n        echo -e &quot;${BOLD}${CYAN}\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255d${NC}&quot;\r\n        echo &quot;&quot;\r\n        echo -e &quot;  ${BOLD}${YELLOW}\u25b8 \u51fa\u7ad9\u7ba1\u7406${NC}&quot;\r\n        echo -e &quot;  ${GREEN}1)${NC} \u6dfb\u52a0\u51fa\u7ad9&quot;\r\n        echo -e &quot;  ${GREEN}2)${NC} \u67e5\u770b\u51fa\u7ad9\u5217\u8868&quot;\r\n        echo -e &quot;  ${GREEN}3)${NC} \u5220\u9664\u51fa\u7ad9&quot;\r\n        echo &quot;&quot;\r\n        echo -e &quot;  ${BOLD}${YELLOW}\u25b8 \u8def\u7531\u89c4\u5219${NC}&quot;\r\n        echo -e &quot;  ${GREEN}4)${NC} \u6dfb\u52a0\u8def\u7531\u89c4\u5219&quot;\r\n        echo -e &quot;  ${GREEN}5)${NC} \u67e5\u770b\u8def\u7531\u89c4\u5219&quot;\r\n        echo -e &quot;  ${GREEN}6)${NC} \u5220\u9664\u8def\u7531\u89c4\u5219&quot;\r\n        echo -e &quot;  ${GREEN}7)${NC} \u8bbe\u7f6e\u9ed8\u8ba4\u51fa\u7ad9&quot;\r\n        echo &quot;&quot;\r\n        echo -e &quot;  ${BOLD}${YELLOW}\u25b8 \u5907\u4efd\u4e0e\u914d\u7f6e${NC}&quot;\r\n        echo -e &quot;  ${GREEN}8)${NC} \u5907\u4efd\u914d\u7f6e&quot;\r\n        echo -e &quot;  ${GREEN}9)${NC} \u6062\u590d\u914d\u7f6e&quot;\r\n        echo -e &quot;  ${GREEN}10)${NC} \u5bfc\u51fa\u914d\u7f6e (JSON)&quot;\r\n        echo -e &quot;  ${GREEN}11)${NC} \u5bfc\u5165\u914d\u7f6e (JSON)&quot;\r\n        echo &quot;&quot;\r\n        echo -e &quot;  ${BOLD}${YELLOW}\u25b8 \u7cfb\u7edf\u7ba1\u7406${NC}&quot;\r\n        echo -e &quot;  ${GREEN}12)${NC} \u5b89\u88c5 sing-box&quot;\r\n        echo -e &quot;  ${GREEN}13)${NC} DNS \u8bbe\u7f6e&quot;\r\n        echo -e &quot;  ${GREEN}14)${NC} \u5e94\u7528\u9884\u8bbe\u6a21\u677f&quot;\r\n        echo -e &quot;  ${GREEN}15)${NC} \u5e94\u7528\u914d\u7f6e\u5e76\u91cd\u542f&quot;\r\n        echo -e &quot;  ${GREEN}16)${NC} \u67e5\u770b\u72b6\u6001&quot;\r\n        echo -e &quot;  ${GREEN}17)${NC} \u67e5\u770b\u65e5\u5fd7&quot;\r\n        echo -e &quot;  ${GREEN}18)${NC} \u5378\u8f7d&quot;\r\n        echo -e &quot;  ${GREEN}0)${NC}  \u9000\u51fa&quot;\r\n        echo &quot;&quot;\r\n        read -rp &quot;\u8bf7\u9009\u62e9 [0-18]: &quot; choice\r\n\r\n        case &quot;$choice&quot; in\r\n            1) _menu_add_outbound ;;\r\n            2) out_list ;;\r\n            3) _menu_del_outbound ;;\r\n            4) _menu_add_route ;;\r\n            5) route_list ;;\r\n            6) _menu_del_route ;;\r\n            7) _menu_set_default ;;\r\n            8) cmd_backup ;;\r\n            9) _menu_restore ;;\r\n            10) cmd_export ;;\r\n            11) _menu_import ;;\r\n            12) cmd_install ;;\r\n            13) _menu_dns ;;\r\n            14) _menu_preset ;;\r\n            15) cmd_apply ;;\r\n            16) cmd_status ;;\r\n            17) cmd_log ;;\r\n            18) cmd_uninstall ;;\r\n            0) echo &quot;\u518d\u89c1\uff01&quot;; exit 0 ;;\r\n            *) warn &quot;\u65e0\u6548\u9009\u62e9&quot; ;;\r\n        esac\r\n        echo &quot;&quot;\r\n        read -rp &quot;\u6309\u56de\u8f66\u7ee7\u7eed...&quot;\r\n    done\r\n}\r\n\r\n_menu_add_outbound() {\r\n    echo &quot;&quot;\r\n    echo -e &quot;  \u51fa\u7ad9\u7c7b\u578b:&quot;\r\n    echo -e &quot;  ${CYAN}1)${NC} \u7c98\u8d34\u5206\u4eab\u94fe\u63a5 (ss:\/\/ \/ vless:\/\/)&quot;\r\n    echo -e &quot;  ${CYAN}2)${NC} Shadowsocks (ss)&quot;\r\n    echo -e &quot;  ${CYAN}3)${NC} SOCKS5&quot;\r\n    echo -e &quot;  ${CYAN}4)${NC} HTTP&quot;\r\n    echo -e &quot;  ${CYAN}5)${NC} VLESS&quot;\r\n    echo -e &quot;  ${CYAN}6)${NC} VMess&quot;\r\n    echo -e &quot;  ${CYAN}7)${NC} Direct&quot;\r\n    echo -e &quot;  ${CYAN}8)${NC} Block&quot;\r\n    echo &quot;&quot;\r\n    read -rp &quot;\u9009\u62e9\u7c7b\u578b [1-8]: &quot; t\r\n    case &quot;$t&quot; in\r\n        1)\r\n            echo -e &quot;  \u7c98\u8d34\u5206\u4eab\u94fe\u63a5 (\u652f\u6301 ss:\/\/ \u548c vless:\/\/):&quot;\r\n            read -rp &quot;  \u94fe\u63a5: &quot; share_url\r\n            [[ -n &quot;$share_url&quot; ]] &amp;&amp; out_add_url &quot;$share_url&quot;\r\n            ;;\r\n        2)\r\n            read -rp &quot;Tag: &quot; tag; read -rp &quot;Server: &quot; srv; read -rp &quot;Port: &quot; port\r\n            read -rp &quot;Method (aes-128-gcm): &quot; method; method=${method:-aes-128-gcm}\r\n            read -rp &quot;Password: &quot; pw\r\n            out_add_ss &quot;$tag&quot; &quot;$srv&quot; &quot;$port&quot; &quot;$method&quot; &quot;$pw&quot;\r\n            ;;\r\n        3)\r\n            read -rp &quot;Tag: &quot; tag; read -rp &quot;Server: &quot; srv; read -rp &quot;Port: &quot; port\r\n            read -rp &quot;User (\u53ef\u9009): &quot; user; read -rp &quot;Password (\u53ef\u9009): &quot; pw\r\n            out_add_socks &quot;$tag&quot; &quot;$srv&quot; &quot;$port&quot; &quot;$user&quot; &quot;$pw&quot;\r\n            ;;\r\n        4)\r\n            read -rp &quot;Tag: &quot; tag; read -rp &quot;Server: &quot; srv; read -rp &quot;Port: &quot; port\r\n            read -rp &quot;User (\u53ef\u9009): &quot; user; read -rp &quot;Password (\u53ef\u9009): &quot; pw\r\n            out_add_http &quot;$tag&quot; &quot;$srv&quot; &quot;$port&quot; &quot;$user&quot; &quot;$pw&quot;\r\n            ;;\r\n        5)\r\n            read -rp &quot;Tag: &quot; tag; read -rp &quot;Server: &quot; srv; read -rp &quot;Port: &quot; port\r\n            read -rp &quot;UUID: &quot; uuid; read -rp &quot;Flow (\u53ef\u9009, \u5982 xtls-rprx-vision): &quot; flow\r\n            read -rp &quot;SNI (\u53ef\u9009, \u542f\u7528 TLS+Reality): &quot; sni\r\n            out_add_vless &quot;$tag&quot; &quot;$srv&quot; &quot;$port&quot; &quot;$uuid&quot; &quot;$flow&quot; &quot;$sni&quot;\r\n            ;;\r\n        6)\r\n            read -rp &quot;Tag: &quot; tag; read -rp &quot;Server: &quot; srv; read -rp &quot;Port: &quot; port\r\n            read -rp &quot;UUID: &quot; uuid; read -rp &quot;Security (auto): &quot; sec; sec=${sec:-auto}\r\n            out_add_vmess &quot;$tag&quot; &quot;$srv&quot; &quot;$port&quot; &quot;$uuid&quot; &quot;$sec&quot;\r\n            ;;\r\n        7) read -rp &quot;Tag (direct): &quot; tag; out_add_simple &quot;direct&quot; &quot;${tag:-direct}&quot; ;;\r\n        8) read -rp &quot;Tag (block): &quot; tag; out_add_simple &quot;block&quot; &quot;${tag:-block}&quot; ;;\r\n        *) warn &quot;\u65e0\u6548\u9009\u62e9&quot; ;;\r\n    esac\r\n}\r\n\r\n_menu_del_outbound() {\r\n    out_list\r\n    echo &quot;&quot;\r\n    read -rp &quot;\u8f93\u5165\u8981\u5220\u9664\u7684 Tag: &quot; tag\r\n    [[ -n &quot;$tag&quot; ]] &amp;&amp; out_del &quot;$tag&quot;\r\n}\r\n\r\n_menu_add_route() {\r\n    out_list\r\n    echo &quot;&quot;\r\n    read -rp &quot;\u76ee\u6807\u51fa\u7ad9 Tag: &quot; out_tag\r\n    [[ -n &quot;$out_tag&quot; ]] || return\r\n\r\n    echo -e &quot;\\n  \u5339\u914d\u6761\u4ef6 (\u7559\u7a7a\u8df3\u8fc7):&quot;\r\n    read -rp &quot;  Domain (\u9017\u53f7\u5206\u9694): &quot; domains\r\n    read -rp &quot;  Domain Suffix (\u9017\u53f7\u5206\u9694): &quot; suffixes\r\n    read -rp &quot;  Domain Keyword (\u9017\u53f7\u5206\u9694): &quot; keywords\r\n    read -rp &quot;  GeoIP (\u9017\u53f7\u5206\u9694, \u5982 cn,us,jp): &quot; geoip\r\n    read -rp &quot;  GeoSite (\u9017\u53f7\u5206\u9694, \u5982 google,cn,netflix): &quot; geosite\r\n    read -rp &quot;  Rule Set URL (\u5b8c\u6574\u94fe\u63a5): &quot; ruleset\r\n\r\n    local args=(&quot;$out_tag&quot;)\r\n    [[ -n &quot;$domains&quot; ]]  &amp;&amp; args+=(--domain &quot;$domains&quot;)\r\n    [[ -n &quot;$suffixes&quot; ]] &amp;&amp; args+=(--suffix &quot;$suffixes&quot;)\r\n    [[ -n &quot;$keywords&quot; ]] &amp;&amp; args+=(--keyword &quot;$keywords&quot;)\r\n    [[ -n &quot;$geoip&quot; ]]    &amp;&amp; args+=(--geoip &quot;$geoip&quot;)\r\n    [[ -n &quot;$geosite&quot; ]]  &amp;&amp; args+=(--geosite &quot;$geosite&quot;)\r\n    [[ -n &quot;$ruleset&quot; ]]  &amp;&amp; args+=(--ruleset &quot;$ruleset&quot;)\r\n\r\n    if [[ ${#args[@]} -le 1 ]]; then\r\n        warn &quot;\u81f3\u5c11\u9700\u8981\u6307\u5b9a\u4e00\u4e2a\u5339\u914d\u6761\u4ef6&quot;\r\n        return\r\n    fi\r\n    route_add &quot;${args[@]}&quot;\r\n}\r\n\r\n_menu_del_route() {\r\n    route_list\r\n    echo &quot;&quot;\r\n    read -rp &quot;\u8f93\u5165\u8981\u5220\u9664\u7684\u5e8f\u53f7: &quot; idx\r\n    [[ -n &quot;$idx&quot; ]] &amp;&amp; route_del &quot;$idx&quot;\r\n}\r\n\r\n_menu_set_default() {\r\n    out_list\r\n    echo &quot;&quot;\r\n    local cur; cur=$(jq -r &#039;.default_outbound \/\/ &quot;direct&quot;&#039; &quot;$SETTINGS_FILE&quot;)\r\n    echo -e &quot;\u5f53\u524d\u9ed8\u8ba4\u51fa\u7ad9: ${CYAN}$cur${NC}&quot;\r\n    read -rp &quot;\u65b0\u7684\u9ed8\u8ba4\u51fa\u7ad9 Tag: &quot; tag\r\n    [[ -n &quot;$tag&quot; ]] &amp;&amp; route_default &quot;$tag&quot;\r\n}\r\n\r\n_menu_dns() {\r\n    local cur; cur=$(jq -r &#039;.dns_mode \/\/ &quot;basic&quot;&#039; &quot;$SETTINGS_FILE&quot;)\r\n    echo -e &quot;\\n\u5f53\u524d DNS \u6a21\u5f0f: ${CYAN}$cur${NC}&quot;\r\n    echo -e &quot;  ${CYAN}1)${NC} basic  \u2014 Google DNS&quot;\r\n    echo -e &quot;  ${CYAN}2)${NC} split  \u2014 \u56fd\u5185\u5206\u6d41&quot;\r\n    read -rp &quot;\u9009\u62e9 [1-2]: &quot; c\r\n    case &quot;$c&quot; in\r\n        1) cmd_dns basic ;;\r\n        2) cmd_dns split ;;\r\n        *) warn &quot;\u65e0\u6548\u9009\u62e9&quot; ;;\r\n    esac\r\n}\r\n\r\n_menu_preset() {\r\n    echo -e &quot;\\n  \u9884\u8bbe\u6a21\u677f:&quot;\r\n    echo -e &quot;  ${CYAN}1)${NC} cn-direct  \u2014 \u56fd\u5185\u76f4\u8fde&quot;\r\n    echo -e &quot;  ${CYAN}2)${NC} ads-block  \u2014 \u5e7f\u544a\u62e6\u622a&quot;\r\n    read -rp &quot;\u9009\u62e9 [1-2]: &quot; c\r\n    case &quot;$c&quot; in\r\n        1) cmd_preset cn-direct ;;\r\n        2) cmd_preset ads-block ;;\r\n        *) warn &quot;\u65e0\u6548\u9009\u62e9&quot; ;;\r\n    esac\r\n}\r\n\r\n_menu_restore() {\r\n    echo &quot;&quot;; ls -la &quot;$BACKUP_DIR&quot;\/ 2&gt;\/dev\/null || warn &quot;\u65e0\u5907\u4efd\u6587\u4ef6&quot;\r\n    echo &quot;&quot;\r\n    read -rp &quot;\u8f93\u5165\u5907\u4efd\u6587\u4ef6\u8def\u5f84: &quot; file\r\n    [[ -n &quot;$file&quot; ]] &amp;&amp; cmd_restore &quot;$file&quot;\r\n}\r\n\r\n_menu_import() {\r\n    read -rp &quot;\u8f93\u5165 JSON \u914d\u7f6e\u6587\u4ef6\u8def\u5f84: &quot; file\r\n    [[ -n &quot;$file&quot; ]] &amp;&amp; cmd_import &quot;$file&quot;\r\n}\r\n\r\n# ==================== \u5e2e\u52a9\u4fe1\u606f ====================\r\nshow_help() {\r\n    cat &lt;&lt;EOF\r\n${BOLD}SBRoute v${VERSION}${NC} \u2014 VPS \u6d41\u91cf\u5206\u6d41\u7ba1\u7406\u5de5\u5177\r\n\r\n${BOLD}\u7528\u6cd5:${NC}\r\n  sbroute                              \u4ea4\u4e92\u5f0f\u83dc\u5355\r\n  sbroute install                      \u5b89\u88c5 sing-box\r\n  sbroute uninstall                    \u5378\u8f7d\r\n\r\n${BOLD}\u51fa\u7ad9\u7ba1\u7406:${NC}\r\n  sbroute out add ss &lt;tag&gt; &lt;server&gt; &lt;port&gt; &lt;method&gt; &lt;password&gt;\r\n  sbroute out add socks &lt;tag&gt; &lt;server&gt; &lt;port&gt; [user] [pass]\r\n  sbroute out add http &lt;tag&gt; &lt;server&gt; &lt;port&gt; [user] [pass]\r\n  sbroute out add vless &lt;tag&gt; &lt;server&gt; &lt;port&gt; &lt;uuid&gt; [flow] [sni]\r\n  sbroute out add vmess &lt;tag&gt; &lt;server&gt; &lt;port&gt; &lt;uuid&gt; [security]\r\n  sbroute out add direct [tag]\r\n  sbroute out add block [tag]\r\n  sbroute out add url &lt;\u5206\u4eab\u94fe\u63a5&gt;        \u89e3\u6790 ss:\/\/ \/ vless:\/\/ \u94fe\u63a5\u6dfb\u52a0\r\n  sbroute out list                     \u5217\u51fa\u6240\u6709\u51fa\u7ad9\r\n  sbroute out del &lt;tag&gt;                \u5220\u9664\u51fa\u7ad9\r\n  sbroute out show &lt;tag&gt;               \u67e5\u770b\u51fa\u7ad9\u8be6\u60c5\r\n\r\n${BOLD}\u8def\u7531\u89c4\u5219:${NC}\r\n  sbroute route add &lt;tag&gt; --domain\/--suffix\/--keyword\/--regex\/--ip-cidr\/--port\/--process &lt;v&gt;\r\n  sbroute route add &lt;tag&gt; --geoip &lt;codes&gt;        GeoIP \u89c4\u5219\u96c6 (\u5982 cn,us,jp)\r\n  sbroute route add &lt;tag&gt; --geosite &lt;names&gt;      GeoSite \u89c4\u5219\u96c6 (\u5982 google,cn,netflix,category-ads-all)\r\n  sbroute route add &lt;tag&gt; --ruleset &lt;url&gt;        \u81ea\u5b9a\u4e49\u89c4\u5219\u96c6 URL\r\n  sbroute route list                   \u5217\u51fa\u8def\u7531\u89c4\u5219\r\n  sbroute route del &lt;index&gt;            \u5220\u9664\u8def\u7531\u89c4\u5219\r\n  sbroute route default &lt;tag&gt;          \u8bbe\u7f6e\u9ed8\u8ba4\u51fa\u7ad9\r\n\r\n${BOLD}DNS:${NC}\r\n  sbroute dns basic                    Google DNS\r\n  sbroute dns split                    \u56fd\u5185\u5206\u6d41 DNS\r\n\r\n${BOLD}\u9884\u8bbe:${NC}\r\n  sbroute preset cn-direct             \u56fd\u5185\u76f4\u8fde\u89c4\u5219\u96c6\r\n  sbroute preset ads-block             \u5e7f\u544a\u62e6\u622a\u89c4\u5219\u96c6\r\n\r\n${BOLD}\u914d\u7f6e:${NC}\r\n  sbroute apply                        \u751f\u6210\u914d\u7f6e\u5e76\u91cd\u542f\r\n  sbroute backup [file]                \u5907\u4efd\r\n  sbroute restore &lt;file&gt;               \u6062\u590d\r\n  sbroute export                       \u5bfc\u51fa JSON\r\n  sbroute import &lt;file&gt;                \u5bfc\u5165 JSON\r\n\r\n${BOLD}\u670d\u52a1:${NC}\r\n  sbroute status                       \u67e5\u770b\u72b6\u6001\r\n  sbroute start|stop|restart           \u670d\u52a1\u63a7\u5236\r\n  sbroute log [lines]                  \u67e5\u770b\u65e5\u5fd7\r\n  sbroute test &lt;tag&gt; [url]             \u8fde\u901a\u6027\u6d4b\u8bd5\r\nEOF\r\n}\r\n\r\n# ==================== \u4e3b\u5165\u53e3 ====================\r\nmain() {\r\n    local cmd=&quot;${1:-}&quot;\r\n    [[ -n &quot;$cmd&quot; ]] &amp;&amp; shift\r\n\r\n    # \u975e help\/version \u547d\u4ee4\u9700\u8981 root \u548c\u521d\u59cb\u5316\r\n    case &quot;$cmd&quot; in\r\n        &quot;&quot;|-h|--help|help|version|-v|--version) ;;\r\n        *) check_root; check_jq; ensure_files ;;\r\n    esac\r\n\r\n    case &quot;$cmd&quot; in\r\n        &quot;&quot;) interactive_menu ;;\r\n        install)   cmd_install ;;\r\n        uninstall) cmd_uninstall ;;\r\n        out|outbound)\r\n            local sub=&quot;${1:-}&quot;; shift 2&gt;\/dev\/null || true\r\n            case &quot;$sub&quot; in\r\n                add)  out_add &quot;$@&quot; ;;\r\n                list) out_list ;;\r\n                del)  out_del &quot;$@&quot; ;;\r\n                show) out_show &quot;$@&quot; ;;\r\n                *)    die &quot;\u7528\u6cd5: sbroute out &lt;add|list|del|show&gt;&quot; ;;\r\n            esac\r\n            ;;\r\n        route)\r\n            local sub=&quot;${1:-}&quot;; shift 2&gt;\/dev\/null || true\r\n            case &quot;$sub&quot; in\r\n                add)     route_add &quot;$@&quot; ;;\r\n                list)    route_list ;;\r\n                del)     route_del &quot;$@&quot; ;;\r\n                default) route_default &quot;$@&quot; ;;\r\n                *)       die &quot;\u7528\u6cd5: sbroute route &lt;add|list|del|default&gt;&quot; ;;\r\n            esac\r\n            ;;\r\n        dns)    cmd_dns &quot;$@&quot; ;;\r\n        preset) cmd_preset &quot;$@&quot; ;;\r\n        apply)  cmd_apply ;;\r\n        backup) cmd_backup &quot;$@&quot; ;;\r\n        restore) cmd_restore &quot;$@&quot; ;;\r\n        export) cmd_export ;;\r\n        import) cmd_import &quot;$@&quot; ;;\r\n        status) cmd_status ;;\r\n        start)  check_root; [[ -f &quot;$SERVICE_FILE&quot; ]] || _create_service; systemctl start &quot;$SERVICE_NAME&quot; &amp;&amp; info &quot;$SERVICE_NAME \u5df2\u542f\u52a8&quot; ;;\r\n        stop)   check_root; systemctl stop &quot;$SERVICE_NAME&quot; &amp;&amp; info &quot;$SERVICE_NAME \u5df2\u505c\u6b62&quot; ;;\r\n        restart) check_root; [[ -f &quot;$SERVICE_FILE&quot; ]] || _create_service; systemctl restart &quot;$SERVICE_NAME&quot; &amp;&amp; info &quot;$SERVICE_NAME \u5df2\u91cd\u542f&quot; ;;\r\n        log)    cmd_log &quot;$@&quot; ;;\r\n        test)   cmd_test &quot;$@&quot; ;;\r\n        -h|--help|help) show_help ;;\r\n        -v|--version|version) echo &quot;SBRoute v${VERSION}&quot; ;;\r\n        *) err &quot;\u672a\u77e5\u547d\u4ee4: $cmd&quot;; show_help; exit 1 ;;\r\n    esac\r\n}\r\n\r\nmain &quot;$@&quot;<\/code><\/pre><\/div><br \/>\n<\/article>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>SBRoute \u2014 \u8f7b\u91cf\u7ea7 sing-box \u6d41\u91cf\u5206\u6d41\u7ba1\u7406\u5668 \u719f\u6089 sing-box \u7684\u670b\u53cb\u90fd\u77e5\u9053\uff0c\u5b83\u7684\u529f\u80fd\u6781<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[],"special":[],"class_list":["post-51","post","type-post","status-publish","format-standard","hentry","category-article"],"_links":{"self":[{"href":"https:\/\/hi.tionmon.de\/index.php?rest_route=\/wp\/v2\/posts\/51","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/hi.tionmon.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/hi.tionmon.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/hi.tionmon.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/hi.tionmon.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=51"}],"version-history":[{"count":4,"href":"https:\/\/hi.tionmon.de\/index.php?rest_route=\/wp\/v2\/posts\/51\/revisions"}],"predecessor-version":[{"id":56,"href":"https:\/\/hi.tionmon.de\/index.php?rest_route=\/wp\/v2\/posts\/51\/revisions\/56"}],"wp:attachment":[{"href":"https:\/\/hi.tionmon.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=51"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/hi.tionmon.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=51"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/hi.tionmon.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=51"},{"taxonomy":"special","embeddable":true,"href":"https:\/\/hi.tionmon.de\/index.php?rest_route=%2Fwp%2Fv2%2Fspecial&post=51"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}