KernelSU .ksurc 定制 su 启动环境:原理、配置与避坑指南
1. 从一次“su 环境不听话”的排查说起
.ksurc这个东西,很多人第一次听说是在翻 KernelSU 的模块目录时,看到一个不起眼的点文件躺在/data/adb/下面,既没有扩展名说明,也没有官方文档大书特书。我最初也把它当成一个可有可无的残留文件,直到有一次在调试一个需要特定环境变量的 root 脚本时,发现每次su进去之后PATH都被重置、自定义的LD_LIBRARY_PATH莫名其妙丢失、连umask都不是我想要的默认值,才回过头来认真研究这个文件到底管什么。
这篇内容就是围绕KernelSU 的.ksurc定制 su 启动环境展开的深度解析。它适合已经刷入 KernelSU、日常会用su执行脚本或调试的中阶用户,也适合那些想搞清楚“为什么我的 root shell 环境和别人不一样”的折腾党。核心要解决的问题很具体:如何让每一次通过 KernelSU 获得的 root shell,都自动带上你想要的变量、别名、路径和工作目录,而不是每次手动 export 一遍。关键词就三个:KernelSU、.ksurc、su 启动环境。下面我会从机制、写法、踩坑、验证四个层面把它拆开讲透,尽量做到你照着抄就能用。
先说结论性的判断:.ksurc本质上是 KernelSU 在拉起 root shell 时读取的一个启动脚本,作用类似于桌面环境里的.bashrc或登录 shell 的 profile 文件,但它生效的时机、作用范围、以及和系统原有 shell 配置的优先级关系,跟普通的 rc 文件并不完全一样。理解这一点,后面所有的定制才不会翻车。
2..ksurc到底在 su 的哪个环节被读取
2.1 一次 su 请求的完整生命周期
要搞明白.ksurc为什么有时候生效、有时候不生效,得先知道 KernelSU 处理一次su请求大致走了哪几步。我用一个简化的链路来描述,方便你建立心智模型:
- 用户在终端或脚本里执行
su,这个请求被送到 KernelSU 的管理层; - 管理层完成授权判断(是否允许该 uid 提权、是否命中授权列表);
- 授权通过后,KernelSU 准备一个 root 身份的 shell 进程;
- 在 shell 正式进入交互或执行命令之前,它会去检查
/data/adb/.ksurc是否存在; - 如果存在且可读,就把它作为启动脚本注入到当前 shell 的初始化流程里;
- 之后才是用户命令真正开始执行。
关键点在第 4 步和第 5 步之间。.ksurc不是被“source 到你的交互 shell 里”这么简单,它更像是被当作一段预置脚本,在 shell 拿到控制权之前就跑完了。这意味着你在里面写的export、alias、cd这些操作,会直接影响后续 shell 的初始状态,但它的执行上下文和你手动敲命令的上下文未必完全一致——这一点是很多坑的根源。
2.2 它和.bashrc、profile的优先级关系
很多人会问:我系统里已经有/etc/profile、用户目录下的.bashrc,再搞一个.ksurc会不会打架?实测下来的规律是这样的:
.ksurc的注入时机通常早于交互 shell 读取用户级 rc 文件;- 如果你在
.ksurc里设置的变量,后续又被.bashrc里的同名变量覆盖,那最终生效的是.bashrc的值; - 反过来,如果
.bashrc里没有碰这个变量,.ksurc的设置就会保留下来。
所以正确的定位是:.ksurc负责“root 环境的基础底座”,.bashrc负责“交互层的个性化”。把那些“每次 su 都必须存在、且不希望被交互配置干扰”的东西放.ksurc,比如基础PATH补全、必要的库路径、安全相关的umask;把纯交互的别名、提示符美化放.bashrc。两者分工明确,就不会互相踩。
提示:不要试图在
.ksurc里做太重的操作,比如启动后台服务、挂载文件系统。它的执行窗口很短,重操作容易拖慢每一次 su 的响应,甚至在某些机型上导致 shell 初始化超时。
2.3 为什么它放在/data/adb/而不是/system或用户目录
这个路径选择其实很有讲究。/data/adb/是 KernelSU 相关持久化数据的聚集地,特点是:可写、重启保留、且不随系统分区只读限制。放在这里,你不需要重新挂载 system 分区就能改,改完下一次 su 立即生效,非常适合频繁调试。相比之下,如果你把配置写进/system/etc之类的地方,不仅要处理只读挂载,还可能因为系统更新被覆盖。
另一个隐含好处是权限边界清晰。/data/adb/本身对普通应用不可见,.ksurc放在这里天然就带了一层隔离,不用担心被随意读取。这也是为什么我建议你不要把这个文件挪到/sdcard下再软链过来——软链在某些 shell 初始化阶段解析时机不对,容易读不到。
3. 写一份能用的.ksurc:从最小可用到进阶定制
3.1 最小可用骨架与文件权限
先给一个我日常用的最小骨架,你可以直接照着建:
# /data/adb/.ksurc export PATH="/data/adb/ksu/bin:/system/bin:/system/xbin:$PATH" export HOME="/data/adb" umask 022三行分别解决三件事:补全 KernelSU 自己的工具目录到PATH、把 root 的HOME指到一个可写且持久的位置、设定一个合理的默认umask。别小看这三行,很多“命令找不到”“配置文件写到奇怪地方”“新建文件权限诡异”的问题,根源都在这。
创建和赋权建议这样操作:
su -c 'touch /data/adb/.ksurc' su -c 'chmod 644 /data/adb/.ksurc' su -c 'chown root:root /data/adb/.ksurc'权限给644就够了,属主 root。有人喜欢给600,理论上更严,但要注意某些注入路径下如果读取身份不是纯 root,600可能导致读不到,实测644兼容性最好。改完之后不需要重启,下一次新开 su 会话就会读取。
3.2 环境变量定制的取舍逻辑
环境变量是.ksurc最核心的用途,但不是什么变量都往里塞。我的取舍原则是:只放“跨会话必须一致、且不希望被交互配置污染”的变量。举几个典型:
| 变量 | 是否建议放.ksurc | 理由 |
|---|---|---|
PATH | 建议 | 保证基础命令可寻址,避免每次手动补 |
HOME | 建议 | 决定配置文件落点,必须稳定 |
LD_LIBRARY_PATH | 谨慎 | 放错顺序会导致系统库被自定义库顶掉,引发崩溃 |
TMPDIR | 建议 | 指向可写目录,避免临时文件写到只读分区 |
PS1 | 不建议 | 纯交互美化,放.bashrc更合适 |
LANG | 视情况 | 影响脚本输出编码,需要统一时可放 |
LD_LIBRARY_PATH那一行我要特别强调:如果你确实需要它,一定要把系统库路径放在自定义路径之后,也就是让系统库优先命中。顺序写反了,轻则某个命令行为异常,重则 shell 直接起不来,表现为 su 之后黑屏或立即退出。我踩过这个坑,排查了半天才发现是库路径顺序问题。
3.3 别名与函数的边界:能放但别贪
.ksurc里写alias和 shell 函数是可行的,但我建议克制。原因是别名只在交互式 shell 里才有意义,而.ksurc的注入上下文未必是交互式的——如果你在脚本里调用su -c 'xxx',别名根本不生效。所以:
- 纯交互用的别名(比如
alias ll='ls -al'),放.bashrc; - 需要在非交互场景也生效的逻辑,写成函数或独立脚本,并在
.ksurc里确保其所在目录在PATH中。
一个实用技巧是把常用的一批 root 操作封装成脚本放到/data/adb/ksu/bin/下,.ksurc只负责把这个目录加进PATH。这样无论交互还是脚本调用,都能直接敲命令名,比别名可靠得多。
3.4 工作目录与 umask 的隐性影响
cd到某个目录这件事,在.ksurc里要慎用。因为如果目标目录在某些场景下不存在(比如外置存储还没挂载),cd失败可能导致后续脚本中断。稳妥写法是加判断:
[ -d /data/adb/workspace ] && cd /data/adb/workspaceumask则是个容易被忽视但影响深远的设置。默认022意味着新建文件是644、目录是755,对 root 场景通常够用。如果你在做一些需要更严格权限的调试,可以设077,但要注意这会让某些依赖组权限的工具行为异常。改umask之前,先想清楚你后续要创建什么类型的文件。
4. 那些让我返工三次的坑
4.1 语法错误导致 su 静默失败
最坑的一类问题是.ksurc里写了语法错误,结果不是报错,而是su 直接静默失败或者行为异常。因为注入阶段没有友好的错误提示,你只会看到“su 之后什么都没发生”或者“shell 秒退”。我的排查套路是:
- 先用
su -c 'echo ok'测试基础提权是否正常; - 如果异常,临时把
.ksurc改名(mv .ksurc .ksurc.bak),再测一次; - 如果改名后恢复正常,基本锁定是
.ksurc内容问题; - 用
sh -n /data/adb/.ksurc做语法检查,能揪出大部分括号、引号不匹配。
这个“改名对照法”是我排查此类问题最快的手段,比逐行注释高效得多。
4.2 变量展开时机与引号陷阱
.ksurc里写export FOO=$BAR时,$BAR的展开发生在注入执行的那一刻,而不是你后续使用的那一刻。如果BAR在注入时还没定义,FOO就是空值。更隐蔽的是引号问题:
# 危险写法:路径含空格时会被拆开 export MYDIR=/data/adb/my folder # 正确写法 export MYDIR="/data/adb/my folder"路径里带空格在 Android 上不算常见,但一旦出现,不加引号就会导致变量被截断,后续所有依赖它的操作全部错位。养成给路径变量加引号的习惯,能省掉大量莫名其妙的 bug。
4.3 覆盖了不该覆盖的 PATH
我见过有人为了“让某个工具优先”,在.ksurc里写export PATH="/my/tool:$PATH",结果把自定义目录放到了系统目录前面,导致系统自带的同名命令被顶掉,行为完全变样。正确做法是明确知道自己在覆盖什么:要么把自定义目录放后面,要么用绝对路径调用,不要盲目前置。PATH 的顺序就是命令解析的优先级,改它等于改整个 shell 的命令语义。
4.4 修改后“不生效”的三种真实原因
改完.ksurc发现没变化,先别怀疑人生,按这个顺序排查:
- 旧会话未退出:已经开着的 su shell 不会重新读取,必须退出重进;
- 被后续 rc 覆盖:检查
.bashrc或系统 profile 是否又改了同名变量; - 文件编码或换行问题:从电脑复制过来的文件如果带 BOM 或 CRLF 换行,注入时可能解析异常,用
dos2unix或编辑器转成 LF。
这三种我全踩过,尤其是第三种,肉眼完全看不出来,只有用file或十六进制查看才发现行尾是\r\n。
5. 验证与回滚:让每次改动都可控
5.1 一套五分钟的验证清单
每次改完.ksurc,我固定跑这套检查,确认没有引入回归:
su -c 'echo $PATH' su -c 'echo $HOME' su -c 'umask' su -c 'command -v 你自定义的命令'四条命令分别验证路径、家目录、权限掩码、自定义命令可达性。全部符合预期,才算这次改动合格。如果哪条不对,回到上一节对照排查。这套清单的价值在于把“感觉能用”变成“确认能用”,避免带着隐患继续往下折腾。
5.2 保留一份可回滚的备份
.ksurc出问题时最怕的是“改坏了又记不清原来写了啥”。我的习惯是每次大改前先备份:
su -c 'cp /data/adb/.ksurc /data/adb/.ksurc.bak'出问题直接cp回来,十秒恢复。备份文件放在同目录即可,注意别让备份文件本身被误读——它没有.ksurc这个精确名字,不会被注入流程命中,安全。
5.3 用独立脚本承载复杂逻辑
当定制逻辑超过十几行,我强烈建议把主体逻辑抽到一个独立脚本(比如/data/adb/ksu/bin/init-env.sh),.ksurc里只留一行调用:
[ -f /data/adb/ksu/bin/init-env.sh ] && . /data/adb/ksu/bin/init-env.sh好处有三:独立脚本可以单独做语法检查和单元测试;.ksurc保持极简,降低注入阶段出错概率;逻辑复用方便,别的场景也能 source 同一份脚本。这是我从“把所有东西堆进.ksurc”转向“分层管理”之后最大的收益。
6. 把 su 环境当成一份可维护的配置来对待
折腾.ksurc这段时间,我最大的体会是:root shell 的环境不该是每次临时拼凑的,而应该是一份有版本、有备份、有验证的配置。很多人把环境变量当成随手 export 的一次性操作,结果换个会话就丢,脚本时灵时不灵,排查成本极高。把.ksurc用好,本质上是把这份“隐性知识”固化下来,让每一次 su 都从一个确定的状态出发。
几个我长期坚持的习惯分享给你:改动前备份、改动后跑验证清单、复杂逻辑抽独立脚本、PATH 和库路径的顺序永远多想一步。这几条不复杂,但能挡掉绝大多数返工。至于更进阶的玩法,比如按不同授权身份加载不同的环境片段,可以在独立脚本里根据$UID做分支,思路是一样的——先保证底座稳定,再谈个性化。