Skip to content

Latest commit

 

History

History
978 lines (581 loc) · 69.9 KB

趣学Linux06:命令行的使用.org

File metadata and controls

978 lines (581 loc) · 69.9 KB

第6章 命令行的使用

虽然我们Linux的图形界面已经比较先进了,绝大多数操作都完全可以用图形界面来完成,但是就像吃过麦当劳肯德基不等于吃过西餐一样,连终端都没进去过,你也好意思说你会用Linux?因此懒蜗牛同学决定开始学习Linux的命令行了。

6.1 这就是命令行

学习Linux命令行,离不开一大堆的命令。不过在这之前,首先要对命令行有些了解。下面就来说说 命令行。

6.1.1 初识终端

命令行并不神秘,打开“应用程序”|“附件”|“终端”,你看到的就是,如图6.1所示

*提示:*也可使用快捷键Ctrl+Alt+t来打开终端。

这种图形界面下的命令行窗口,叫做伪终端。在这个窗口里,你可以近距离地跟我交流。我们操作系统是很希望用户能够和我们使用命令来交流的,像朋友般倾诉,感受彼此的心声,而且还低碳环保,节省能源(省CPU和内存啊)。

*提示:*如果只是想要通过命令运行一下某个软件,例如gconf-edit,可以按下Alt+F2组合键并输入命令来执行。

【两种老板】

用户对于我们操作系统来说就相当于老板。使用图形界面的用户和使用字符界面的用户是两种完全不同的老板。

前者高高在上,拒人千里之外,就会比划,有事都不直说。比如他指着电灯开关的按钮冲你“嗯”一下,那就是让你过去把电灯打开;他用手一指桌面上的文件,冲你“嗯”一声,那是想让你选中这个文件;他要是指着文件冲你“嗯,嗯”两声,你就得明白他是让你打开文件(这里你还得注意,他“嗯”的那两声之间的间隔长短,要把握好分寸。要是间隔很短,那叫“双嗯”。要是间隔长,那叫“嗯”两下,对应的操作是不一样的)。他要是用手冲着桌面上的一堆东西指一圈,然后嘴里拉长声的“嗯~”一下,那就是让你把这些东西全都选中。给这种老板打工,需要很大忍耐力啊。

要是遇到第2种老板就要好得多,这种老板,平易近人,没有架子,有事会跟你平等地交流:“小笨啊,你列个表,看看我这目录下都有什么东西啊。”“小兔啊,3分钟后下班啦,到时候记得关机啊。”“小笨啊,咱挺寂寞的,放个片子看看吧。就放那个wall-E.avi吧。”遇到这样的老板,工作起来,才是心旷神怡。

用命令行的用户,就相当于这第2种老板,跟他们交流,舒服。

6.1.2 Shell的基本概念

那么Shell又是个什么东西呢?

【Shell的作用】

Shell是啥?是海鲜馆的扇贝?是汽车用的润滑油?都不是,他是一个外壳。什么叫外壳呢?咱们慢慢说。

我们Linux是个内核,这个内核是可以做很多事情的,整个电脑的硬件都归我管。显卡、声卡、内存、硬盘都归我控制;硬盘上的各种程序也归我调度。那么,我应该用这些硬件软件去干点什么事情呢?我不知道,因为没有人给我下命令。下命令的就是人类用户,比如我的懒蜗牛同学。

可是人类用户要做什么操作,靠他拿嘴说,我肯定是听不懂的。因此就需要一个能够把人类用户的操作意图转述给我的软件,这个软件就是Shell。他就像罩在我这个操作系统和人类用户之间的一个外壳一样,在我和人类之间相互转达信息。

*提示:*Shell广义上可以指操作系统和用户接口的界面,图形界面也是一种Shell。因为图形界面的本质也是实现“把人类用户的操作意图转述给内核”。

【Shell的种类】

Shell有很多种,有bash、csh、ksh等,各有特点。

 bash------这是最常见的Shell了,全名为Bourne Again Shell。基本上多数发行版都用他作为默认的Shell程序。他的各项功能都比较完善,是个全能型选手。

【Shell的实质】

有的同学说了:说了这么半天,这个Shell到底是个啥软件啊,我怎么感觉不到他的存在呢?我从来没有运行过他呀。他跟终端有啥关系呢?

Shell,其实就是一个二进制的程序,跟狐狸、gedit他们一样。只不过,Shell的任务不是上网,也不是编辑文件,而是和用户交流。

比如我们Ubuntu系统中,默认的Shell是bash,也就是/bin/bash这个二进制文件。你没有亲自调用过他,是因为每次用户打开终端的时候,终端程序会自动调用用户的Shell。

那么终端怎么知道用户的Shell程序是什么呢?这很简单,在/etc/passwd文件里有记载。比如我们的lanwoniu用户打开了虚拟终端,G终端就找到passwd文件里对应当前用户的一行,类似下面这样:

cat /etc/passwd | grep gaowei | sed "s/$USER/me/g"

这一行的最后一段就说明了这个用户的默认Shell是/bin/bash。于是G终端就去叫醒bash,bash起床,通过G终端来跟懒蜗牛用文字交流。那么bash最先说的一句话大概就是“你好”,当然不会这么不专业,这句话用bash的专业语言说出来就是下面这样:

这一行是什么意思呢?

 “@”之前的,是当前用户的用户名。

 “@”后面,“:”前面是计算机名,这两个都好理解。

 “:”后面、“$”前面是当前所在目录,就是当前输入命令的人所在的位置。“~”代表用户的家目录,也就是“/home/<用户名>”这个位置。

 “$”则是命令提示符,在“$”后面就可以输入命令了。

*提示:*普通用户的提示符是$,如果用root登录终端,则提示符是#。但Ubuntu系统默认禁用root用户,所以一般看不到#提示符。

6.1.3 bash的工作(简单的Shell命令介绍)

正说着,懒蜗牛已经开始敲命令玩了。什么ls、free、top、fdisk等常用命令,挨个试验。于是工作间里也开始忙碌了起来。你可能以为bash会在懒蜗牛的指挥下跑来跑去,执行各种操作。其实完全不是那么回事,bash只是作为一个命令的传达者而已,真正干活的是那些命令们,也就是ls、free这些家伙。

【bash和图形界面的工作性质相同】

这些所谓的命令,其实都是一个个的小程序,或者说一个个的小软件而已。就跟狐狸妹妹、OO老先生一样,只不过比他们小巧很多。如果你愿意,也可以把Firefox视为一个上网用的图形化界面的命令,为了方便描述,咱们以后管这些家伙叫做命令程序吧。

当用户输入命令比如ls的时候。ls这两个字符就被传给了bash。bash怎么处理呢?首先bash要看输入的字符是不是自己的什么关键字,比如for,history之类的,如果是,就归bash来处理了;如果不是,就说明懒蜗牛是要找个命令程序,bash就要负责去找到懒蜗牛想要的这个程序,并且叫他起床干活。

这个工作过程其实跟Gnome的工作是很相似的,只不过Gnome是根据鼠标的点击位置来判断用户想要运行哪个软件,而bash是根据用户输入的字符来判断的。

【bash查找命令的艰辛历程】

那么bash去哪里找哪些命令程序呢?不知道您有没有听说过有个叫做环境变量的东西,跟Windows系统里的那个环境变量差不多,其中有个环境变量叫做PATH,里面记录着bash去找程序的路径。如果你想看看PATH到底是什么,运行echo $PATH就可以了。会得到类似这样的输出:

echo $PATH | sed "s/$USER/me/g"

*提示:*$符号在命令中表示引用变量。可以export设置变量(这里引用和设置的都是全局变量,后文会讲到)。例如:

第1行设置test变量的值为字符串“my\_test\_word”,第2行使用echo命令来查看test变量,第3行是运行结果。

当懒蜗牛运行一个命令,比如ls时,bash就对照着PATH里面的设置,开始找了。

他先去/usr/local/sbin房间里面找(根据PATH的设置顺序)。敲开门,客气地问:“请问ls是住这屋么?”等了半天,除了墙角的蜘蛛网上那8条腿的小家伙寂寞地弹了几下琴弦之外,再也没有活物给他任何回应,于是bash意识到这屋没人,赶快去下一屋。

