Android设备adb shell命令行提示符定制修改

如何定制修改安卓adb shell的命令行提示符
Views: 286
15 0
Read Time:2 Minute, 26 Second

当我们通过adb shell进入Android的shell环境时,命令行左侧都会显示当前的设备名。如下所示:

c1200:/ $
c1200:/ $

在Linux环境下,当我们通过SSH连接进入Linux Shell时,命令行左侧同样会显示当前设备的名称,不同的是,Linux环境下显示的设备名称,其实是对应当前Linux设备的Hostname与User相结合,而安卓默认的命令行提示符则简单一些。

在Linux环境中,命令行提示符的显示由PS1环境变量决定,Android内同样如此,这里我们查看一下PS1环境变量:

PS C:\Users\bst> adb shell
c1200:/ # echo $PS1
${| local e=$? (( e )) && REPLY+="$e|" return $e }$USER@$HOSTNAME:${PWD:-?} #
c1200:/ #
PS C:\Users\bst> adb shell c1200:/ # echo $PS1 ${| local e=$? (( e )) && REPLY+="$e|" return $e }$USER@$HOSTNAME:${PWD:-?} # c1200:/ #

该环境变量的值设定的含义具体解析如下:

${| … }:这是一种形式的命令替换,它执行其中的命令并将其输出作为字符串插入到PS1中。在这个情况下,整个 ${| … } 结构实际上是用来设置提示符的。

local e=$?:这一行声明了一个局部变量 e 来存储上一个命令的退出状态,熟悉shell的朋友应该知道$? 是一个特殊变量,保存了上一个执行命令的退出状态码。

(( e )) && REPLY+=”$e|”:这是一个条件表达式,它检查变量 e 的值是否为非零。如果是,它将上一个命令的退出状态码附加到提示符字符串 REPLY 中,以及一个竖线(|),表示非零退出状态码。

return $e:这一行会返回上一个命令的退出状态码。这意味着,如果前一个命令失败(退出状态码非零),那么整个提示符将会包含该失败命令的退出状态码,并且Shell的退出状态码将会被设置为该值。

$HOSTNAME:这是主机名变量,用于显示当前主机的名称。

${PWD:-?}:这是当前工作目录的变量。${PWD} 会显示当前工作目录的绝对路径。如果当前工作目录无法获取,它会显示一个问号(?)作为占位符。

所以,综上所述,这个 PS1 设置的含义是:显示主机名、当前工作目录的绝对路径,并在提示符中显示上一个命令的退出状态码(如果存在),以及一个竖线分隔符;那么PS1是如何被设置的呢,关于这部分的设置逻辑,这里我们需要参考sh的源码:

//external/mksh/src/main.c
if (Flag(FLOGIN))
include(substitute("$HOME/.profile", 0), 0, NULL, true);
if (Flag(FTALKING)) {
cp = substitute("${ENV:-" MKSHRC_PATH "}", DOTILDE);
if (cp[0] != '\0')
// include属于自定义函数
include(cp, 0, NULL, true);
}
//include为自定义实现的函数,实现了对shell环境变量的配置
include(const char *name, int argc, const char **argv, bool intr_ok)
{
Source *volatile s = NULL;
struct shf *shf;
const char **volatile old_argv;
volatile int old_argc;
int i;
shf = shf_open(name, O_RDONLY | O_MAYEXEC, 0, SHF_MAPHI | SHF_CLEXEC);
if (shf == NULL)
return (-1);
if (argv) {
old_argv = e->loc->argv;
old_argc = e->loc->argc;
} else {
old_argv = NULL;
old_argc = 0;
}
newenv(E_INCL);
if ((i = kshsetjmp(e->jbuf))) {
quitenv(s ? s->u.shf : NULL);
if (old_argv) {
e->loc->argv = old_argv;
e->loc->argc = old_argc;
}
switch (i) {
case LRETURN:
case LERROR:
case LERREXT:
/* see below */
return (exstat & 0xFF);
case LINTR:
/*
* intr_ok is set if we are including .profile or $ENV.
* If user ^Cs out, we don't want to kill the shell...
*/
if (intr_ok && ((exstat & 0xFF) - 128) != SIGTERM)
return (1);
/* FALLTHROUGH */
case LEXIT:
case LLEAVE:
case LSHELL:
unwind(i);
/* NOTREACHED */
default:
internal_errorf(Tunexpected_type, Tunwind, Tsource, i);
/* NOTREACHED */
}
}
if (argv) {
e->loc->argv = argv;
e->loc->argc = argc;
}
s = pushs(SFILE, ATEMP);
s->u.shf = shf;
strdupx(s->file, name, ATEMP);
i = shell(s, 1);
quitenv(s->u.shf);
if (old_argv) {
e->loc->argv = old_argv;
e->loc->argc = old_argc;
}
/* & 0xff to ensure value not -1 */
return (i & 0xFF);
}
//external/mksh/src/main.c if (Flag(FLOGIN)) include(substitute("$HOME/.profile", 0), 0, NULL, true); if (Flag(FTALKING)) { cp = substitute("${ENV:-" MKSHRC_PATH "}", DOTILDE); if (cp[0] != '\0') // include属于自定义函数 include(cp, 0, NULL, true); } //include为自定义实现的函数,实现了对shell环境变量的配置 include(const char *name, int argc, const char **argv, bool intr_ok) { Source *volatile s = NULL; struct shf *shf; const char **volatile old_argv; volatile int old_argc; int i; shf = shf_open(name, O_RDONLY | O_MAYEXEC, 0, SHF_MAPHI | SHF_CLEXEC); if (shf == NULL) return (-1); if (argv) { old_argv = e->loc->argv; old_argc = e->loc->argc; } else { old_argv = NULL; old_argc = 0; } newenv(E_INCL); if ((i = kshsetjmp(e->jbuf))) { quitenv(s ? s->u.shf : NULL); if (old_argv) { e->loc->argv = old_argv; e->loc->argc = old_argc; } switch (i) { case LRETURN: case LERROR: case LERREXT: /* see below */ return (exstat & 0xFF); case LINTR: /* * intr_ok is set if we are including .profile or $ENV. * If user ^Cs out, we don't want to kill the shell... */ if (intr_ok && ((exstat & 0xFF) - 128) != SIGTERM) return (1); /* FALLTHROUGH */ case LEXIT: case LLEAVE: case LSHELL: unwind(i); /* NOTREACHED */ default: internal_errorf(Tunexpected_type, Tunwind, Tsource, i); /* NOTREACHED */ } } if (argv) { e->loc->argv = argv; e->loc->argc = argc; } s = pushs(SFILE, ATEMP); s->u.shf = shf; strdupx(s->file, name, ATEMP); i = shell(s, 1); quitenv(s->u.shf); if (old_argv) { e->loc->argv = old_argv; e->loc->argc = old_argc; } /* & 0xff to ensure value not -1 */ return (i & 0xFF); }

