Shell 十三问
作者:www.chinaunix網中人
详细讨论在:bbs.chinaunix/forum/viewtopic.php?t=218853&postdays=0&postorder=asc&start=0
1) 为何叫做 shell

在介绍 shell 是甚么东西之前,不妨让我们重新检视使用者与计算机系统的关系:
(FIXME)

我们知道计算机的运作不能离开硬件,但使用者却无法直接对硬件作驱动,
硬件的驱动只能透过一个称为"操作系统(Operating System)"的软件来控管,
事实上,我们每天所谈的 linux ,严格来说只是一个操作系统,我们称之为"核心(kernel)"
然而,从使用者的角度来说,使用者也没办法直接操作 kernel
而是透过 kernel "外壳"程序,也就是所谓的 shell ,来与 kernel 沟通。
这也正是 kernel shell 的形像命名关系。如图:
(FIXME)

从技术角度来说,shell 是一个使用者与系统的互动界面(interface)
主要是让使用者透过命令行(command line)来使用系统以完成工作。
因此,shell 的最简单的定义就是---命令解译器(Command Interpreter)
* 将使用者的命令翻译给核心处理,
* 同时,将核心处理结果翻译给使用者。

每次当我们完成系统登入(log in),我们就取得一个互动模式的 shell ,也称为 login shell primary shell
若从行程(process)角度来说,我们在 shell 所下达的命令,均是 shell 所产生的子行程。这现像,我们暂可称之为 fork
如果是执行脚本(shell script)的话,脚本中的命令则是由另外一个非互动模式的子 shell (sub shell)来执行的。
也就是 primary shell 产生 sub shell 的行程,sub shell 再产生 script 中所有命令的行程。
(关于行程,我们日后有机会再补充。)

这里,我们必须知道:kernel shell 是不同的两套软件,而且都是可以被替换的:
* 不同的操作系统使用不同的 kernel
* 而在同一个 kernel 之上,也可使用不同的 shell
linux 的预设系统中,通常都可以到好几种不同的 shell ,且通常会被列于如下档案里:
/etc/shells
不同的 shell 有着不同的功能,且也彼此各异、或说"大同小异"
常见的 shell 主要分为两大主流:
sh
burne shell (sh)
burne again shell (bash)
csh
c shell (csh)
tc shell (tcsh)
korn shell (ksh)
(FIXME)

大部份的 Linux 系统的预设 shell 都是 bash ,其原因大致如下两点:
* 自由软件
* 功能强大
bash gnu project 最成功的产品之一,自推出以来深受广大 Unix 用户喜爱,
且也逐渐成为不少组织的系统标准。
2) shell prompt(PS1) Carriage Return(CR) 的关系?

当你成功登录进一个文字界面之后,大部份情形下,
你会在荧幕上看到一个不断闪烁的方块或底线(视不同版本而别)
我们称之为*游标*(coursor)
游标的作用就是告诉你接下来你从键盘输入的按键所插入的位置,
且每输如一键游标便向右边移动一个格子,若连续输入太多的话,则自动接在下一行输入。

假如你刚完成登录还没输入任何按键之前,你所看到的游标所在位置的同一行的左边部份,
我们称之为*提示符号*(prompt)
提示符号的格式或因不同系统版本而各有不同,在 Linux 上,只需留意最接近游标的一个可见的提示符号,通常是如下两者之一:
$:给一般使用者帐号使用
#:给 root (管理员)帐号使用

事实上,shell prompt 的意思很简单:
* shell 告诉使用者:您现在可以输入命令行了。
我们可以说,使用者只有在得到 shell prompt 才能打命令行,
cursor 是指示键盘在命令行所输入的位置,使用者每输入一个键,cursor 就往后移动一格,
直到碰到命令行读进 CR(Carriage Return,由 Enter 键产生)字符为止。
CR 的意思也很简单:
* 是使用者告诉 shell:老兄你可以执行我的命令行了。
严格来说:
* 所谓的命令行,就是在 shell prompt CR 字符之间所输入的文字。
(思考:为何我们这里坚持使用 CR 字符而不说 Enter 键呢?答案在后面的学习中揭晓。)

不同的命令可接受的命令行格式或有不同,一般情况下,一个标准的命令行格式为如下所列:
command-name options argument

若从技术细节来看,shell 会依据 IFS(Internal Field Seperator) command line 所输入的文字给拆解为"字段"(word)
然后再针对特殊字符(meta)先作处理,最后再重组整行 command line
(注意:请务必理解上两句话的意思,我们日后的学习中会常回到这里思考。)

