Terminal Emulator for Android(安卓终端模拟器)的使用

时间:2022-02-17 12:45:37
网络上关于介绍Linux命令在的资料颇多,本文并不打算太多重复这些;相应地,本文通过分享一些可能并不是太常见但却很有用的应用实例,帮助读者(同时也帮助我自己)加深对类Unix的理解。

本文事实上是在介绍安卓系统下的shell以及一些有用的工具,以期“抛砖引玉”。对于那些没有兴趣或者耐心读注解的读者,以井号“#”打头的行可以跳过不看。

1 引言

1.1 本文的目标读者

对“安卓终端模拟器怎么用”这个问题感兴趣的人。虽然说我设想的读者是“有了一点点Shell基础的小白”,但是高手读了也不会感到乏味吧。


1.2 终端模拟器是个啥?

要回答终端模拟器是什么,首先要回答“终端”是什么。在那个遥远的年代,计算机的数量十分有限,而且几乎都是大型机。如果想要多人共用一台计算机怎么办?就是使用“终端”(Terminal)。Terminal在Oxford Learner's Dictionary中有这样一个解释:
(computing) a piece of equipment, usually consisting of a keyboard and a screen that joins the user to a central computer system

大概意思是Terminal是用来连接到*计算机的一套包含键盘和显示器的设备。据笔者所知,终端使用串行通信协议和*计算机进行通信。没错,就是本科数电和单片机讲的那个串行通信。显然,终端可以是一个复杂的计算机系统,但更多情况下终端都是较为简易的计算机系统(甚至单片机系统)。如果你手头有树莓派(Raspberry Pi)的话,可以把树莓派当作“*计算机”,把你的PC当作“终端”,利用树莓派的串行通信接口和USB转TTL的器件(比如PL2303或者CH340)体验一回真正的“终端”。终端和串行通信具体细节已经远远超出了本文的范围,这里不再描述。

所谓终端模拟器,模拟的并不是终端和*计算机通信这件事本身,而是终端的用户界面。简单地说就是“一个黑框框里面有个光标你可以敲一些命令完成你想要做的事情”。如果你用过DOS或者命令提示符或者Linux的“虚拟终端”,你肯定已经知道是什么了。今后本文在不引起误解的情况下,“终端”均指“终端模拟器”这类软件。

1.3 本文需要用到的软件/程序

(1) Terminal Emulator for Android(安卓终端模拟器),包名为jackpal.androidterm
(2) busybox,到各大应用市场搜busybox即可找到各种各样的busybox安装工具。有的ROM(比如我的MIUI)自带了busybox,这时就不必重新安装了。
(3) 好用的键盘,笔者使用的是Keyboard for Excel(有tab键但是没有ctrl)。你也可以使用Hacker's Keyboard这样的tab键、ctrl键都有的键盘。

1.4 在手机上使用终端模拟器的一些特点

(1) 能懒则懒——命令序列均封装成脚本或函数或别名(alias);
(2) 很多在Linux中熟悉的工具需要通过调用busybox实现;
(3) 文件名包含非ASIIC字符(比如中文字符)不总是很好地被支持,尤其是调用busybox的时候。


2 编辑脚本文件的方法

2.1 使用安卓或者PC系统上的文本编辑器

大多数文件管理器都内置了文本编辑的功能,如Total Commander、ES文件管理器等。如果你特别喜欢语法高亮,可以考虑一下Quick Edit,这是一个安卓上的支持多种程序设计语言高亮的app。

比如,你编辑了一个文件名为hello.sh的脚本文件,你写入了如下内容
#!/system/bin/sh
# 显示“Hello Android terminal”,然后退出
echo "Hello Android terminal"

Terminal Emulator for Android(安卓终端模拟器)的使用

# 这个脚本的第一行指示要用什么程序运行这个脚本,也就是所谓的释伴行(Shebang)。一定要写释伴行。虽然有某些方式允许你没有释伴行也能够成功执行脚本,但是不写释伴行是一件非常不严谨的事情。关于释伴行的更多知识可以参阅*(https://en.wikipedia.org/wiki/Shebang_(Unix))
# 这个脚本的地第二行是一个注释。Shell脚本中,井号“#”之后的到所在行末尾的内容都不会被当作语句执行。
# 脚本的第三行是一条很简单的显示文本的语句。

2.2 使用busybox中的vi