又到了/usr/local/bin,他依然是很礼貌地敲开门问候,里面只有一位懒蜗牛前几天安装的叫做Maya的软件,只听那位叽里咕噜地说了几句2012啥的玛雅语,bash也听不懂,不过反正他不是ls就对了,赶紧去下一间。

*提示:*/usr/local目录与/usr目录中的结构类似,都包含bin、sbin、lib、incud等目录。对于Linux系统,并没有明确地对这两个目录内容进行定义。不过一般来说,对于Ubuntu系统,/usr目录中用于存放从软件源中安装的软件。/usr/local中用于存放用户用其他方式安装的软件。

bash推开/usr/sbin这屋的门一看,这回里面很热闹,而且都是重要人物。有管用户创建的useradd、每天启动必备的gdm、负责跟通过网络跟Windows XP共享文件的smbd和nmbd等。一听bash来找ls,useradd没好气地说:“哎呀,ls怎么可能在我们这里呢?我们这里都是管理级的程序,都是领导!那个ls是谁都能运行的,他怎么会在sbin里,你得去bin里面找啊。”

bash只好客气地退了出去,继续去找/usr/bin、/sbin/、/bin,终于在/bin里面找到了ls,于是赶紧叫醒ls,让他去干活。至此,bash的任务也就结束了,他就回去等待懒蜗牛的下一个命令了。

*提示:*一般/bin、/usr/bin、/usr/local/bin目录下存放的是普通用户使用的命令。/sbin、/usr/sbin、/usr/local/sbin目录下存放的是需要root权限才能使用的命令。

上面说的是直接敲一个命令的情况。直接输入一个命令,bash就会去PATH变量记载的路目中查找。如果你想运行一个二进制程序(比如刚刚从网上下的某个软件的安装程序),但这个程序不在这些/bin、/sbin之类的目录里怎么办呢?也好办,只要你运行的时候加上路径就可以了。如果没有写路径,那么bash就去PATH中的目录里找,如果写了路径,他就直接去指定的路径找了。

*提示:*Linux中,一般当前目录------即“./”,并不在PATH变量中,所以要运行当前目录下的二进制文件或脚本文件,也需要加上路径。例如要运行当前目录下的setup.sh脚本,需要输入:./setup.sh。

【有困难找纯爷们儿】

懒蜗牛对照着书本练习各种命令。学到ifconfig命令的时候,觉得书上写得不是很明白,怎么办呢?bash告诉他:“别着急,我给你找个人问问,这个人,纯爷们儿!”然后bash扭头冲着硬盘里喊:“嘿man!你出来,说说这是怎么个意思。”

随着咔咔嚓嚓一阵硬盘响,只见内存中走来一人。见此人人高马大,膀大腰圆,扇子面的身材,胳膊跟大腿一样粗,这就是bash说的那个“man”。这纯爷们儿说出话来如同打个炸雷一样:“嘿!你好啊。洒家我是专职命令解说员,你有什么想知道的吗?”懒蜗牛输入了命令:

man ifconfig

意思就是问man,这个ifconfig怎么用啊?man仰天大笑一声:“嚯哈哈哈,要说这个ifconfig嘛......不难,听我慢慢地道来!”当然,他不是用中文说的,而是用英语介绍了一下ifconfig命令的使用方法, *提示:*进入man的界面后按q键可退出man。

6.2 这么用Shell

懒蜗牛同学那敲敲打打的命令行生活就这样开始了。从那以后,工作间里少见了红酒大师;看不到盒子妹妹;心有灵犀也不常来上班了;OO老先生也难得起床了,工作间里净是些命令行的小程序在跑来跑去。

6.2.1 理解目录结构

这一天,懒蜗牛觉得玩伪终端不够过瘾,于是按下Ctrl+Alt+F1组合键进入黑漆漆的终端界面来敲名令。看着一屏屏的字符,懒蜗牛感觉很有成就感。

*提示:*进入终端后,可以按Ctrl+Alt+F7组合键回到图形界面。

【当前目录,家目录】

懒蜗牛进入终端后,先习惯性地运行了一下ls,看到了当前目录下的所有文件。

所谓当前目录,就是用户现在所在的目录。比如你在你家卧室发呆,那么你的当前目录就是卧室;过一会儿你又去客厅发呆了,那么当前目录就是客厅;然后你去厕所发呆,当前目录就是厕所(怎么到哪都发呆)。那么懒蜗牛现在的当前目录是哪个目录呢?就是他的家目录,就是图形界面的“位置”下的“主文件夹”那个目录,也就是/home/lanwoniu这个目录。当懒蜗牛每次打开终端的时候,无论是虚拟终端还是按Ctrl+Alt+F1组合键进入的终端,刚一进去,都是在懒蜗牛的这个家目录里。

*提示:*命令行下可以用“~”符号代表当前用户的家目录。

【出去走走】

不过,毕竟不是什么事情都要在家目录里做的,如果你在这个目录里看够了,想出去走走,到其他的目录逛逛,这时候就需要cd命令了。cd命令跟光盘没有关系,他是Change Directory(改变目录)的缩写。这个命令可以改变当前的目录,他就像出租车一样,可以让你到达你想去的任何一个目录(当然,前提是你得有权限进入那个目录,就像你不能让出租车开进中南海一样)。

cd命令的用法就是:

【绝对路径和相对路径】

那么,路径该怎么写?如何描述你想去的目录呢?一般有两种方法:绝对路径和相对路径。

绝对路径就是无论去哪都统一从一个根本的位置上描述。比如你打车,司机师傅问你去哪,你说:“我去地球,亚洲,中国,北京市,崇文区,羊肉大街,排骨胡同,376号。”这么说就是绝对路径。无论你在什么地方打车,都从一个根本的位置上说起(比如地球,当然,你愿意从太阳系说也行)。层层递进,最终说到最小、最详细的那个地方为止,就肯定错不了。我们这里目录的地址当然不能从地球开始说了,我们的根目录“/”就是那个最初的、根本的位置,无论你去哪个目录,都可以从这里说起,比如:

cd /usr/share/fonts/X11

这条命令的意思就是说,要去根目录下的,usr目录下的,share目录下的,fonts目录下的,X11目录。

绝对路径很准确并且最直接,不过有时候也比较费劲,所以,cd还支持相对路径。相对路径,就相当于你打车,司机师傅问你去哪,你说:“就前面那路口,左转,过三个红绿灯走700米有一家精神病院,到时候我指给您,您就靠边停车就行了。”这种描述方法就是以当前所在的地点为起始地点进行描述,而不用从外太空开始说。那么具体到我们这个系统里怎么说呢?还拿刚才那个命令作为例子。假如现在已经在/usr/share目录下了,那么就运行:

cd /fonts/share

这样就进入X11目录了。这句命令的意思就是,要去当前目录下的,fonts目录下的,X11目录。

【引导员】

懒蜗牛同学学会了cd命令,兴奋地在终端里cd来,cd去的,像个跑进游乐园的小孩子。转着转着,忽然觉得有点迷路了。静下来想一想:我现在是在哪个目录呢?是在/usr/bin,还是/bin,还是/usr/local/bin呢?

其实,他看一眼那个提示符$前面的内容就可以了,默认设置下这里显示的就是用户所在的目录的绝对路径。不过这个提示符的格式是可以修改的,如果修改了,这里显示的不是当前的路径了,怎么办呢?

这一点,我们早为您想到了。命令行中配有专业的引导员,告诉您您现在所在的位置,这位引导员就是pwd。可别一看名字就以为他是负责修改密码的,其实他跟password一点关系都没有,他是Print Working Directory的缩写。用法简单,输入pwd就行了,他就会告诉你现在所在的目录。

6.2.2 重要的TAB–命令补全功能

学习完ifconfig命令,懒蜗牛同学又开始研究fdisk,这是个磁盘分区命令。学习了这个命令之后,懒蜗牛又学习了查看网络端口的netstat命令。渐渐地,懒蜗牛发现了一个问题:随着命令字符越来越多,敲起来越来越费劲了。

一开始ls、cd、top这样的命令很简短,输入也没觉得麻烦。可遇到字母多的命令,一遍一遍地敲就慢多了,有没有什么省事的办法呢?

【便捷高效的键盘】

