这不是我想要的shell

世上本没有shell,命令多了,也就成了shell。

——Pope

要说shell,就不能不谈起Unix的Keep It Simple Stupid理念。Unix社区崇尚简洁、专注,鼓励一个工具只做一件事,做好这件事。当你需要完成一项复杂的工作时,分解,细化,再交给相应的小工具去执行。这样就需要提供一种机制把各个工具连接起来。所以,在我开来,学习shell重在掌握任务分解和命令间的互联。

当然,你还会发现,shell的用法很奇怪,毕竟它是个很古老的东西了,包容各种历史遗留也是无奈之选。

 


 

其实,本文是一篇Learning the Bash Shell (3rd)的读书笔记。

shell是一种用户与类Unix操作系统之间以文字方式进行交互的媒介。还有其他的交互形式,比如图形界面。

shell命令一般是祈使句 如:去 给老子 拿瓶水来。

shell命令先是一个动词,紧接着若干副词(option)和名词(argument)(也可能没有)。

操作的对象大多是文件(Unix里几乎所有东西都是文件)。文件分为普通文件(文本),可执行文件和目录。

目录可以包括若干子目录,包括本目录(.)和父目录(..)。目录被组织成树形结构,用路径来标示。

可以明确指定shell操作的对象的绝对路径,如果不说就默认是在当前目录下。当前目录虽然被称为目录却没有物理对应,是一个概念上的浮动地址,可以通过pwd(print the working directory,你看多怪的名字,不知道的还以为是password呢)命令来查看,还可以用cd命令(change the working directory)来修改。此外还有主目录(~)和操作记录中的上一目录(-)。

shell的操作对象可以是具体名词(如某个文件名),也可以是抽象名词(用来描述某一特征,需要用到通配符(wildcard))。

通配符有三种基本模式:

匹配任一字符、匹配任意字符、匹配字符集。

!, *, […], [!…], {…}

Unix将I/O设备统一看作文件,并把I/O看作处理任意长的字节流。

Unix把I/O流简化为标准输入输入(standard input)、标准输出(standard output)和标准错误输出(standard error output)三个端口。输入流默认为键盘,输出和错误流默认为显示屏。如果需要,可以将这些端口映射到其他设备或文件上(重定向)。

Unix下的小工具往往只专注于某项处理。数据被不同的小工具一道道筛选、加工,最后得到想要的结果。正是因为专注,让它们更容易组合起来构建复杂的工具。

  • 奇怪的命名

虽然shell命令的命名也不是无章可循,但总归是很奇怪的。以下是几个常用的工具:

但比起那些预设变量,这还算好的。

比如位置参数,人家名字直接叫1, 2, ⋯⋯要想取到它们的值,前面加$($var其实是${var}的简写),即$1, $2, ⋯⋯。还有0(脚本文件的名字)、*(所有位置参数构成的字符串,用IFS(Internal Field Separator)的首字符作分隔符)、@("$1" "$2" ... "$N")、#(位置参数的个数)⋯⋯

不带参数的cat就像

while true { echo input }

  • 有管道还不够吗

cat是用来解释重定向的绝佳例子: cat < file1 > file2 等价于cp file1 file2。command < filename,表示command从某个文件而不是标准输入拿到数据; command > filename,表示command将数据输出到某个文件而不是标准输出。

我觉得这个设计很好,但记法太屎。我宁愿多敲几个字,把从哪输入,输出到哪明确清楚了(比如: file1 > cat > file2)而不是把<和>作为前缀贴到操作对象前面。

除了重定向输出到某个文件,你还可以把输出作为其他命令的输入直接导入(管道pipe,用|表示。这个记法也很屎,体现不出方向来,虽然知道是从左到右。为什么不能和重定向统一起来呢?!比如,$ cut -d: -f1 < /etc/passwd | sort | lp 写出 /etc/passwd > cut -d: -f1 > sort > lp 的形式。可能是于当初把命令设计成前缀形式有关吧,如果当初设计成中缀形式并把命令也看成文件(命令本来就是内存里的一个文件),也许会方便很多)。

  • 文本编辑原来这么阳春白雪

bash的emacs-mode对文本编辑提供很精细的控制:对字符的操作(^B, ^F, ^D, )、对单词的操作(⎋B, ⎋F, ⎋D, ⎋)、对整句的操作(^A, ^E, ^K, ^Y(找回前一次被删掉的东西)),甚至是对历史记录的操作(^P(相当于), ^N(相当于), ^R)。