关于vi以及vim的入门学习的资料请自行搜索,这里只介绍一些基本操作。
vi是一种有模式文本编辑器,分为编辑模式和命令模式。在命令模式中,通过hjkl四个键移动光标,键入:w写入(保存)文件,键入:q退出vi,键入:q!不保存直接退出vi。在命令模式下按下i(在光标位置插入)、o(在下一行插入)、a(在当前光标之后插入)都可以进入编辑模式。在编辑模式中按ESC返回命令模式。由于终端模拟器提供特殊按键映射,因此可以先按下音量+,再按下e来等效ESC。

键入

busybox vi hello.sh

编辑上述的hello.sh,并使用:w保存。
Terminal Emulator for Android(安卓终端模拟器)的使用

3. 运行脚本文件

3.1 "正常的方法"

假设你把这个hello.sh文件保存在了sd卡的根目录中,也就是说这个文件的位置是/sdcard/hello.sh中,你可以通过如下命令把它复制到终端模拟器的主目录中。
cp /sdcard/hello.sh ~/

# NOTE: 自动补全

# 如果你的终端支持自动补全的话,你可以试着只打出cp /sdcard/he,然后按tab键。如果支持自动补全,此时终端的输入缓冲区插入的不是制表位,而应该是补全了整个文件路径/sdcard/hello.sh

接着,赋予这个文件执行权限

cd ~ # 切回主目录

chmod 700 hello.sh # 给文件的所有者读取(r)、写入(w)和执行(x)权限

# NOTE: 安卓上的chmod和Linux的chmod的区别

# 你可能注意到了在Linux上通常采用的另一种写法

chmod u+x hello.sh

# 用不了,只能用八进制文件权限表示法。那么“700”这三个数字是什么意思呢?

# 在Linux文件权限中,文件权限的作用域有三个,分别是所有者(User)、所有者所在的组(Group)和其他人(Other)。每个作用域内可以管控的权限都是三种,即上文提到的读取(r)、写入(w)和执行(x)权限。把rwx看成一个三位二进制数(r表示最高位,w表示中间那一位,x表示最低位),那么便可以用每一个二进制位取0或者取1代表对应权限是“有”,还是“无”。
# 例如,如果要求只能读取,不能写入也不能执行,那么rwx=100,换算成八进制就是4。反过来说,八进制7对应二进制111,也就是说rwx=111。亦即允许所有权限。
# 由于文件权限作用域有三个,因此用于文件权限控制的二进制数实际上应该是rwxrwxrwx这样一个9位二进制数。高三位表示所有者权限(User),中间三位表示所在组(Group)的权限,低三位表示其他人的权限(Others)。也就是说,上面的700表示的是rwxrwxrwx=111000000。
# 对于一个脚本文件的执行,至少需要读(r)和执行(x)这两个权限。如果你的脚本想要被以root权限执行(如果你已经root了的话),那么这个文件的权限应该变成705或者755。

# 需要注意的是,作为一种安全机制,安卓系统为每一个安装的app都创建一个独立的用户和组。因此你在使用终端模拟器执行某个脚本的时候,实际上对应的用户是“终端模拟器”这个用户。

# NOTE: FAT文件系统不能保存文件权限!

# 这也就是为什么要复制到你的终端模拟器的主目录下的原因。SD卡通常是格式化成FAT格式的,这种格式不能保存文件权限标志位。如果你硬要在SD卡上chmod的话,会得到如下图的错误提示。

Terminal Emulator for Android(安卓终端模拟器)的使用


执行这个脚本


./hello.sh

成功的话应该能看到类似下图的输出。

Terminal Emulator for Android(安卓终端模拟器)的使用


3.2 简单粗暴的方法

这里的方法不需要复制到主目录,可以让脚本留在SD卡上,也不需要chmod
(1) 使用sh

直接执行

cd /sdcard/

sh hello.sh

(2) 使用source和.(一个半角句号)

cd /sdcard/
source hello.sh

# 或者
# . hello.sh
# NOTE: 有什么区别和联系呢?
# 首先,如果你使用简单粗暴的方法,那么你的脚本中可以没有释伴行(有也没关系),因为你在把hello.sh作为sh的参数执行的时候,已经明示了脚本解释程序为sh。在使用source的时候,你的脚本解释程序即为当前的shell(一般来说也是sh)。
# 区别是,如果你的脚本中包含变量声明、函数、别名,2.1节 “正常的方法”和使用sh的方法在脚本执行完成后会,这些变量不会保存在当前shell空间中,但是source相当于把脚本直接插入当前shell空间(这一点类似于C/C++中的#include)。source完了之后,脚本中的变量声明、函数、别名会保留在当前的shell空间中

# NOTE: 简单粗暴的方法只能由人类使用
# 如果你的脚本是http服务器的cgi脚本,那么你必须采用3.1节的方法——因为这时候调用脚本的是httpd,而不是某个shell!