其中的 IFS shell 预设使用的字段分隔符,可以由一个及多个如下按键组成:
* 空格键(White Space)
* 表格键(Tab)
* 回车键(Enter)

系统可接受的命令名称(command-name)可以从如下途径获得:
* 明确路径所指定的外部命令
* 命令别名(alias)
* 自定功能(function)
* shell 内建命令(built-in)
* $PATH 之下的外部命令
每一个命令行均必需含用命令名称,这是不能缺少的。
3) 别人 echo、你也 echo ,是问 echo 知多少?

承接上一章所介绍的 command line ,这里我们用 echo 这个命令加以进一步说明。
温习---标准的 command line 包含三个部件:
* command_name option argument

echo 是一个非常简单、直接的 Linux 命令:
* argument 送出至标准输出(STDOUT),通常就是在监视器(monitor)上输出。
(注:stdout 我们日后有机会再解说,或可先参考如下讨论:
www.chinaunix/forum/viewtopic.php?t=191375 )

为了更好理解,不如先让我们先跑一下 echo 命令好了:
代码:
$ echo

$

你会发现只有一个空白行,然后又回到 shell prompt 上了。
这是因为 echo 在预设上,在显示完 argument 之后,还会送出一个换行符号(new-line charactor)
但是上面的 command 并没任何的 argument ,那结果就只剩一个换行符号了...
若你要取消这个换行符号,可利用 echo -n option
代码:
$ echo -n
$

不妨让我们回到 command line 的概念上来讨论上例的 echo 命令好了:
* command line 只有 command_name(echo) option(-n),并没有任何 argument
要想看看 echo argument ,那还不简单﹗接下来,你可试试如下的输入:
代码:
$ echo first line
first line
$ echo -n first line
first line $

于上两个 echo 命令中,你会发现 argument 的部份显示在你的荧幕,而换行符号则视 -n option 的有无而别。
很明显的,第二个 echo 由于换行符号被取消了,接下来的 shell prompt 就接在输出结果同一行了... ^_^

事实上,echo 除了 -n options 之外,常用选项还有:
-e :启用反斜线控制字符的转换(参考下表)
-E:关闭反斜线控制字符的转换(预设如此)
-n :取消行末之换行符号( -e 选项下的 \c 字符同意)

关于 echo 命令所支持的反斜线控制字符如下表:
\aALERT / BELL (从系统喇叭送出铃声)
\bBACKSPACE ,也就是向左删除键
\c:取消行末之换行符号
\EESCAPE,跳脱键
\fFORMFEED,换页字符
\nNEWLINE,换行字符
\rRETURN,回车键
\tTAB,表格跳位键
\vVERTICAL TAB,垂直表格跳位键
\nASCII 八进位编码( x 开首为十六进制)
\\:反斜线本身
(表格资料来自 O'Reilly 出版社之 Learning the Bash Shell, 2nd Ed.)

或许,我们可以透过实例来了解 echo 的选项及控制字符:

例一:
代码:
$ echo -e "a\tb\tc\nd\te\tf"
a       b       c
d       e       f

上例运用 \t 来区隔 abc 还有 def ,及用 \n def 换至下一行。

例二:
代码:
$ echo -e "\141\011\142\011\143\012\144\011\145\011\146"
a       b       c
d       e       f

与例一的结果一样,只是使用 ASCII 八进位编码。

例三:
shell代码
代码:
$ echo -e "\x61\x09\x62\x09\x63\x0a\x64\x09\x65\x09\x66"
a       b       c
d       e       f

与例二差不多,只是这次换用 ASCII 十六进制编码。

例四:
代码:
$ echo -ne "a\tb\tc\nd\te\bf\a"
a       b       c
d       f $

因为 e 字母后面是删除键(\b),因此输出结果就没有 e 了。
在结束时听到一声铃向,那是 \a 的杰作﹗
由于同时使用了 -n 选项,因此 shell prompt 紧接在第二行之后。
若你不用 -n 的话,那你在 \a 后再加个 \c ,也是同样的效果。

事实上,在日后的 shell 操作及 shell script 设计上,echo 命令是最常被使用的命令之一。
比方说,用 echo 来检查变量值:
代码:
$ A=B
$ echo $A
B
$ echo $?
0

(注:关于变量概念,我们留到下两章才跟大家说明。)

好了,更多的关于 command line 的格式,以及 echo 命令的选项,
就请您自行多加练习、运用了...
4) " "(双引号) ' '(单引号)差在哪?

