M.Tim Jones 是 developerWorks 全球网站的一位多产撰稿人,他的文章涉及的主题非常广泛而有深度。Tim 为了让大家全面了解 Linux,推出了一系列内核剖析方面的文章,深受大家的欣赏;随着 Linux 在虚拟化技术方面的日臻成熟,Tim 及时为我们带来了相关内容的介绍。在本文中,他为我们带来了Linux Shell的进化史。

计算机上的大多数日常工作任务,通过鼠标点击即可完成,但要想真正发挥Linux本身的威力,还需要Shell和命令行。Shell就如同编辑器一样,你可以根据个人爱好选择。虽然不同的Shell可以提供不同的功能,但是它们的核心开发思想都是一致的。

现在让我们来追寻Shell的发展足迹,并探讨几个现在流行的Shell。

Shell的历史

Shell也称命令行解释器,有着悠久的历史,我们从第一个UNIX Shell说起。

V6 Shell

1971年,贝尔实验室的Ken Thompson为UNIX开发了第一个Shell——V6 Shell。类似于它在Multics上的前身,V6 Shell(/bin/sh)是一个在内核之外执行的独立的用户程序。

诸如通配符(模式匹配参数扩展,如*.txt)之类的概念在独立的glob实用程序中实现,如同if命令评估条件表达式一样。这种实现保证了Shell的短小精悍,它只有不到900行的C源代码。

V6 Shell引入了一系列简洁的语法,用于重定向(< >和>>)和管道(|或^),并一直沿用至今。你也会发现,它提供了对顺序调用命令(;)和异步命令(&)的支持。

Thompson Shell(即V6 Shell)的缺陷在于脚本编程能力的不足,它只是一个交互性的命令解释器。

Bourne Shell

1977年,Stephen Bourne在贝尔实验室为V7 UNIX开发了Bourne Shell,并一直沿用至今。很多情况下,它甚至是默认的Shell。Bourne Shell是用C语言开发的,但相比其他Shell,其语法更类似于算法语言(ALGOL)。

Bourne Shell的设计有两个目标:作为命令行解释器交互执行操作系统命令和脚本编程。

为了取代Thompson Shell,Bourne Shell有着巨大的优势:

  1. 在脚本中引入了控制流、循环和变量,提供了更强大的语言与操作系统交互

  2. 允许使用脚本作为筛选器,为处理信号提供集成支持,但缺乏自定义函数机制

  3. 纳入了一系列今天我们仍在使用的功能,包括命令替换、here-document等

Bourne Shell在前进的道路上迈出了至关重要的一步,同时也成了其他派生Shell的基石。Bourne Shell的出现推动了Korn shell (ksh)、Almquist Shell(ash)和流行的Bourne Again Shell(Bash)的开发,在Bourne Shell发布之际,C Shell也已在开发之中。下图显示了Shell之间的血缘关系。

enter image description here
Shell之间的传承

Shell的基础架构

Linux Shell的基础架构并不复杂,如下图所示。

enter image description here
Linux Shell基础架构示意图

Linux Shell的架构看起来非常类似于一条管道,对输入进行语法解析、符号扩展,最后执行。

探索Linux Shell

接下来,让我们来探讨几个Shell,看看它们为Linux Shell的发展做出了哪些贡献。

C Shell

1978年,Bill Joy还在加州大学伯克利分校读书的时候,就为BSD UNIX(Berkeley Software Distribution UNIX)开发了C Shell。

五年之后,C Shell引入了Tenex系统中的一些功能,如命令行编辑功能和文件名和命令自动补全功能。Tenex C shell(tcsh)保持了对csh的向后兼容,并且提升了整体的交互性,它是Ken Greer在卡内基-梅隆大学开发出来的。

C Shell的一个主要设计目标就是创建一种语法类似于C语言的脚本语言,因为C语言在当时非常流行,而且UNIX是用C来开发的。

Bill Joy在C Shell中引入的另一个重要特性是命令历史记录。此功能可以保留之前执行的命令的历史记录,并允许用户查看并选择执行之前的命令。

例如,键入history命令可以查看之前执行的命令,使用上下箭头键可以选择一个命令,使用!!可以执行前一个命令。引用前一个命令的参数也是可以的,比如,!*可以引用前一个命令的所有参数,!$可以引用前一个命令的最后一个参数。

请参看下面的一个Tcsh脚本示例,这个脚本使用了一个参数(目录名称),统计该目录下所有可执行文件的数目。

代码1:在tcsh中统计可执行文件的数目

