热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

从Bash和Korn到Cshell:评估Linux中的shell

Shell就像编辑器一样:每个人都有自己喜欢的选择并极力为该选择辩护(还告诉您为什么应该使用该选择)。确实如此,s

  Shell 就像编辑器一样:每个人都有自己喜欢的选择并极力为该选择辩护(还告诉您为什么应该使用该选择)。确实如此,shell 可提供不同的功能,但它们都实现了数十年前开发的核心理念。

  我第一次使用现代 shell 是在二十世纪 80 年代,当时我正在 SunOS 上开发软件。当我了解了将一个程序的输出用作另一个程序的输入(甚至多次连环地使用)的能力后,我就有了一种简单且高效的方式来创建过滤器和转换。该核心理念提供了一种方式来构建一些简单工具,这些工具足够灵活,可与其他工具组合使用。通过这种方式,shell 不仅提供了一种与内核和设备交互的方式,还提供了现在作为软件开发中的常见设计模式的集成服务(比如管道和过滤器)。

  让我们首先简单介绍一下现代 shell 的发展历史,然后探讨如今一些可用于 Linux 的外来的有用 shell。

  shell 的发展历史

  Shell(或命令行解释器)具有很长的历史了,但我们的讨论从第一个 UNIX® shell 开始。(贝尔实验室的)Ken Thompson 于 1971 年开发了第一个用于 UNIX 的 shell,名为 V6 shell。类似于它在 Multics 中的前身,这个 shell (/bin/sh) 是一个在内核外部执行的独立用户程序。globbing(参数扩展的模式匹配,比如 *.txt)等概念是在一个名为 glob的独立实用程序中实现的,就像用于评估条件表达式的 if 命令一样。这种独立性可保持 shell 很小,只需不到 900 行 C 源代码(请参见 参考资料 获取原始源代码的链接)。

  该 shell 为重定向(<> 和 >>)和管道(| 或 ^)引入了一种紧凑的语法,这种语法已延续到现代 shell 中。您也可以找到对调用顺序命令(使用 ;)和异步命令(使用 &)的支持。

  Thompson shell 缺少的是编写脚本的能力。它的唯一用途就是用作一个交互式 shell(命令解释器)来调用命令和查看结果。

  1977 年以来的 UNIX shell

  撇开 Thompson shell,我们开始将目光转移到 1977 年引入 Bourne shell 时的现代 shell。Bourne shell 由 Stephen Bourne 在 AT&T Bell Labs 为 V7 UNIX 创建,它在如今仍然是一个有用的 shell(在一些情况下还被用作默认的根 shell)。作者在研究 ALGOL68 编译器之后开发了 Bourne shell,所以您会发现它的语法比其他 shell 更加类似于 Algorithmic Language (ALGOL)。尽管使用 C 开发,源代码本身甚至使用了宏赋予它一种 ALGOL68 特色。

  Bourne shell 有两个主要目标:用作一个命令解释器来交互式执行操作系统命令,以及用于编写脚本(编写可通过 shell 调用的可重用脚本)。 除了取代 Thompson shell,Bourne shell 还提供了相对于其前身的多项优势。Bourne 向脚本中引入了控制流、循环和变量,提供了一种更加强大的语言来与操作系统交互(包括交互式和非交互式)。该 shell 还允许您使用 shell 脚本作为过滤器,提供对处理信号的集成支持,但缺乏定义函数的能力。 最后,它整合了我们如今使用的许多功能,包括命令替换(使用反引号)和用于将保留的字符串文字嵌入到脚本中的 HERE 文档。

  Bourne shell 不仅是向前发展的重要一步,也是众多衍生的 shell 的基础,其中许多 shell 如今应用在典型的 Linux 系统中。图 1 演示了重要 shell 的系列。Bourne shell 导致了 Korn shell (ksh)、Almquist shell (ash) 和流行的 Bourne Again Shell(或 Bash)的开发。在 Bourne shell 发布时,C shell (csh) 正在开发。图 1 显示了主要系列,但没有展示所有影响,也没有展示一些具有重要贡献的 shell。

  图 1. 1977 年以来的 Linux shell

  

1977 年以来的 Linux shell 的 “家族树” 图

 

  我们稍后将分析其中一些 shell,查看为它们的进步做出贡献的语言和功能示例。

  基本 shell 架构

  一种假想的 shell 的基本架构很简单(Bourne 的 shell 就是一个证据)。在图 2 中可以看到,基本架构看起来类似一个管道,其中会分析和解析输入,展开符号(使用各种方法,比如括号、波浪号、变量、参数扩展和替换,以及文件名生成),最终执行命令(使用 shell 内置的命令或外部命令)。

  图 2. 假想 shell 的简单架构

  