但emacs-mode最强大的功能还得是文本补全()。当你敲入几个字符并按下,可能会出现四种情况:

  • 没有以这些字符开头的词,shell会小叫一声beep;
  • 找到一个唯一的匹配,shell会补全它,并附带一个空格方便你敲其他字符;
  • 如果找到一个唯一匹配的目录,shell会补全文件名,并附上一个斜杠(/);
  • 如果有多个匹配可能,shell会补上这些可能匹配的最长前缀。

如果你想知道都有哪些可能的选择,敲两下(或者按⎋-?,它会显示所有可能选择)。

shell提供了很多快捷键来补全不同类型的名字(比如,-/ 专门补文件名,⎋-~专补用户名,⎋-!专补命令名⋯⋯)

既然提到了emacs-mode,不用想也能知道肯定有个vi-mode。

vi最著名的就是用h, j, k, l作为←↓↑→。这得追溯到当初Bill Joy用ADM-3a开发vi,这种机器的h, j, k, l键上分别有四个←↓↑→小箭头,所以后面你知道的。

vi-mode和vi一样,也有两种模式(输入模式和控制模式)。在输入模式下,你可以键入、删除(⌫, ^W)。按下可以进入控制模式。这个模式下,可以用单键来控制移动(h, l, b, w, 0, $)(很喜欢这种单键控制的方式,其实很多软件都可以借鉴。如果你不用编辑文字,干嘛要用control、shift这类的辅助键。这就好像有名字空间你不用,非得都搞成全家变量一样)。你不光可以移动一个字符(h, l)、一个单词(b, w),甚至还可以移动到某个特定的字符上(F-×, f-×,×表示任意字符)按下i(或a, I(等效于0i), A (等效于$a), R (与r略有不同,r只替换一个字符而且不会进入输入模式,即你不能再键入其他字符))切回输入模式。控制模式下还有个不得不提的命令——撤销(u, undo)。另外“.”也很有用,它反复执行上一次的操作。

vi也有文本补全,不过不是,而是进入控制模式按反斜杠(/)。

一直觉得vi很有意思,设计很精巧,像一门微型语言。比如删除操作,按下d(delete)之后不会有什么变化,还要指定删除的方向(向前还是向后)和长度(字符还是单词还是整句,是当前光标位置到行首还是到行尾)。这不是v. + adv. + n. 结构么!而且一个动作可以有多个说法(比如删除整句,可以用dd,也可以用0d$)。让你感觉它不是一堆互不相干的快捷键,而是一句意义明确的指示。

命令行工具一个总的印象就是太费脑子,这里指的是要记的东西太多,不如GUI直观。但好处就在于它能控制得很精细,不像GUI,界面设计成什么样就只有哪些功能。

  • 到底0是真是假

在流程控制中,shell用返回状态作为逻辑判断的结果。这样,0(在[...]中)就表示真,其他返回码均表示假。但在算术表达式($((...))或$[...])中,0却是假,1才是真。而算术式的另一种形式((...))却用0作为真,1为假-_-!!

  • 丑陋的语法

如果说if...then...fi是bash的特色的话,bash的case这是巨丑陋:

 

你就不能正常一点?!至少对称一点,别弄出半边括号来好不。

还有,按理说shell的语法是不依赖空格或是换行的。可你看看赋值语句,多了空格它不干,再看看[ ... ]少了空格它也不干。而在for, while, select里do如果新起一行就不用打分号。

shell语法看似统一,其实很混乱。这货不是一门语言,而是一堆微型语言,虽然每个都很简单,但要想全都记住却有不小难度。

P.S. Learning the bash Shell似乎没有继承Learning系列简单明了的衣钵,看似很全,其实毫无系统。示例晦涩,讲解不清,比如对getopts里OPTSTRING(Mac下好像是这个,书中是OPTARG)的说明,OPTSTRING是一个由option及其所带参数构成的一个序列,而OPTIND是OPTSTRING里下一个要处理的option的下标(不敢举例,怕自己理解有误)。

 

  • 我想要的shell:

首先互联功能必须很强大;

灵活的语法(不依赖空格神马的,不限定格式);

统一的语法;

⋯⋯

(cont.)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值