#!/bin/tcsh
# find all executables

set count=0

# Test arguments
if ($#argv != 1) then
  echo "Usage is $0 <dir>"
  exit 1
endif

# Ensure argument is a directory
if (! -d  $1) then
  echo "$1 is not a directory."
  exit 1
endif

# Iterate the directory, emit executable files
foreach filename ($1/*)
  if (-x $filename) then
    echo $filename
    @ count = $count + 1
  endif
end

echo
echo "$count executable files found."

exit 0

Korn Shell

Korn Shell是由David Korn设计的,差不多跟Tenex C Shell同时发布。除了向后兼容Bourne Shell,Korn Shell最引人瞩目的特性就是支持脚本编程。

Korn Shell原来是专有软件,直到2000年,它才(遵照通用公共许可协议)作为开源软件发布。除了对Bourne提供很好的兼容性,Korn Shell还包含了一些来自其他Shell的功能,如csh的历史记录功能。它还提供了一些更先进的功能,这些功能可以在诸如Ruby和Python之类的现代脚本语言中找到——例如,关联数组和浮点运算。

Korn Shell在许多操作系统上都是可用的,这些系统包括IBM AIX and HP-UX。Korn Shell努力支持Portable Operating System Interface for UNIX(POSIX)Shell语言的标准。

我们来看一下在Korn Shell中统计可执行文件数目脚本的例子:

代码2:在Korn Shell中统计可执行文件的数目

#!/usr/bin/ksh
# find all executables

count=0

# Test arguments
if [ $# -ne 1 ] ; then
  echo "Usage is $0 <dir>"
  exit 1
fi

# Ensure argument is a directory
if [ ! -d  "$1" ] ; then
  echo "$1 is not a directory."
  exit 1
fi

# Iterate the directory, emit executable files
for filename in "$1"/*
do
  if [ -x "$filename" ] ; then
    echo $filename
    count=$((count+1))
  fi
done

echo
echo "$count executable files found."

exit 0

Bourne-Again Shell

Bourne-Again Shell,即bash,是一个开源的GNU项目,旨在替换Bourne Shell。Bourne-Again Shell由Brian Fox开发,现在已经成为最流行的Shell之一,被广泛应用在Linux、Darwin、Windows和Cygwin之上。

除了支持脚本的向后兼容性,bash还吸收了Korn Shell和C Shell的一些特性。例如,命令历史记录,命令行编辑,目录堆栈,很多实用的环境变量,命令行自动完成,等等。

Bash继续发展了一些新的特性,如支持正则表达式和关联数组。

虽然一些特性是bash独有的,但我们仍然可以编写与其他脚本语言相兼容的脚本。下面是在bash中统计可执行文件数目的脚本示例。

代码3:在Bourne-Again Shell中统计可执行文件的数目

#!/bin/bash
# find all executables

count=0

# Test arguments
if [ $# -ne 1 ] ; then
  echo "Usage is $0 <dir>"
  exit 1
fi

# Ensure argument is a directory
if [ ! -d  "$1" ] ; then
  echo "$1 is not a directory."
  exit 1
fi

# Iterate the directory, emit executable files
for filename in "$1"/*
do
  if [ -x "$filename" ] ; then
    echo $filename
    count=$((count+1))
  fi
done

echo
echo "$count executable files found."

exit 0

这些Shell之间的一个关键区别是它们使用了不同的授权。Bash是一个GNU项目,遵循GPL授权,而C Shell则遵循了BSD许可,Korn Shell则遵循了通用公共许可证。

早期Shell的许多理念和接口在35年之后依然保持不变,这对其作者是一个巨大的肯定。任何一个行业都在不断重塑自我,Shell也在发生着历史的变迁。尽管许多新的Shell被开发出来,但Bourne Shell及其后继者依然是现在的首选。

关于作者

enter image description here
M.Tim Jones
M. Tim Jones 是一名嵌入式软件工程师,他是 Artificial Intelligence: A Systems Approach, GNU/Linux Application Programming(现在已经是第 2 版)、AI Application Programming(第 2 版)和 BSD Sockets Programming from a Multilanguage Perspective 等书的作者。他的工程背景非常广泛,从同步宇宙飞船的内核开发到嵌入式系统架构设计,再到网络协议的开发。Tim 是位于科罗拉多州 Longmont 的 Emulex Corp. 的一名顾问工程师。

译者:张志平
译文来源csdn链接:Linux Shell进化简史
原文链接:IBM developerworks

推荐阅读

enter image description here enter image description here