4 实例


4.1 告诉我这周是这学期的第几周?

我经常想不起来这学期的这一周是这学期的第几周。这可不是什么好事,要是临近考试周,就要抓紧时间复习了才行。

busybox vi ~/weekofterm.sh

#!/system/bin/sh

start=$(busybox date -d "2017-01-01 00:00" +%U)    # 为了演示假设有个从1月1日开始的学期
now=$(date +%U)
let count=now-start+1
length=19

# 我校一学期只有可怜的19周
if [ $count -gt 0 ] && [ $count -le 19 ] ; then
let mod=count%10
case $mod in
 1)
 if [ $count -ne 11 ] ; then
   suffix="st"
 else
   suffix="th"
 fi
 ;;
 2)
 if [ $count -ne 12 ] ; then 
   suffix="nd"
 else
   suffix="th"
 fi
 ;;
 3)
 if [ $count -ne 13 ] ; then
   suffix="rd"
 else
   suffix="th"
 fi
 ;;
 *)
 suffix="th"
 ;;
esac
echo "Calendar: This is the ${count}${suffix} week of this term."
#echo
fi
chmod 700 ~/weekofterm.sh


执行
~/weekofterm.sh
Terminal Emulator for Android(安卓终端模拟器)的使用

4.2 探索所在局域网中有哪些主机在线

我的脚本中假设子网掩码是255.255.255.0

busybox vi ~/scan_online.sh

#!/system/bin/sh