这里MKSHRC_PATH的值为/system/etc/mkshrc,其内容为:

set +o nohup
if (( USER_ID )); then PS1='$'; else PS1='#'; fi
PS4='[$EPOCHREALTIME] '; PS1='${|
local e=$?
(( e )) && REPLY+="$e|"
return $e
}$HOSTNAME:${PWD:-?} '"$PS1 "
set +o nohup if (( USER_ID )); then PS1='$'; else PS1='#'; fi PS4='[$EPOCHREALTIME] '; PS1='${| local e=$? (( e )) && REPLY+="$e|" return $e }$HOSTNAME:${PWD:-?} '"$PS1 "

所以这里PS1的设置逻辑其实是通过sh解析/system/etc/mkshrc文件后配置到环境变量内的。

所以如果我们要修改命令行提示符,可以通过修改/system/etc/mkshrc文件的配置,比如我希望命令行提示符显示当前所属用户(一般情况下为shell用户,adb root之后则为root用户),则可以修改/system/etc/mkshrc为如下所示:

set +o nohup
if (( USER_ID )); then PS1='$'; else PS1='#'; fi
PS4='[$EPOCHREALTIME] '; PS1='${|
local e=$?
(( e )) && REPLY+="$e|"
return $e
}$USER@$HOSTNAME:${PWD:-?} '"$PS1 "
set +o nohup if (( USER_ID )); then PS1='$'; else PS1='#'; fi PS4='[$EPOCHREALTIME] '; PS1='${| local e=$? (( e )) && REPLY+="$e|" return $e }$USER@$HOSTNAME:${PWD:-?} '"$PS1 "

修改的逻辑很简单,其实就是利用$USER环境变量来获取当前用户信息,并将其置于HOSTNAME之前。

之后我们再重新adb shell进入,看看效果:

shell@c1200:/ $
shell@c1200:/ $

如果我们先adb root,再adb shell进入,会发现命令行提示符为:

root@c1200:/ $
root@c1200:/ $

是不是不一样了?是不是很神奇!

除此之后,我们应该还关注到在设置PS1的过程中用到HOSTNAME环境变量,该变量的值一般是与ro.product.device的值保持一致的,而ro.product.device与Android编译配置时的环境变量TARGET_DEVICE保持一致,TARGET_DEVICE的值又与我们的编译配置PRODUCT_DEVICE配置一致;

所以如果我们要修改命令行提示符中的设备标识,一来我们可以修改编译配置时的PRODUCT_DEVICE名称,二来可以修改/system/etc/mkshrc,设定HOSTNAME的值即可。

Happy
Happy
50 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
50 %
Surprise
Surprise
0 %
FranzKafka95
FranzKafka95

极客,文学爱好者。如果你也喜欢我,那你大可不必害羞。

文章: 91

留下评论

您的电子邮箱地址不会被公开。 必填项已用*标注

zh_CNCN