还是回到我们的 command line 来吧...
经过前面两章的学习,应该很清楚当你在 shell prompt 后面敲打键盘、直到按下 Enter 的时候,
你输入的文字就是 command line 了,然后 shell 才会以行程的方式执行你所交给它的命令。
但是,你又可知道:你在 command line 输入的每一个文字,对 shell 来说,是有类别之分的呢?

简单而言(我不敢说这是精确的定议,注一)command line 的每一个 charactor ,分为如下两种:
* literal:也就是普通纯文字,对 shell 来说没特殊功能。
* meta:对 shell 来说,具有特定功能的特殊保留字符。
(注一:关于 bash shell 在处理 command line 时的顺序说明,
请参考 O'Reilly 出版社之 Learning the Bash Shell, 2nd Edition,第 177 - 180 页的说明,
尤其是 178 页的流程图 Figure 7-1 ... )

Literal 没甚么好谈的,凡举 abcd123456 这些"文字"都是 literal ... (easy)
meta 却常使我们困惑..... (confused?)
事实上,前两章我们在 command line 中已碰到两个机乎每次都会碰到的 meta
* IFS:由 <space> <tab> <enter> 三者之一组成(我们常用 space )
* CR:由 <enter> 产生。
IFS 是用来拆解 command line 的每一个词(word)用的,因为 shell command line 是按词来处理的。
CR 则是用来结束 command line 用的,这也是为何我们敲 <enter> 命令就会跑的原因。
除了 IFS CR ,常用的 meta 还有:
= 设定变量。
$ 作变量或运算替换(请不要与 shell prompt 搞混了)
> :重导向 stdout
< :重导向 stdin
|:命令管线。
& :重导向 file descriptor ,或将命令置于背境执行。
( ):将其内的命令置于 nested subshell 执行,或用于运算或命令替换。
{ }:将其内的命令置于 non-named function 中执行,或用在变量替换的界定范围。
; :在前一个命令结束时,而忽略其返回值,继续执行下一个命令。
&& :在前一个命令结束时,若返回值为 true,继续执行下一个命令。
|| :在前一个命令结束时,若返回值为 false,继续执行下一个命令。
!:执行 history 列表中的命令
....

假如我们需要在 command line 中将这些保留字符的功能关闭的话,就需要 quoting 处理了。
bash 中,常用的 quoting 有如下三种方法:
* hard quote' ' (单引号),凡在 hard quote 中的所有 meta 均被关闭。
* soft quote " " (双引号),在 soft quoe 中大部份 meta 都会被关闭,但某些则保留( $ )(注二)
* escape \ (反斜线),只有紧接在 escape (跳脱字符)之后的单一 meta 才被关闭。
( 注二:在 soft quote 中被豁免的具体 meta 清单,我不完全知道,
有待大家补充,或透过实作来发现及理解。 )

下面的例子将有助于我们对 quoting 的了解:
代码:
   $ A=B C   # 空格键未被关掉,作为 IFS 处理。
   $ C: command not found. (FIXME)
   $ echo $A
   
   $ A="B C"   # 空格键已被关掉,仅作为空格键处理。
   $ echo $A
   B C

在第一次设定 A 变量时,由于空格键没被关闭,command line 将被解读为:
* A=B 然后碰到<IFS>,再执行 C 命令
在第二次设定 A 变量时,由于空格键被置于 soft quote 中,因此被关闭,不再作为 IFS
* A=B<space>C
事实上,空格键无论在 soft quote 还是在 hard quote 中,均会被关闭。Enter 键亦然:
代码:
   $ A='B
   > C
   > '
   $ echo $A
   B
   C

在上例中,由于 <enter> 被置于 hard quote 当中,因此不再作为 CR 字符来处理。
这里的 <enter> 单纯只是一个断行符号(new-line)而已,由于 command line 并没得到 CR 字符,
因此进入第二个 shell prompt (PS2,以 > 符号表示)command line 并不会结束,
直到第三行,我们输入的 <enter> 并不在 hard quote 里面,因此并没被关闭,
此时,command line 碰到 CR 字符,于是结束、交给 shell 来处理。

上例的 <enter> 要是被置于 soft quote 中的话,也会同样被关闭,用 escape 亦可:
代码:
   $ A=B\
   > C\
   >
   $ echo $A
   B
   C

上例中,第一个 <enter> 跟第二个 <enter> 均被 escape 字符关闭了,因此也不作为 CR 来处理,
但第三个 <enter> 由于没被跳脱,因此作为 CR 结束 command line

至于 soft quote hard quote 的不同,主要是对于某些 meta 的关闭与否,以 $ 来作说明:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。