假想 shell 的简单架构图,包括内核

 

  在 参考资料 部分中,您可以找到一些链接来了解开源 Bash shell 的架构。

  探索 Linux shell

  现在让我们看看其中一些 shell,回顾它们所做的贡献并在每个 shell 中查看示例脚本。查看的内容包括 C shell、Korn shell 和 Bash。

  Tenex C shell

  C shell 是 Bill Joy 1978 年在加州大学伯克利分校攻读研究生期间为 Berkeley SoftwareDistribution (BSD) UNIX 系统开发的。5 年后,该 shell 引入了来自 Tenex 系统(在 DEC PDP 系统上很流行)的功能。Tenex 引入了文件名和命令完成功能,以及命令行编辑功能。Tenex C shell (tcsh) 仍然向后兼容 csh,但改进了它的整体交互式功能。tcsh 是 Ken Greer 在 Carnegie Mellon University 开发的。

  该 C shell 的一个重要的设计目标是创建一种类似 C 语言的脚本语言。这是一个有用的目标,因为 C 语言是所使用的主要语言(此外,该操作系统也是主要使用 C 语言开发的)。

  Bill Joy 在 C shell 中引用的一项有用功能是命令历史。此功能维护以前执行的命令的历史,允许用户检查并轻松选择之前的命令来执行。例如,键入命令 history 将显示以前执行的命令。可使用向上和向下箭头来选择命令,或者可以使用 !! 执行前一个命令。也可以引用以前的命令的参数,例如 !* 引用前一个命令的所有参数,其中 !$ 引用前一个命令的最后一个参数。

  看一下 tcsh 脚本的一个简单示例(清单 1)。这段脚本获取一个参数(一个目录名称),输出该目录中的所有可执行文件以及找到的文件数量。我将在每个示例中重用此脚本设计来演示区别。

  tcsh 脚本可分解为 3 个基本部分。首先,请注意,我使用了 shebang 或 hashbang 符号来将此文件声明为可由定义的 shell 可执行文件(在本例中为 tcsh 二进制文件)解释。这允许我以常规可执行文件的形式执行该文件,而不在它之前添加解释器二进制文件。它维护找到的可执行文件数量,所以我将此数量初始化为 0。

  清单 1. 用 tcsh 编写的查找所有可执行文件的脚本

 

  #!/bin/tcsh

  # find all executables

  set count=0

  # Test arguments

  if ($#argv != 1) then

  echo "Usage is $0

"

 

  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

 

  第一部分测试用户传递的参数。#argv 变量表示传入的参数数量(不包括命令名称本身)。您可指定这些参数的索引来访问它们:例如,#1 表示第一个参数(它是 argv[1] 的简写)。该脚本需要一个参数;如果它未找到该参数,则输出一条错误消息,使用 $0 表示在控制台输入的命令名称(argv[0])。

  第二部分确保传入的参数是一个目录。如果该参数是一个目录,-d 操作符返回 True。但请注意,我首先指定了一个 ! 符号,这表示无效。这样,表达式可表明,如果参数不是一个目录,则输出一条错误消息。

  最后一部分迭代目录中的文件,以测试它们是否可执行文件。我使用方便的 foreach 迭代器,它循环括号(在本例中为该目录)中的每一项,然后在循环中测试每一项。这一步使用 -x 操作符测试文件是否为可执行文件,如果是,则输出该文件并将数量加一。在脚本的末尾,我输出可执行文件的数量。

  Korn shell

  Korn shell (ksh) 由 David Korn 设计,是在与 Tenex C shell 相同的时期引入的。Korn shell 的一项最有趣的功能是,它除了向后兼容原始的 Bourne shell,还可用作脚本语言。

  Korn shell 在 2000 年以开源形式发布(依据 Common Public License)以前一直是专用的软件。除了提供与 Bourne shell 强大的向后兼容性,Korn shell 还包含其他 shell 的功能(比如 csh 的历史功能)。该 shell 还提供了可在现代脚本语言(比如 Ruby 和 Python)中找到的一些更加高级的功能 — 例如,关联数组和浮点算法。Korn shell 可用于多种操作系统,包括 IBM® AIX® 和 HP-UX,致力于支持 Portable Operating System Interface for UNIX (POSIX) shell 语言标准。

  Korn shell 是 Bourne shell 的衍生物,因此看上去更像 Bourne shell 和 Bash 而不是 C shell。让我们看一个查找可执行文件的 Korn shell 示例(清单 2)。

  清单 2. 用 ksh 编写的查找所有可执行文件的脚本

 

  #!/usr/bin/ksh

  # find all executables

  count=0

  # Test arguments

  if [ $# -ne 1 ] ; then

  echo "Usage is $0

"

 

  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

 

  在清单 2 中,您将注意到的第一点是它与 清单 1 的相似性。在结构上,该脚本基本上是相同的,但在执行条件、表达式和迭代的方式上存在明显的区别。没有采用类似 C 的测试操作符,ksh 采用了典型的 Bourne 风格操作符(-eq、-ne 和 -lt 等)。

  Korn shell 也有用一些与迭代相关的区别。在 Korn shell 中,使用了 for in 结构,使用命令替换来表示从命令 ls &#39;$1/*(表示指定子目录的内容)的标准输出创建的文件列表。

  除了上面定义的其他功能,Korn 还支持别名功能(用于将一个词替换为用户定义的字符串)。Korn 还有其他许多功能默认已禁用(比如文件名称完成),但可由用户启用。

  Bourne-Again Shell

  Bourne-Again Shell(或 Bash)是一个开源 GNU 项目,旨在取代 Bourne shell。Bash 由 Brian Fox 开发,已成为世上最流行的 shell 之一(出现在 Linux、Darwin、Windows®、Cygwin、Novell、Haiku 等系统中)。顾名思义,Bash 是 Bourne shell 的一个超集,大部分 Bourne 脚本都可原封不动地执行。

  除了支持脚本的向后兼容性,Bash 还整合了来自 Korn 和 C shell 的功能。您将找到命令历史、命令行编辑、一个目录栈(pushd 和popd)、许多有用的环境变量和命令完成等。

  Bash 继续在发展,拥有许多新功能,支持正则表达式(类似于 Perl)和关联数组。尽管其中一些功能可能在其他脚本语言中不存在,但可以编写兼容其他语言的脚本。对于这一点,清单 3 中所示的示例脚本等同于 Korn shell 脚本(来自 清单 2),除了 shebang 区别 (/bin/bash)。

  清单 3. 用 Bash 编写的查找所有可执行文件的脚本

  #!/bin/bash

  # find all executables

  count=0

  # Test arguments

  if [ $# -ne 1 ] ; then

  echo "Usage is $0

"

 

  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 发布的,而 csh、tcsh、zsh、ash 和 scsh 都是依据 BSD 或一种类似 BSD 的许可来发布的。Korn shell 可依据 Common Public License 使用。

  外来的 shell

  对于大胆的开发人员,可基于您的需要或爱好使用替代的 shell。Scheme shell (scsh) 提供了一种使用 Scheme(Lisp 语言的一种衍生物)的脚本环境。Pyshell 是对创建使用 Python 语言的类似脚本的一次尝试。最后,对于嵌入式系统,可以使用 BusyBox,它将一个 shell 和所有命令合并到一个二进制文件中,以简化其分发和管理。

  清单 4 给出了以 Scheme shell (scsh) 编写的查找所有可执行文件的脚本。这段脚本可能看起来很奇怪,但它实现了与我们目前所提供的脚本类似的功能。这段脚本包含 3 个函数,直接使用可执行代码(在末尾)来测试参数数量。这段脚本真正强大之处在showfiles 函数内,它迭代一个列表(在 with-cwd 后构造),在列表的每个元素后调用 write-ln。这个列表通过迭代指定的目录并在其中过滤是可执行文件的文件来生成。

  清单 4. 用 scsh 编写的查找所有可执行文件的脚本

 

  #!/usr/bin/scsh -s

  !#

  (define argc

  (length command-line-arguments))

  (define (write-ln x)

  (display x) (newline))

  (define (showfiles dir)

  (for-each write-ln

  (with-cwd dir

  (filter file-executable? (directory-files "." #t)))))

  (if (not (= argc 1))

  (write-ln "Usage is fae.scsh dir")

  (showfiles (argv 1)))

  结束语

  早期 shell 的许多理念和大量接口在之后的 35 年几乎未变 — 这是对早期 shell 的原始作者的贡献的有力证明。在一个不断自我改造的行业中,shell 已大大改进,但没有发生重大变化。尽管存在过创建特殊 shell 的尝试,但 Bourne shell 的衍生物仍然是所使用的主要 shell。


推荐阅读
  • SWIG 3.0.12 Windows官方版下载:实现C语言与PHP、Java、Python等多语言代码互调接口
    SWIG 3.0.12 Windows官方版是一款强大的接口生成工具,能够实现C语言与多种高级编程语言(如Java、C#)及脚本语言(如PHP、JavaScript、Python)之间的互操作性。它不仅支持跨语言调用,还提供了丰富的封装选项,确保了代码的高效性和可维护性。 ... [详细]
  • 利用Python与Android进行高效移动应用开发
    通过结合Python和Android,可以实现高效的移动应用开发。首先,需要安装Scripting Layer for Android (SL4A),这是一个开源项目,旨在为Android系统提供脚本语言支持。SL4A不仅简化了开发流程,还允许开发者使用Python等高级语言编写脚本,从而提高开发效率和代码可维护性。此外,SL4A还支持多种其他脚本语言,进一步扩展了其应用范围。通过这种方式,开发者可以快速构建功能丰富的移动应用,同时保持较高的灵活性和可扩展性。 ... [详细]
  • 字节跳动深圳研发中心安全业务团队正在火热招募人才! ... [详细]
  • 在Ubuntu系统中配置Python环境变量是确保项目顺利运行的关键步骤。本文介绍了如何将Windows上的Django项目迁移到Ubuntu,并解决因虚拟环境导致的模块缺失问题。通过详细的操作指南,帮助读者正确配置虚拟环境,确保所有第三方库都能被正确识别和使用。此外,还提供了一些实用的技巧,如如何检查环境变量配置是否正确,以及如何在多个虚拟环境之间切换。 ... [详细]
  • 利用树莓派畅享落网电台音乐体验
    最近重新拾起了闲置已久的树莓派,这台小巧的开发板已经沉寂了半年多。上个月闲暇时间较多,我决定将其重新启用。恰逢落网电台进行了改版,回忆起之前在树莓派论坛上看到有人用它来播放豆瓣音乐,便萌生了同样的想法。通过一番调试,终于实现了在树莓派上流畅播放落网电台音乐的功能,带来了全新的音乐享受体验。 ... [详细]
  • 技术日志:Ansible的安装及模块管理详解 ... [详细]
  • Python作为当今IT领域中最受欢迎且高效的语言之一,其框架能够显著加速Web应用程序的开发过程。本文推荐并对比了十大顶级Python Web开发框架,其中CubicWeb以其卓越的代码重用性和模块化设计脱颖而出,为开发者提供了强大的支持。 ... [详细]
  • 当前,众多初创企业对全栈工程师的需求日益增长,但市场中却存在大量所谓的“伪全栈工程师”,尤其是那些仅掌握了Node.js技能的前端开发人员。本文旨在深入探讨全栈工程师在现代技术生态中的真实角色与价值,澄清对这一角色的误解,并强调真正的全栈工程师应具备全面的技术栈和综合解决问题的能力。 ... [详细]
  • 深入解析:Android开发进阶之Vim编辑器基础操作与应用
    本文深入探讨了Android开发中使用Vim编辑器的基础操作与应用。通过详细解析Vim的基本命令、配置文件和常用插件,帮助开发者提高代码编写效率。文章还介绍了如何在Android开发环境中高效利用Vim,包括集成开发环境(IDE)的配置和常见问题的解决方法。适合初学者和有经验的开发者参考。 ... [详细]
  • Linux学习精华:程序管理、终端种类与命令帮助获取方法综述 ... [详细]
  • 为何Serverless将成为未来十年的主导技术领域?
    为何Serverless将成为未来十年的主导技术领域? ... [详细]
  • Spring Boot与Redis的高效集成方案
    本文探讨了Spring Boot与Redis的高效集成方法,详细介绍了如何在Spring Boot项目中配置和使用Redis,以提升应用性能和数据处理能力。同时,文章还涉及了Go语言社区的相关资源,为Golang开发者提供了宝贵的技术交流平台。 ... [详细]
  • 在Linux/WSL环境中,本文对Shell任务的并行处理进行了详细的测试与分析。通过多种并行处理技术,如GNU Parallel和xargs,探讨了如何有效提升任务执行效率和系统资源利用率。实验结果表明,合理配置并行参数能够显著缩短任务完成时间,提高系统整体性能。此外,文章还介绍了Shell脚本编写的基本原则和最佳实践,为读者提供了实用的参考。 ... [详细]
  • 本文深入解析了 Python 爬虫技术在 B 站数据挖掘中的应用,通过分析海量用户行为和内容数据,揭示了热门 UP 主成功的背后因素。Python 作为一种强大的编程语言,其面向对象和解释执行的特点使其成为数据抓取和处理的理想选择。文章详细介绍了如何利用 Python 爬虫技术获取 B 站的数据,并通过数据分析方法,探讨了热门 UP 主的创作策略和互动模式,为内容创作者提供了有价值的参考。 ... [详细]
  • 如何有效解决MySQL中预编译语句失效的问题及专业应对策略 ... [详细]
author-avatar
mobiledu2502875803
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有