if [ $# -ne 1 ] ; then
echo "Please supply a prefix, i.e. 192.168.1"
exit 1
fi
cat /dev/null > tmp
for i in $(busybox seq 1 254) ; do
ping -c 1 $1.$i >> tmp &
done

echo "Output stored in file tmp"
echo "run"
echo "    grep -i5 '1 received' tmp"
echo "to view"

chmod 700 ~/scan_online.sh

执行这个脚本

~/scan_online.sh 192.168.1

运行

grep -i5 '1 received' tmp

查看输出

Terminal Emulator for Android(安卓终端模拟器)的使用

标记有“1 received”的IP地址是在线的。


4.3 删除相册目录中总是在不断长大的.thumbnails (以及MIUI的最近任务缩略图 [需要root])

busybox vi ~/clean_thumbnails.sh
#!/system/bin/sh

if [ -d /sdcard/DCIM/.thumbnails/ ] ; then
(
cd /sdcard/DCIM/.thumbnails/
t=$(busybox ls -1a | busybox wc -l)
if [ $t -gt 2 ] ; then
  echo "Cleaning cache..."
  rm -v .*
  rm -v *
 
  # 如果你使用了alias rm='rm -i',你可能想要执行如下的语句
  # busybox env rm -v .*
  # busybox env rm -v *
  echo    # 输出一个空行
fi
)
fi

# 下面删除的是MIUI缓存的“近期任务”缩略图,非MIUI系统及未获取root权限的
# MIUI请注释掉或删除
if [ -d /data/system/app_screenshot ] ; then
  echo "Cleaning cache..."
  su -c "rm -v /data/system/app_screenshot/*"    # 需要root权限
fi  

chmod 755 ~/clean_thumbnails.sh


4.4 下载bbcnewssummary

下载这个功能可以用curl、wget和aria2实现,笔者发现curl、wget并不是所有ROM都有。为了方便起见可以使用强大的下载工具aria2。
aria2的主页是https://aria2.github.io/,从那里你可以找到安卓版aria2的下载地址。

假设你已经把aria2的可执行程序aria2c下载下来,放到了/sdcard/aria2c这个位置。

cp /sdcard/aria2c ~/ # 复制到“终端模拟器”的主目录

cd ~

chmod 700 aria2c # 使其可执行

mkdir -p "/sdcard/Download/bbcnewssummary/"

busybox vi download_bbcnewsummary.sh

#!/system/bin/sh

aria2c -x10 \
 -d "/sdcard/Download/bbcnewssummary/"  \
 -o "$(date +%F_%H)hrs_bbcnewssummary.mp3" \
 http://wsdownload.bbc.co.uk/worldservice/css/96mp3/latest/bbcnewssummary.mp3
BBC news summary每小时更新一次,长度在5分钟以内。是非常好的练习英语听力的材料。

4.5 手机上的http服务器

这个功能实际上使用的是busybox的httpd组件。busybox httpd需要配置文件httpd.conf。配置文件可以参看下面的两个链接
https://wiki.openwrt.org/doc/howto/http.httpd
https://git.busybox.net/busybox/tree/networking/httpd.c

先做一些准备工作

cd ~

mkdir www

cd www

mkdir www-data # 用于存放网页等

busybox vi httpd.conf # httpd配置文件


busybox vi www-data/index.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>演示</title>
</head>
<body>

<h1>演示标题</h1>
<div>这是一个演示的页面</div>

</body>

</html>

然后编写用于启动httpd服务器的脚本

busybox vi ~/www/android_startup.sh

#!/system/bin/sh

busybox httpd -f -p 8080 -c ~/www/httpd.conf -h ~/www/www-data/
# 其中-f表示前台运行,方便使用Ctrl C关闭

chmod 700 ~/www/android_startup.sh

启动

~/www/android_startup.sh

停止
直接按ctrl c(也可以先按音量减再按c)

效果如图:

Terminal Emulator for Android(安卓终端模拟器)的使用

当然也可以做得更美观一点,不过网站设计与编程不在本文的讨论之列。

Terminal Emulator for Android(安卓终端模拟器)的使用



4.6 重启到recovery [需要root]

这个很简单……只有一行

su -c "reboot recovery"

4.7 禁用、启用手机上的app [需要root]

停止Google Play服务

su -c "pm disable com.google.android.gms"

启用Google Play服务

su -c "pm enable com.google.android.gms"

3.8 查看CPU温度【可能需要root】

cat /sys/devices/virtual/thermal/thermal_zone0/temp
如果permission denied,就只能以root身份执行了

su -c "/sys/devices/virtual/thermal/thermal_zone0/temp"

5 使用初始化文件

5.1 初始化文件简介

如同bash等shell一样,终端模拟器使用的默认shell也有初始化文件。这个初始化文件在shell启动时自动插入当前shell空间。
初始化文件主要用来设置PATH、别名、用户自定义函数、环境变量等。

5.2 默认的初始化文件/system/etc/mkshrc

这是我的MIUI系统的初始化文件。修改需要root权限,且系统升级后修改会丢失,因此不建议修改这个文件。

5.3 Terminal Emulator for Android的初始化命令

在终端模拟器的首选项中,找到Shell栏目的初始命令

Terminal Emulator for Android(安卓终端模拟器)的使用

这里如果填入source(或者一个点),使初始化命令插入一段脚本,即可达到目的。

5.4 使用.shrc

一个例子.shrc如下
# 设置自定义的PATH
export PATH=$PATH:/data/data/jackpal.androidterm/app_HOME/bin:/system/bin
# 增加一个LANG变量
export LANG=zh_CN.UTF-8
# 修改PS1变量,使得命令提示符不再只有一个“$”那么死板
export PS1='$?|[$USER@$HOSTNAME:$(busybox basename ${PWD:-?})] $ '
# 系统信息
function sysinfo()
{
    echo Machine type: `busybox uname -m`
    echo Hostname: `busybox uname -n`
    echo OS name: `busybox uname -s`
    echo OS release: `busybox uname -r`
    echo OS version: `busybox uname -v`
    echo Host processor type: `busybox uname -p`
}
# 挂载/system分区
function mount_system
{
    su -c "mount -o remount,rw /system"
}
# CPU温度
function cputemp
{
    su -c "cat /sys/devices/virtual/thermal/thermal_zone0/temp"
}
# 抓取当前activity
function current_activity
{
    echo "I will grasp current window focus in 3 seconds..."
    su -c "sleep 3 ; dumpsys window windows | grep -E mCurrentFocus"
}
# 设置别名
alias gping='ping -c 5 www.google.com'
alias which='busybox which'
alias grep='grep --color=auto'
alias rm='rm -i'
alias ifconfig='busybox ifconfig'
alias vi='busybox vi'
alias nc='busybox nc'
alias mkfifo='busybox mkfifo'

#清屏
clear

# 删除相册目录中的缓存
~/clean_thumbnails.sh
#显示日期和时间
echo "User logged in at `date`"
# 显示日历
busybox cal `date +%m` `date +%Y`
# 显示当前周次
~/weekofterm.sh
# 显示一个空行
echo
# 切到主目录
cd ~

6 小结

安卓系统上的Shell与Linux上的Shell有区别,但也有联系。有句话说“Where is a shell, there is a way.”,诚然本文所述远远不能表现Shell的强大,更不能完全展现安卓机上Shell的强大。本文文笔并不出众,内容也恐有疏漏,唯希望本文能成为一个启发读者的引子,希望读者读了之后能有恍然大悟,或者感到惊奇的感觉。