很多人不喜欢键盘,不喜欢打字。其实想想,早在电脑刚刚被发明出来的时候,键盘就已经是每一台电脑所必备的输入设备。作为从那个字符界面的时代走过来的Linux系统,我们自然充分考虑通过键盘操作整个系统的便捷和效率问题。直到现在,使用键盘操作Linux都会拥有意想不到的高效率和成就感。

我以前很不明白,键盘可以发送上百个命令,用起来应该很方便才对,为什么人类就那么喜欢那个只能发送:上、下、左、右、左键、右键、滚轮这么几个命令的鼠标呢?(当然,有的鼠标还有一些额外的功能键,但是那也比键盘少啊)。后来见多识广的OO老先生给我解释,我才明白。原来是因为人类记忆力不行,没有我们软件这么可靠,记不住那么多个键,于是只好用那只能发送几个命令的鼠标了。

好了,绕得有点远,其实说起来在我们的命令行里通过键盘敲命令是很方便的,只是很多人不大熟悉如何节省时间而已,都以为用键盘和我交流跟用键盘和那个DOS系统交流一样麻烦呢。其实我已经很人性化了,就因为键盘上有个键------Tab。

【重要的Tab键】

看一个人的键盘,就可以猜测出他平时用电脑干什么。如果W, A, S, D, U, I, J, K这些键严重磨损,说明这哥们儿玩拳皇的;如果A, Shift, Ctrl, 1, 2, 3, 4, …, 9, 0键严重磨损,说明是个玩即时战略的,星际魔兽之类;如果Alt, S或Ctrl,Enter键磨损,大概是天天聊QQ;如果Tab键严重磨损,那估计就是个Linux高手了。因为在Linux的命令行下,Tab键起着命令补全的重要作用。

比如说,要运行ifconfig命令,可以不用完全输入这8个字母,只要输入ifc,然后按Tab键,bash就知道你要干什么了。因为所有可以运行的命令里面以ifc开头的就只有ifconfig,所以当你按下Tab键的时候,它就会替你写出完整的命令:ifconfig,

这是因为在你按下Tab键的时候,bash会去PATH变量所设置的所有目录里遍历一遍,检查了里面所有的有执行权限的文件,查到了ifconfig文件(命令其实就是个可执行文件嘛)。之所以这么快,是因为他早就把这些重要的东西缓冲进内存了,所以下次别抱怨我们Linux动不动就把你内存占满了哦。

*提示:*Linux系统的内存管理机制是尽量多地使用内存。将空闲的内存用来做缓存。因此bash遍历PATH中的路径时并不是去硬盘读取,而是直接在内存中处理。

那么如果你再少写个字母呢?比如你只写了if,然后就按Tab键,Bash遍历了一遍PATH中的路径后发现,有4个命令是以if开头的,所以他不知道你要的是哪个命令,于是就不做任何动作。这时候如果你再按一下Tab,他就会提示你:以if开头的命令有if、ifconfig、ifup、ifdown、ifquery。然后你自己看需要的是哪个,照着输入就行了,很交互吧?如果没明白,可以看图6.7所示的效果。

这样除了减少按键次数以外,还有一个好处就是你可以不必完全记住整个命令,能够记住前几个字母就可以通过Tab把整个命令回忆出来。

6.2.3 翻旧账------命令的history

有了Tab,用户输入新命令的时候省事了不少,还有一个history功能,可以让用户重复以前输入过的命令的时候省心。

在终端里,如果你想输入上一次输入的命令,按一下向上箭头,就看到了;如果想要再上一次的命令,就再按一下;如果想要再再上一次的命令,就再再按一下;如果想要再再再再再上一次的命令......不怕键盘坏掉你就按吧。

好了,总该有些更靠谱的方法吧。如果想查看很久远以前的命令怎么办?请输入history。这是一个命令,可以显示之前运行过的n条命令,默认情况下n=1000。现在图形界面越来越发达,输入命令的机会越来越少,估计1000条记录都能把去年的命令显示出来了。

*提示:*也可以指定显示最近的n条命令。例如运行:

history 20

显示最近的20条命令。

说起来history命令也没啥神奇的,他之所以能够显示曾经运行过的命令,不是因为他有啥水晶球,而是负责接收用户命令的bash会把每一条命令记录下来,写在~/.bash\_history文件中。用户输入history的时候,bash再把这个文件打开,显示出里面的内容。

*提示:*history并不像ls那样是独立的命令,而是bash的关键字。即history是否可用与所用的Shell有关,本文所述仅针对bash。

懒蜗牛输入了fdi这3个字母,然后按了一下Tab键,bash赶紧给他把命令补全,fdi变成了fdisk,懒蜗牛同学感觉省事了不少(其实就少输入了俩字母)。听说这个fdisk是用来给硬盘分区的命令,可不能瞎玩,还是先问问纯爷们儿吧。于是,一阵嚯哈哈哈的笑声又从硬盘里传来了。

*提示:*fdisk需要操作硬盘设备,因此需要加上sudo提升权限才能运行。

6.2.4 more or less------命令的分页显示

6.2.5 wildcards

6.3 Shell编程

懒蜗牛同学经过一段时间的学习,已经对我们Ubuntu系统里面的基本命令了如指掌了。接下来他又想要干点什么更有意思的事情呢?

6.3.1 把命令打包执行

【日复一日】

这一日,懒蜗牛把一张SD卡插进了电脑,然后运行mount命令来挂载:

sudo mount /dev/sdb1 /mnt/

挂载之后,把里面的东西复制了进来:

cp /mnt/*JPG /home/me/Pictures/

之后把这些图片打成压缩包,放到一个目录里作为备份:

tar -czvf /home/me/Backup/Pic.tar.gz /mnt/*.JPG

备份完了,把卡里的文件删掉吧:

rm /mnt/*.JPG

最后,卸载这个SD卡:

sudo umount /mnt/

又一日,懒蜗牛又把这张SD卡插进来了,里面有了新的文件。于是,他像前一次一样操作,又一次挂载:

然后第3天......懒蜗牛终于忍不住了。每次都要输入这么多命令很麻烦啊,能不能省事一点呢?

【高级批处理】

懒蜗牛同学回忆起了在很久很久以前,用过一个叫做DOS的操作系统,那里面有一种叫做“批处理”的东西,可以把很多条命令写进一个.bat文件里,一起执行,似乎很强大。那么我们Linux系统里有没有这种批处理呢?在我们Linux系统面前说批处理,那简直就是关公面前耍大刀,华佗门口卖止疼膏了。

您或许听说过我们Linux系统中有叫做Shell脚本的东西。这是我们Linux的骄傲,如果批处理文件是辆自行车的话,Shell脚本就是波音747!这么强大的东西,解决懒蜗牛同学现在遇到的问题,绰绰有余。

要写个Shell脚本很简单,随便用一个什么文本编辑器,写上你要执行的命令,然后保存,就可以了。比如要解决懒蜗牛同学每次敲很多相同命令的烦恼,那么可以写这么一个文本文件:

所有的命令写在此处.

然后保存,随便起个名字就可以,比如叫“daily_backup.sh”。到此为止这个脚本还不能运行,还得赋予这个文件“可执行”权限。就这么操作:

chmod +x ./daily_backup.sh

这就可以了。这回这个脚本就可以运行了,就这样:

./daily_backup.sh

不过因为里面涉及挂载操作,所以脚本里面调用了sudo。因此运行的时候会提示输入密码。这样,用这么一个脚本就可以省去懒蜗牛同学每次敲命令的烦恼了。

*提示:*脚本文件并不要求特定的扩展名,只要是文本文件,具有可执行权限即可。但一般习惯上将脚本文件的扩展名命名为.sh。

【灵活的Shell脚本】

不过有的同学可能发现了,这样简单地把静态的命令写成脚本,并不能完全解放懒蜗牛同学。懒蜗牛每天打包备份的文件都是不一样的,但是这个脚本里,备份文件的文件名是固定的呀,这样懒蜗牛同学每次运行完这个脚本还得去改一下文件名。就不能送佛送到西,帮忙帮到底么?

当然能,咱们来把脚本修改一下:

sudo mount /dev/sdb1 /mnt/
cp /mnt/*.JPG /home/me/Pictures/
tar -czvf ~/Backup/Pic"$(date +'%Y%m%d')".tar.gz /mnt/*.JPG
rm /mnt/*.JPG
date +'%Z'

或者也可在date命令后添加参数,以设定输出格式。可支持的格式参数如表6.1所示。

6.3.2 规范的Shell脚本

虽然懒蜗牛同学用一个类似批处理的脚本文件解决了他每次都要手动敲很多命令的烦恼,但是他写的这个所谓的脚本还是太初级了,一点都不专业。

【要有必要的注释】

【指明使用哪个Shell】

#! /bin/bash

这又是什么意思呢?有的同学说:我知道,这个以“#”开头,所以只是个注释。但是,很不幸,这不是注释。

确实,我说过,以“#”开头的是注释。但是,“#!”放在一起还出现在脚本第一行,那就不是注释啦!这一行的意思是用来指明这个脚本所需要的Shell。

前面说过,我们Linux系统中有很多的Shell,比如bash、tcsh、ksh等。这些不同的Shell,他们的特性、语法什么的,大都是不同的。那么一个脚本程序就有必要说明一下,这是个bash的脚本,还是tcsh或者别的什么Shell的脚本。就好像你写了一段代码,总要告诉人家你写的是C语言的代码还是Java语言的代码吧。

当然,你可以直接告诉系统,说这个脚本是一个bash的脚本,请用bash来解释这个脚本。那么你就需要这样运行你写的脚本(比如脚本叫做myscript.sh):

【使用函数】

另外,Shell脚本也像大多数编程语言一样支持函数。如果你有一段代码需要在脚本里执行多次,不必反复地写多份,而是把它们写成一个函数,直接调用即可。比如这个自动备份的脚本:

#! /bin/bash
backup_pictures ()  {
sudo mount /dev/sdb1 /mnt/
cp /mnt/*.JPG /home/me/Pictures/
tar -czvf ~/Backup/Pic"$(date +'%Y%m%d')".tar.gz /mnt/*.JPG
rm /mnt/*.JPG
}

把备份的动作写成一个backup_picture()函数后,需要进行备份的时候,在脚本里调用这个函数就可以了。脚本在执行的时候,会先略过写进函数里的部分,直到执行到某一行调用了这个函数,再回来执行。

6.3.3 在Shell中使用变量

作为一种编程语言,少不了变量。我们Linux系统中强大的Shell自然也要支持变量。

【用户变量------信手拈来】

【变量类型------只有字符串】

有的同学可能注意到了,我们的脚本里并没有给变量明确声明一个类型。这是因为Shell的变量只有一种类型,就是字符串。没有什么整型、浮点型之类的概念。咱们再用一个简单的脚本说明一下:

num=8
num="$num + 2"
echo $num

运行这个脚本,会看到最终打印出来的num的值是“8+1”,而不是“9”。因为bash这家伙压根就没长数学的脑子!他只会把变量的值作为字符串处理。

可是虽然bash没长数学脑子,但是我们的生活不能没有数学啊,遇到需要计算的问题时怎么办呢?没关系,bash不会算,有人会算,就是expr命令。

expr专门用于Shell脚本中,负责对几个字符串变量进行数学计算。比如刚才这个脚本,我们实在是想计算num+1,看看到底得多少。那么就可以这样:

num=8
num=$(expr $num + 1)
echo $num

这样就可以如愿看到数字9了。

【环境变量------哪都能用】

刚才我们随手定义的变量,可以叫做用户变量。自己定义自己用就好了。除了用户变量之外,还有一个重要的概念,就是环境变量。比如我们之前遇到过的PATH变量、HOME变量等。

所谓环境变量,有点类似于C语言里面的全局变量,它在整个系统中都有效。用户变量只在这一个脚本内有效,出了这个脚本,这个变量就没了。而全局变量一旦设定,可以在整个系统中的任何时候、任何地方进行访问。要让一个变量成为全局变量很简单,只要在变量赋值语句前加上export,类似这样:

export env_num=8
ech $env_num

运行这个脚本,你自然会看到输出一个“8”,当然这并不是环境变量的特点。环境变量的特点是你运行完这个脚本以后,再输入命令:

依然会看到这个变量的值还是“8”。

*提示:*环境变量在当前会话结束后失效。

当然,像这样创建出一个环境变量的需求并不多,一般我们在写Shell脚本的时候,多数是使用或者修改已经存在的环境变量。比如通过$TZ变量获取本系统所在的时区;通过$HOME变量获取当前用户的家目录地址等。比如懒蜗牛同学的自动备份脚本,现在每次都是固定往/home/lanwoniu/backup目录下备份,很不灵活。这里就可以应用全局变量,改成这样:

#! /bin/bash
backup_pictures ()  {
sudo mount /dev/sdb1 /mnt/
cp /mnt/*.JPG $HOME/me/Pictures/
tar -czvf ~/Backup/Pic"$(date +'%Y%m%d')".tar.gz /mnt/*.JPG
rm /mnt/*.JPG
}

如此一来,就不光是lanwoniu用户可以用这个脚本了,任何用户都可以用这个脚本进行备份,提高了灵活性。

【特殊变量------一堆符号】

除了普通的用户变量和环境变量外,还有一些特殊的变量。这些特殊变量特殊在如下几方面。

长得就特殊。  脚本执行时自动被设定。  不可修改。

下面我们就看看这些变量的样子,主要就是下面这些。

 $n------这里,n是一个从0到9的数字。这个变量代表了执行本脚本所加的第n个参数。n=0时代表脚本本身的名称。这个变量跟C语言中的argv[]有点类似。

 $*------这个变量代表执行本脚本所加的所有参数(不包括脚本名本身)。

 $#------执行本脚本所加的参数个数,类似C语言中的argc。

 $$------这个脚本的PID。

echo $n
echo $*
echo $#
echo $$
pstree -asp $$

【变量赋值】

变量赋值,除了可以直接写出初值之外,还可以将命令的运行结果赋给变量。比如咱们之前用到过的“`”符号,可以调用指令并获得该指令的输出。那么同样也可以把这个输出赋值给一个变量。还拿懒蜗牛同学的备份脚本做例子,可以再这样修改一下:

这回我们把当前的日期,存储进了today这个变量,这样如果要多次用到日期,就不必每次都调用date命令了,直接从变量中读取就可以了。

*** 6.3.4 Shell中的条件判断

所有编程语言,都少不了条件判断语句。我们的Shell也是可以支持简单的条件判断的。

【if和fi】

最常见简单的条件判断,就是if语句了。Shell中的if语句比较有个性,if后面的“表达式”部分必须被足够的空格分隔得分崩离析才可以。比如咱们看下面这个脚本(建议把这个脚本命名为kill):

if [ "$1" = "me" ]; then
    echo "Hi"
else
    echo "Hello $1"
fi

*提示:*要注意这个脚本中“[”符号前边和后边、“”$1””后边、“=”后边、“]”前边,都要有空格。否则脚本错误。

这个脚本的运行效果就是这样:

好,我们暂时不去讨论这个脚本中的虐畜及自虐倾向,只讨论里面的if语句。if的工作,就是根据后面命令的返回值,来判断程序应该走哪条分支。另外,if语句一定要有对应的fi作为结尾(相当于endif)。

这里大家可能有点困惑:if后面的命令?if后面不是一个表达式么?哪来的命令呢?

好,那我们写个更简单点的脚本说明一下。

if ls -l /; then
    echo "ls return true!"
else
    echo "ls return false"
fi

运行这个脚本,可以看到Shell会调用“ls –l /home”这条命令,列出/home目录下的文件,然后因为if判断到“ls –l /home”这条命令返回了结果“真”(因为命令执行成功嘛),因此显示出了“ls return true!”这个字符串。如果/home这个目录不存在,ls命令就会执行失败,返回“假”,于是if根据这个返回值判断,程序运行else对应的语句,就会打印出“ls return false!”这个字符串。

这下大家是不是明白些了?if与“;”号之间的,就是一条Shell中的命令而已。这个命令返回真,则进入if对应的分支;返回假,则进入else对应的分支(如果有else的话)。那么刚才那个kill脚本里的“[“$1” = “me”]”这段难道也是命令么?答对了!这就是命令。 “ls -l home”中,“ls”是命令名,后面是两个参数:“-l”和“/home”。 “[ “$1” = “me” ]”中,“[”是命令名!后面有4个参数,分别是“”$1””,“=”,“”me””,以及“]”。 没错,“[”是一个无比简练的意想不到的命令。这条命令就在/usr/bin/下,用ls可以看到。它跟apt-get、gcc等命令一样,是一条实实在在的Shell命令。这条命令的作用,就是判断后面参数所组成的表达式的值(真或假),并返回。“[”命令要求输入的最后一个参数必须是“]”(这主要是为了你们人类看着顺眼点)。

这样就可以理解一个问题了:既然“[”后面的所谓的变量、常量、“]”号等,对于“[”命令来说都是它的参数,那么这些参数之间必然都要有空格。所以Shell脚本中的if语句必须写成类似这个样子:

if [ $a = $b ]; then
if [ ! $a = $b ]; then

而不能写成下边这样:

if [$a=$b]; then
if [ !$a = $b]; then

“[”命令不仅可以判断相等条件,还可以判断很多复杂的条件,如表6.2所示。

表6.2 常用判断参数

./Images/image00526.jpeg

*提示:*“-eq”与“=”的意义不同。“-eq”用于判断数值上的相等,“=”用于判断字符串的完全匹配。例如:$a的值为“03”,$b的值为“3”,则“-eq”将判断这2者相等,而“=”将判断这2者不等。“!=”和“-ne”同理。

有了if命令,我们就可以把懒蜗牛同学的自动备份脚本再进一步完善一下:

./Images/image00527.jpeg

【case】

类似于C语言的switch,case语句,Shell中也有case语句来实现多分支的判断。看看下边这个小脚本:

./Images/image00528.jpeg

这个脚本的运行结果估计您也能看明白,就是个英语没学好的卖萌脚本。通过这个脚本可以比较好地理解case的作用:就是用某个变量的值,去匹配下边的几个“)”符号前的字符串。如果某行匹配,则执行该行的语句,直到发现双分号“;;”时停止。如果没有找到匹配的,就执行“*)”一行的内容,遇到双分号时停止。

提示:*“)”一行也可以不存在,则匹配不到任何字符串时就不执行任何命令。

6.3.5 Shell中的循环语句

【for循环】

Shell脚本同样支持for循环。不过跟多数语言的for循环的写法不太一样的是,Shell脚本中的for循环有种很有个性的格式:

for v in v_list
do commands
done

其中,“变量”就是一个变量,这个变量一般会在do和done之间的命令列表中用到。而这个“名字列表”则是一个由空格分隔的字符串列表。Shell在执行for循环时,每次依次从“名字列表”中取出一个字符串赋给“变量”作为变量的值,并执行“命令列表”中的命令。另外,在写for语句时,也可以省略in及名字列表部分,这表示用当前的位置参数来代替这时的名字列表。

这样说很枯燥,写个小程序吧:

for num in 1 2 3 4 5 6 six
    do echo "num = $num"
done

运行这个脚本,会看到这样的输出:

这样就很明白了吧。这种方式用于批量处理文件会很方便。当然,如果你需要C语言中那种for循环,也是可以的。不过写法稍稍有点不一样,要用两个小括号。类似这样:

./Images/image00532.jpeg

这里就不做过多解释了。像其他语言中一样,num的值从1开始,依次累加,直到不满足“num<7”这个条件。

【while循环】

while循环也是经常用到的一种结构,它的用法大约如下:

./Images/image00533.jpeg

其中,“循环条件”的写法也是和if语句一样的。多数情况下使用“[”命令来计算条件并返回结果,这里不再赘述。

无论是while循环还是for循环,都可以使用break和continue指令。其中break指令用于跳出当前循环体,执行后面的操作;continue指令用于忽略本次循环,直接回到循环体的开始位置,执行下一次循环。这和其他常用编程语言中的break和continue是一样的。

6.3.6 扩展阅读:Linux的文件权限

这一回中,我们提到了写一个脚本,要赋予它可执行权限才能执行。有的同学可能对这个权限还不是很明白,那咱们就仔细说说Linux下的文件权限。

【简单的权限------只有3种】

熟悉Windows系统的同学应该都知道,Windows下可以对文件设置很详细的权限。谁可以读这个文件,谁可以写这个文件,等等,如图6.8所示。

./Images/image00534.jpeg

图6.8 Windows设置文件权限

我们Linux系统中的权限相对简单很多,对于一个文件(包括文件夹),只有3种权限------读、写、执行。

对于一个普通的文件,拥有对这个文件的读权限,就是可以读取里面的内容。对于一个目录(目录也是特殊的文件,在我们Linux世界,一切皆是文件),拥有对这个目录的读权限,就意味着可以查看目录中的文件列表(也就是可以用ls命令看里面都有什么东西)。

对于一个普通的文件,拥有对这个文件的写权限,就是可以改变里面的内容,增加、修改、删除,这些都算改变。对于一个目录,拥有对这个目录的写权限,就意味着可以删除、移动或者添加目录里的文件或者目录。也就是说,对于目录文件,里面的文件列表就相当于这个目录文件的“内容”,有写权限就是可以修改内容。

*提示:*要注意的是,对于一个普通文件,有写权限并不代表可以删除这个文件。要对这个文件所在的目录有写权限才可以删除这个文件。

对于一个普通的文件,拥有对这个文件的执行权限,就是可以运行这个文件,比如我们写的脚本。对于一个目录,拥有对这个目录的执行权限,就意味着可以进入这个目录(比如用cd命令)。如果一个目录有执行权限,但是没有读权限,就意味着你可以通过cd命令进入这个目录,但是进去之后运行ls发现什么也看不见。

*提示:*在赋予一个文件可执行权限时,并不会对这个文件的格式进行检查。也就是说,你完全可以赋予一个JPEG格式的图片文件可执行权限而不会收到任何错误提示。当然,当你真的试图运行这个图片文件的时候,肯定会报错的。

【面对的用户------只有3类】

现在,这3种权限我们明白了。但是权限离不开对用户的识别,Windows下可以细致地针对某一个用户或者某一个组来分配特定的权限,那么Linux呢?本着简洁高效不折腾的原则,我们Linux系统只对3类用户设置权限。

注意我说的是3类,可不是3个用户哦。哪3类用户呢?就是文件的所有者、群组、其他。

在我们Linux系统中,每个文件都明确地属于一个用户。比如我们这里,/home/lanwoniu目录下的文件基本上都属于lanwoniu用户;/etc、/usr、/bin这些系统目录中的文件,都属于root用户等。一个文件所属于的那个用户,我们就叫他所有者吧。Linux系统中,可以对一个文件的所有者,设置一套权限。比如一个文件叫做“懒蜗牛记账.odt”,属于lanwoniu用户。那么可以设定,对于lanwoniu用户,这个文件可以被读和写,但不能执行(一个文档而已,当然不能执行)。

文件除了属于一个用户外,还要属于一个组。就好像你家里,你的电脑是属于你的,别人用不了(不知道开机密码)。但同时它也是属于你们家的,你爹虽然不能打开它,但是他有权把它卖掉(让你不好好学习,哼!)。Linux系统可以对文件所属的组设置一套权限。还比如“懒蜗牛记账.odt”文件,它除了属于lanwoniu用户外,还可以属于family组。那么就可以设定,对于family组的用户,这个文件可以被读,但不能写和执行。

除了所有者和群组之外的用户,就是“其他”了。可以对其他用户设置一套权限。例如除了lanwoniu用户和属于family组的用户以外,其他用户既不能读,更不能写和执行那个“懒蜗牛记账.odt”文件。就像你家邻居赵大婶完全不能够对你的电脑进行任何处理一样。

【设置权限的命令------chmod】

说了这么半天了,可能有人着急问:到底怎么针对用户设定文件的权限呢?好,下面我们就来介绍这个问题,这需要一个命令------chmod。

chmod是专门用来修改文件权限的命令,它的使用格式大约是这样:

chmod +xrw file

其中,“设置权限的对象”,就是指所有者、群组、其他这3类。当然你不能在命令里写中文“所有者”,而是用字母表示:u代表文件的所有者;g代表文件的群组;o代表其他。而文件的权限就是读、写、执行,当然也用字母表示:r表示读,w表示写,x表示执行。

比如我想给懒蜗牛日记.odt文件设置权限,我想给这个文件的所有者(也就是lanwoniu这个用户)增加可执行权限(就是个实验,别管这个动作多么抽风),那么就可以运行如下命令:

chmod u+x file

其中,u代表要对所有者的权限进行操作;+号代表要增加权限;x代表要增加的是执行权限。那么如果我想去掉family组内的成员对这个文件的读权限(日记不能瞎让别人看),那么就可以运行这样的命令:

chmod g-r file

其中,g代表要对群组的权限进行操作;-号代表要去掉权限;r代表要去掉的是读取的权限。于是family组的成员就不能够读取懒蜗牛同学的日记了。

或者,也可以在图形界面中对文件的权限进行设置。右击要设置权限的文件,在弹出的快捷菜单中选择“属性”,出现文件属性对话框。切换到“权限”标签,就可以看到对文件设置权限的界面了,如图6.9所示。

6.4 正则表达式

在使用Shell命令或者Shell编程的时候,经常会用到一种叫做“正则表达式”的东西。有了它,很多事情事半功倍。作为一名想成为高手的菜鸟,懒蜗牛同学觉得有必要了解一下这个东西。

6.4.1 什么是正则表达式

正则表达式(RegularExpression)是指一个用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串。听着挺迷茫的吧,那就说简单点:正则表达式就一段火星文似的字符串,这段字符串可以用来表示有一定规律的很多段字符串。

最初的正则表达式,出现在理论计算机科学的自动控制理论和形式化语言理论中(完全听不懂的举手)。在这些领域中有对计算的模型和对形式化语言描述与分类的研究。20世纪40年代,WarrenMcCulloch与WalterPitts将神经系统中的神经元描述成小而简单的自动控制元。到20世纪50年代,数学家StephenColeKleene利用称为“正则集合”的数学符号来描述此模型。后来,UNIX的创始人,KennethLaneThompson将此符号系统引入编辑器QED,然后是UNIX的编辑器ed,并最终引入了grep。自此,正则表达式被广泛地使用于各种UNIX或类UNIX系统的各种工具中。

6.4.2 初识正则表达式

懒蜗牛同学想学习正则表达式,也不是一时兴起。前几天,他确实遇到了类似的需求。

话说那一天,懒蜗牛拿到一个脚本文件,里面大量使用了sed命令。懒蜗牛同学想要复制里面的所有sed命令,存入另一个文件里,作为学习sed命令用法的参考。经过学习和研究,懒蜗牛了解到有个grep命令可以完成这个操作,他的用法大约是这样:

grep pattern file

这样的命令所做的事情,就是在“文件名”所指定的文件中,查找带有“字符串”所指定的内容的行,并输出到标准输出。当然,懒蜗牛同学是想存成文件,于是就加了个输出转向,运行了这个命令:

grep 'sed' .

*提示:*“>”可以将原本输出到标准输出的内容(即打印到屏幕上的内容),转向到一个文本文件中。并且如果文件已经存在,它将覆盖掉文件原有内容。如果不想覆盖文件原有内容,可以使用“>>”符号,代替“>”,如此则会在文件末尾追加新的内容。

这样,就把learn.sh脚本中,所有带有sed字样的行,全部输出到了sed\_command.txt文件中。但是当懒蜗牛打开sed\_command.txt查看的时候发现了问题。所有带有sed命令的行,固然是都写进来了,但同时还有很多跟sed命令无关的行,也跑了进来。比如这一行注释:

还有:

grep之所以会将这些行匹配出来,是因为它们确实包含了“sed”这个关键字,只不过不像懒蜗牛想象的作为单独的一个命令而已。所以这并不能怪罪grep不智能,而是懒蜗牛陈述的要求并不准确。懒蜗牛的要求如果用准确一点的人类语言描述,应该是这样:查找所有包含sed作为完整单词的行。

当然你用人类语言说,grep肯定听不懂。于是,这时候就需要正则表达式出场了。我们需要使用“\b”元字符,这个字符代表了单词的开头或者结尾。那么懒蜗牛要查找的东西,就应该这样表示:

./Images/image00543.jpeg

这样,像kissed、used这样的词,就不会被匹配到了。这里,我们写的“\bsed\b”就是一个正则表达式。除了“\b”外,还有很多元字符,各代表不同的意义,如表6.3所示。

表6.3 元字符

./Images/image00544.jpeg

*提示:*更精确地说,\b是匹配这样的一种位置------它的前一个字符和后一个字符,有一个是,但不全是\w(一个是,一个不是或不存在)。

6.4.3 强大的正则表达式

又有一回,那位和懒蜗牛同学聊天的MM遇到了麻烦。她需要从她写的日记中找到一个固定电话的号码。但是记忆力如此差的她,竟然完全不记得这个电话号码大约出现在哪几天的日记中。于是,她希望能够找出日记中出现过的所有电话号码,然后她根据上下文判断哪个才是需要的。

【使用反义字符】

这样的工作,懒蜗牛同学自然毫不犹豫地包揽下来。在收到MM传来的足有3 MB大的diary.txt文件后,懒蜗牛运行了这个命令:

./Images/image00545.jpeg

咱已经知道了,\d可以匹配一个数字。这个命令的意思很明白:查找diary.txt文r件中,所有出现了连续8个数字的行。

运行之后,懒蜗牛得到了一些输出,但是好像依旧比较乱。因为一些手机号、QQ号、各种账号之类的也被搜出来了。它们虽然都是8个以上连续的数字,但毕竟它们“包含”了连续的8个数字,所以被grep找出来了。

那么如果要精确匹配“有且只有8位的数字”该怎么办呢?这就要用到正则中的反义了。常用的反义字符如表6.4所示。

表6.4 反义字符

./Images/image00546.jpeg

那么,对于懒蜗牛同学的需求,就应该运行这样的命令:

./Images/image00547.jpeg

这个命令用人类语言描述就是:查找diary.txt文件中,所有出现了连续8个数字,且此8个数字的前后1个字符都不是数字的行。这样就能更准确地定位1个固定电话的号码了。

【使用重复】

不过这样写连续的8个“\d”还是有点累,其实这里可以精简一下,写成这样:

./Images/image00548.jpeg

其中,“{8}”的意思,就是前一个字符重复8次(\d看作一个元字符)。类似的重复还有几个,如表6.5所示。

表6.5 常用的重复

./Images/image00549.jpeg

举几个例子吧。比如正则表达式“go*gle”可以匹配“ggle”、“gogle”、“gooogle”、“goooooooooooogle”等。反正就是中间有多少个o、有没有o都没关系;而“go+gle”则不能匹配“ggle”,它代表必须有至少一个o;“go?gle”就只能匹配“ggle”和“gogle”;“go{2,4}gle”就只能匹配到“google”、“gooogle”、“goooogle”这3种情况了。

【使用中括号】

懒蜗牛终于找出了所有的“出现了连续8个数字,且此8个数字的前后1个字符都不是数字”的行,并让MM过目。结果,MM很不好意思地表示:这些好像都不是,那个电话号码有可能写成了xxxx xxxx的格式,也没准是xxxx-xxxx,或者是带区号的(xxx)xxxx-xxx......

从昏厥中苏醒过来的懒蜗牛同学毫不气馁,继续用正则表达式满足MM的需求。区号不区号的先不去管,先看看xxxx xxxx和xxxx-xxxx怎么匹配吧。其实也简单,使用中括号就可以了,像这样:

./Images/image00550.jpeg

这样的正则表达式用人类语言描述就是“前面有且仅有4个数字,中间有一个横杠‘-‘或者空白,后面有且仅有4个数字的”这么一个字符串。

其中中括号的意思,是表示里面的字符都是或的关系。例如“[abc]”可以匹配a、b、c中的任意一个(且仅有一个)字母。或者也可以写一个范围,例如“[a-z]”可以匹配任何一个小写的字母。“[0-9]”就完全相当于“\d”了。

咱们还是举例子吧。比如“b[ae]d”这样一个表达式,就可以匹配bed和bad这两个字符串;而“[a-c]an”可以匹配aan、ban、can这3个字符串。

在精确地匹配了xxxx-xxxx这种形式的电话号码后,懒蜗牛同学终于找到了MM想要的电话。因此,在接下来的几天之内,他一直都对正则表达式赞不绝口。不过,他用到的这点功能,不过是正则表达式中的最初级用法,沧海一粟尔。

6.4.4 无处不在的正则表达式

刚才咱们看着懒蜗牛同学折腾了这么半天,都是在使用grep命令时应用正则表达式。其实正则表达式的用途相当广泛,基本上在我们Linux系统里,你能想到的地方都能够支持正则表达式。比如常用的查找文件的find命令、已经见识过的grep命令、编辑字符流的sed命令,甚至连ls命令都是支持正则表达式的。还有Vim编辑中,Emacs编辑器中,都有支持正则表达式的操作。Shell编程中更可以用正则表达式了。

可以这么说,在我们Linux系统里,只要你觉得某个地方可以用正则表达式来简化操作、提高效率,那么这个地方就一定支持正则表达式。

6.5 多彩的Shell

懒蜗牛逐渐开始适应了纯终端的操作,于是他有个想法:在这个黑漆漆的界面中,搭建起一个可用的环境,这样以后开机就可以不进图形界面,直接用命令行的软件来做各种事情,可以更高效,更快速。他决定,用一周的时间来完成这件事情。

6.5.1 懒蜗牛同学的计划

起初,懒蜗牛来到混沌漆黑的命令行。

界面是黑漆漆一片,ls还会出些菱形的方块块,懒蜗牛的手指游弋在键盘上面。

懒蜗牛说:“要有中文!”就有了中文。

懒蜗牛看中文是顺眼的,有中文,有英文,没有了乱码。这是头一日。

懒蜗牛说:“要有声音!打破寂静的黑夜。”

懒蜗牛就让命令行里发出美妙的乐声,这是第2日。

懒蜗牛说:“要有窗,看到外面的世界。”于是有了浏览器,懒蜗牛可以重新看到那些网络上的奇花异草,光怪陆离。有了通信工具,懒蜗牛又可以把这些光怪陆离,异草奇花分享给朋友们,这是第3日。

懒蜗牛说:“要有色彩,有图,才有真相!”于是就有了图。

图片为黑白的世界带来了色彩。美好的、丑陋的、思念的、怀旧的、唯美的、憧憬的、现实的、抽象的,各种的图片,懒蜗牛微笑了。这是第4日。

懒蜗牛说:“要动起来,要鲜活的世界。”于是,就动起来了,这是第5日。

第6日,懒蜗牛说,我累了,要休息。于是,就没有于是了,这小子没开机。

众人:合着懒蜗牛就是传说中的上帝?

我说:不是,上帝第7日才休息。

众人:废话,那是上帝没赶上实行双休日。

我估计这么说您还是听不懂,那我慢慢给您讲解。

6.5.3 在Shell下播放音乐

周二,懒蜗牛得到了比较顺手的有中文环境的命令行之后,又开始踏上了新的寻觅旅程------寻觅音乐。

在图形界面下的时候,有很多播放软件可以让懒蜗牛浸泡在音乐的海洋里。那在字符界面下呢?想想也不应该有问题呀,放音乐又不需要图形界面是不是?字符界面下播放音乐还是不成问题的,Mplayer就可以播放各种音频,还有个Mpg123也可以放出声来。不过他们俩虽然能放出声音来,毕竟不专业,怎么也得有个播放列表、歌曲管理的功能吧。于是,懒蜗牛选择了moc。

*提示:*Gnome界面下,鼠标悬停在音乐文件上就可以预览其内容,这个功能就是通过调用Mpg123实现的。

【终端里的窗口】

moc是一个字符界面的音乐播放软件。有好奇而OUT的同学可能会猜想:字符界面播放音乐,那是不是要输入命令才播放音乐呢?比如输入play,就播放;输入stop就暂停;输入load xxxx.list就导入播放列表这样?要是30年前没准能有这种软件,不过在现在这么简洁高效的年代,怎么可能用这么难用的东西呢?虽说是字符界面,但是不代表就不能有窗口!是的,你没听错,在字符界面下也能画出窗口来!怎么画?用字符拼!

我们知道,ASCII码中有一些特殊的字符,什么横杠、竖杠、拐弯杠什么的。用这些特殊的符号,加上可控制的字符底色,可以拼接出有窗口效果的终端显示的界面。

当然,如果每个程序都自己写代码去拼肯定是很费事的,所以崇尚共享的我们Linux系统提供了一套专门在字符界面下画窗口的函数库,叫做ncurses。调用这个库画窗口就跟用gtk+提供的接口在图形界面下画窗口类似,所以编程人员不必在意怎么用各种字符拼出好看的窗口,只要调用ncurses就行了。ncurses的前身是curses,大名鼎鼎的Vi就是用它实现的界面。

【moc的操作】

moc就是一个基于ncurses的、字符界面的音乐播放软件。直接找apt就可以安装。懒蜗牛装了之后赶紧运行一下试试。虽然包名叫moc,不过运行的命令是mocp。运行起来之后如图6.11所示。

./Images/image00560.jpeg

图6.11 mocp运行界面

运行起来之后,左边是文件列表窗口,可以通过上下箭头选择相应目录,按回车键进入,最上面的那个“..”表示上一级目录,这个都知道吧,不知道的面壁去。

选到要听的MP3文件按A键将文件加入右边的播放列表。把MP3都加进来之后,用Tab键,切换左边的文件列表和右边的播放列表。按回车键就可以播放了。然后还有些快捷键比较常用,介绍如下。

 h键,打印出使用说明。

 左右方向键可以调节播放的进度。

 “,”、“.”用来控制音量,“,”键为减小,“.”键为增大。

 s键停止播放。

 b、n键分别是跳到“上一首”、“下一首”曲目。

 R和X键用来控制重复播放和循环播放(注意大小写的区别,所谓按R键,就是按下Shift然后按下r键,也就是在终端打出大写的R的操作顺序)。

有人说,这个播放软件把整个屏幕都占了,我还怎么干别的呀?简单,你有如下3个选择。

(1)不干别的了。

(2)按下Ctrl+Alt+F2组合键,换个终端。当然,这两个方法都是比较弱智的,呵呵。

(3)其实mocp是一个Server和前端分离的软件,你可以按q键退出前端界面,然后该干啥干啥,音乐继续播放,想再回来就再运行mocp。如果想彻底退出,就按Q键,也就是按住Shift再按q键。

好了,懒蜗牛已经添加好播放列表,按下回车键,音箱里传来了悠扬的乐曲。

6.5.4 在命令行中上网

周三这天,懒蜗牛要解决一个关键的应用------上网。平时在图形界面下,最忙碌的就是狐狸妹妹Firefox,基本上只要电脑开着,内存里就少不了狐狸妹妹的身影。要是命令行下不能上网,那还不得把懒蜗牛同学憋屈死。

不过命令行毕竟是命令行,功能还是有限的,所以对浏览器的要求也不能太高,简单的文字的网页还是没问题的。图片呢,实时的显示也不行,不过勉强可以调用别的程序来看一下。但是像Flash、在线的视频音频这类的就不用想了。好在懒蜗牛同学比较通情达理,也就是想看看文字的东西和一些简单的图片,所以,有那么几个候选软件是可以满足懒蜗牛要求的:w3m, lynx, links。

【命令行中的浏览器------w3m】

这3个软件都是可以直接叫apt去装的,很省事。但经过懒蜗牛的试用,lynx和links对中文的支持都相对差点,并且他们都不能显示图片,所以被pass了,w3m华丽胜出。不过w3m也不是天生就会显示图片,他需要一些工具,这些工具被打成一个包放在软件源里,包的名字叫做w3m-img,懒蜗牛让apt给w3m搬来了这个工具包之后,w3m才拥有了显示网页上的图片的能力。

当然,w3m显示图片跟moc显示窗口可不一样,可不是拿特殊字符拼出图片,如果是那样,整个屏幕顶多拼个超级玛丽出来,连蘑菇都没地方显示了,估计就是图6.12所示的这个效果。

./Images/image00561.jpeg

图6.12 字符拼成的超级玛丽

要显示正经的JPG这样的图片,还是需要framebuffer的支持。framebuffer基本上是能够使命令行界面变得多姿多彩的最基本、最重要的条件。懒蜗牛要访问的网页无疑基本上都是中文的,所以需要能够支持中文的终端。咱之前说过了,就是fbterm。于是,懒蜗牛就在fbterm下运行起了w3m。

【jfbterm中的w3m】

不过,天有不测风云日,人有无意失手时,软件也有偶尔不大正常的时候。

虽然在某些系统某些机器上,fbterm下的w3m(前提是装了w3m-img哦)确实可以正常地显示出图片,不过不知道为什么在我们这里,这个w3m和fbterm配合了半天也没配合上。他们一运行起来就听着他们俩不停地吵吵:

“fbterm,快点把fb0设备给我,我要显示网页上这个图片。”

“你怎么又要啊,我这边的中文还没显示好呢。”

“你怎么那么多中文要显示啊?”“你这不废话么,我显示的都是你的网页,显示多少中文还不是你说了算。”“别急别急,你显示完了赶紧给我用。”“等等,等等,再有3毫秒就完了。”

“靠,懒蜗牛翻篇了。刚才那个图片翻过去了,白算了半天。”

“好了我用完了,给你用去吧。”

“用什么用,他已经翻到下一页了,又一个新的图片,我先得解码看看这个图片里是什么才知道该显示什么嘛。”

总之,这俩软件从来没对上过,于是懒蜗牛就没看到图片。

*提示:*Ubuntu 10.04中,开启fbterm后会因/dev/fb0设备被占用而导致w3m无法显示图片。

不过后来懒蜗牛还是找到了解决办法,那就是换了另一个终端,其实跟fbterm还有点关系,叫做jfbterm,也一样可以支持中文。在jfbterm下,w3m终于可以正常显示图片了,虽然效果肯定不如狐狸的好看,但能在不开图形界面的情况下看到图片,也算奢侈了,就是类似图6.13这样的效果。

./Images/image00562.jpeg

图6.13 用w3m访问Google

6.5.5 在Shell下看图片

周四,懒蜗牛又一次摄影归来,插上SD卡,运行mount命令挂载并且把照片cp到主目录中的照片文件夹。之后,就开始研究命令行下到底用什么来看照片了。

要看照片无疑还是需要framebuffer的支持,这一点咱就不说了。那么那个能够利用framebuffer设备来显示图片的软件是谁呢?那可是一个大名鼎鼎的家伙------fbi!

别激动,这个fbi不是某大叔的啥啥调查局,只是Linux下的一个FrameBuffer Image Viewer,也就是基于framebuffer的图像查看器而已。这个查看器可以查看各种常见的图片格式,全屏显示、缩放、幻灯片模式播放、旋转,都没问题,更复杂的什么调节对比度、亮度啥的自然就不行了,毕竟人家只是个查看器嘛。懒蜗牛复制好了照片后,用cd命令切换进入存着照片的目录,运行了:

./Images/image00563.jpeg

这个命令大家应该能看懂吧,就是让fbi去查看当前目录下的所有的JPG文件的意思。顺便复习一下,“*”这个通配符是由bash来处理的,就是说,懒蜗牛输入了这个命令后,bash先把命令改成:

./Images/image00564.jpeg

这样的形式,然后再按照这个命令去叫醒fbi,告诉他要去显示这么多图片。然后就是fbi起床,一个一个地显示这些图片,当懒蜗牛按“-”、“+”键的时候就对图片进行缩小、放大。按下PageDown键就播放下一张,偶尔遇到竖着拍的照片可能还需要按r或者l键旋转一下,总之,懒蜗牛在悠然轻松地欣赏照片的过程中度过了一晚上。

6.5.6 在Shell下播放视频

光看静态的照片还是有些不过瘾的,于是周五,懒蜗牛同学复制来了一个叫“lanjingling.rmvb”的文件,看名字好像是叫什么“懒精灵”?真是什么人看什么片。

【观看本地文件】

那命令行下能看片么?答案依然是肯定的。当然,也依然离不开framebuffer。平时在图形界面下懒蜗牛经常用来看片的播放器是SMplayer,当初咱们介绍这个家伙的时候就说过,他只是个前端界面,真正在后面默默无闻地进行播放工作的是Mpayer,Smplayer能播放什么文件完全取决于Mplayer能播放什么文件,只是因为Mplayer总是在后台,人们都忽略了他的存在。现在,到了字符界面,Mplayer终于可以从后台来到前台了。懒蜗牛用cd命令来到了存放lanjingling.rmvb这个文件的目录里,运行了:

mplayer

于是,一个动态的画面出现在了黑漆漆的屏幕上,一群蓝皮肤白帽子的小家伙映入了懒蜗牛同学的眼帘。我猜懒蜗牛同学一定最喜欢里面那个叫惰惰的家伙。

*提示:*如果不添加任何参数运行“mplayer路径/视频文件”,则只会按照视频的原始大小显示,且此时按f快捷键无法全屏。效果类似图6.14所示。如果需要全屏播放,需要添加“-fs”参数,例如:

./Images/image00566.jpeg

图6.14 在命令行下播放视频

./Images/image00567.jpeg

【观看流媒体】

除了硬盘里现成的视频文件以外,Mplayer还可以播放mms流媒体,也就是一些在线的视频也是可以播放的。

懒蜗牛找到了一些网上的电视台的流媒体地址,什么CCTV5啊,CCTV8啊,这TV那TV的,反正我是不知道什么内容,但是懒蜗牛看得很起劲。流媒体的地址大约就是 mms://URL/FILENAME 这样的形式,要看这样的媒体可以简单地运行:

mplayer mms:

好像懒蜗牛同学屋里也没有那个叫做TV的东西,于是有了mplayer,他就拿电脑当作TV用了。

6.5.7 扩展阅读:bash的发展历史

【Thompson Shell】

在最初创造UNIX系统的时候,KenThompson大牛在Bell实验室写了一个简单的程序,将它作为新的UNIX操作系统的接口界面来让系统和人类进行交流。这个程序就叫做Shell,那时候还没正式起名字,为了让使用这个Shell的人们不忘记作者的辛劳,大家就管它叫ThompsonShell了。

ThompsonShell的功能很简单,用户通过它输入一些指定的命令,它负责解释为需要计算机做的操作,并去执行。另外它还能够支持一些简单的脚本,就是把一堆命令写进一个文件里依次执行。但并没有更高级的例如流程控制、分支、变量、函数之类的东西。

【Bourne Shell】

同时,Ken/Thompson的同事,同在Bell实验室的SteveBourne(就是图6.15所示的这位)也写了一个Shell程序。作为一个有很多重要程序要写的大牛,SteveBourne也没来得及给他写的Shell起名字,于是人们同样为了不忘记作者的贡献,管这个Shell叫做Bourne Shell。这个Shell跟Thompson Shell不同,他加入了流控制,可以写简单的函数。

./Images/image00569.jpeg 图6.15 Steve Bourne

【标准Shell之争】

等到了20世纪70年代晚期,每种Shell在Bell实验室都有不少人用,于是形成了两个派别。说来这两个Shell应该都是很不错的,用熟悉了都很顺手,但是这两个Shell是互不兼容的,就如同修习了少林的内功后再去学武当的心法,多半不是很习惯。

但是作为一个成熟的商业化的系统,总该有个默认的、标准的Shell才方便用户学习使用。于是在Bell实验室里,分别支持ThompsonShell和BourneShell的两大帮派,进行了激烈的辩论,经过3次连续的UNIX用户组集会上两大帮派的斗争之后,终于确立了BourneShell成为UNIX的标准Shell。

【Bash诞生】

等到1978年,Bourne Shell随着Version7 UNIX一同发布,终于告别了实验室,和广大用户见面了。9年后,1987年,一个叫做BrianFox的家伙非常喜欢BourneShell,并且觉得它还可以更加完善,于是开始在BourneShell的基础上进行创造,几年后它成为一个更加完整而且好用的Shell。出于对BourneShell的缅怀和崇拜,他将这个Shell命名为Bourne Again Shell------简称bash。现在,bash是绝大多数Linux系统及Mac OS X v10.4系统的默认Shell。甚至还被移植到了Windows系统上,什么?你没见过?那你听说过Cygwin吧,那里面就是bash。

6.6 本章小结

这个懒蜗牛同学对于Linux系统,可说是越用越有感觉了。在图形界面下玩腻了就跑到字符界面下感受了一番。他记住了一些简单的命令,还学会了编写简单的Shell脚本和使用正则表达式。但这不是主要的,更重要的是了解了命令行的很多基本的概念,也可以感受到Linux系统的一些优秀的设计思想。有了命令行的使用基础以后,懒蜗牛接下来会去学点什么呢?咱们下回再说。