命令行界面已经使用了很久,平时也有很多常用的命令行工具,但是对一些相关的概念(终端、Shell、命令、脚本等)却一直有些似懂非懂,今天记录一下,一是梳理自己的思路,二也是希望能对读到这篇博客的人有所帮助。
- 命令行界面(CLI)和命令(行) —— 是一种页面交互风格,对比于GUI
- 终端(Terminal) —— 人与机器交互的接口,负责数据的输入与输出
- 现在的一些叫法名词(本小节可只看这一段)
- 历史:起源、字符终端(哑终端、智能终端)、图形终端
- 现代:终端模拟器(Terminal Emulator)
- 补充:控制台、TTY、终端服务器
- Shell —— Shell是一种程序,对OS内核提供的服务进行包装,给用户使用
- Shell与终端的分工 —— 终端负责数据输入输出,Shell负责数据的处理。
- 脚本Script —— 实现一些操作自动化,目前脚本语言也有多种
一、命令行界面(CLI)和命令(行)
1.1 CLI、GUI是什么?
是与用户交互的界面的两种风格
- 命令行界面CLI(Command-Line Interface) 是在GUI得到普及之前使用最为广泛的用户界面,通常不支持鼠标,用户通过键盘输入文本指令,计算机接收到指令后,予以执行。也有人称之为字符用户界面。
- 图形用户界面GUI(Graphical User Interface) 采用图形方式显示(用户可以通过鼠标点击、键盘作为输入)。与早期计算机使用的命令行界面相比,图形界面对于用户来说在视觉上更易于接受,学习成本大幅下降,也让计算机的大众化得以实现。
Bash命令行界面图例:
因为,命令行界面的软件通常需要用户记忆操作的命令,所以操作起来不那么方便,但是,由于其本身的特点,命令行界面要较图形用户界面节约计算机系统的资源(GUI要保证界面渲染得易于接收)。在熟记命令的前提下,使用命令行界面往往要较使用图形用户界面的操作速度要快。
所以,现在的操作系统中,虽然很注重图形用户界面,提供了好看好用的GUI Shell,但却都没有因而停止提供文字模式的命令行操作方式(CLI Shell),相反的,许多系统反而更加强这部分的功能。
1.2 命令(行) 与 Linux命令
CLI程序区别于GUI程序的是:后者提供给用户的使用(输入)方式是点击、输入即可,输出的结果也是形象生成的图形,而CLI程序提供给用户的是一条条命令,用户通过键盘输入命令、参数来使用功能,结果是以一系列的字符形式输出
- 命令提示符
- 命令提示符是操作系统在CLI界面上向用户提供的一种提示标志。命令提示符有两重含义:一是标志着上一条命令的结束; 二是标志着可以启动运行下一条命令。
- 比如DOS中的
C>
、UNIX中的¥
或%
、Mac OS中的$
- 命令(行) — 终端上的输入
- 通常把在命令提示符后打入的程序名和其参数称为命令行(到回车为止)或命令。
- 任一命令行(即任一条命令)的本质都是 申请某一程序执行。
- 交互式命令:与用户进行交互。比如shell等待你的输入,并且执行你提交的命令
- 非交互式命令:不存在进行交互,而是读取存放在文件中的命令,并且执行它们。当它读到文件的结尾,命令也就终止了。
- 注意:区分一个概念 Linux命令 — 一个个的可执行文件。
- OS提供了一组不同操作命令组成的集合,每个命令实现用户所要求的不同功能,为用户提供相应的服务。
- 每一条命令都对应着一个可执行文件。
- 这些命令通常是用C语言编写的,也有一些是用其他编程语言编写的。比如awk命令是awk语言编写。
- 另外自己也可以基于内核提供的一些系统调用、编程语言的标准库、已安装的命令行工具等来开发一些命令行工具。
- 系统自带、自安装的这些命令工具,可以在其他语言中通过指定方式直接调用。
- 对于一些开发人员来说,经常会使用自己熟悉的编程语言,常见的比如bash、zsh shell语言、AWK、Perl、Python、Nodejs等,来编写自己的命令或工具,以执行特定的任务,扩展Linux系统的功能。
- OS提供了一组不同操作命令组成的集合,每个命令实现用户所要求的不同功能,为用户提供相应的服务。
- 我们在终端中,输入的就是一行行bash或zsh等shell编程语言代码,也称命令(行),经过/bin/zsh、/bin/bash等Shell程序的解释,进而成功调用一个个的Linux命令工具(可执行程序)。
linux/cmd命令语法规则、语法格式:
1 | command [options] [arguments] |
命令行的执行过程:
1.3 常见的CLI Shell程序
- CLI Shell
- bash/sh/ksh/csh/zsh(Unix-like系统)
- COMMAND.COM(MS-DOS系统)
- cmd.exe/命令提示符(Windows NT和Windows CE系统)
- Windows PowerShell(支持.NET Framework技术的Windows NT系统)
- 此外,还有一些个人开发的、很好用的CLI工具
二、Shell
指操作系统中为用户提供访问内核所提供之服务的程序,因此与之相对的是内核(Kernel),内核管理着整台计算机的硬件(硬盘、显卡、网卡、CPU等),是现代操作系统中最基本的部分。但是,内核处于系统的底层,是不能让普通用户随意操作,所以内核不提供和用户的交互功能。
2.1 Shell 分类
- CLI Shell(命令行式的用户交互界面),CLI Shell出现的很早,是传统意义上的shell,如果不特别注明,shell一般都是指 CLI Shell。
- GUI Shell(图形化的用户交互界面),GUI通常会建构在视窗系统上。(视窗系统是以使用视窗作为主要特征之一的图形用户接口的构成组件。更为明确地说,它是桌面环境的构成组件。)
不管是GUI shell 还是CLI Shell,其实都是在解释命令,只不过GUI shell隐藏解释命令细节,用户通过点击鼠标的操作操作内核命令。所以Shell本质上就是个命令解释器。
2.2 CLI Shell的发展
不同的GUI Shell提供给用户不同的界面风格、操作方式,不同的 CLI Shell也会提供给用户不同风格的命令语法:除非我们需要编写shell脚本,普通用户一般很难发现不同shell脚本语言差异。对文件操作ls、pwd等等这些常用命令所有shell都一样支持。
Bash,Zsh和其他Linux Shell之间有什么区别?
Thompson Shell
:是第一个shell环境,在贝尔实验室开发并于1971年发布。Shell环境一直在基于这个概念,添加了各种新功能,功能和速度的改进C shell(csh)
:发布于1978年,它添加了许多交互式元素,用户可以控制他们的系统,如别名(长命令的快捷方式),作业管理能力,命令历史等。Bourne Shell(sh)
:被称作现代Shell最突出的祖先,发布于1979年,它成为Unix中的默认命令解释器,因为它支持命令替换,管道,变量,条件测试和循环,以及其他功能。- 它没有为用户提供太多的定制,并且不支持像别名,命令完成和shell函数这样的现代化的细节。
tcsh
、ksh
:随着时间的推移,很多人修复了bug,并向C shell添加了功能,最终导致了csh的改进版本,称为“tcsh
”。 但是csh在基于Unix的计算机上仍然是默认的,并且添加了一些非标准的功能。- 贝尔实验室的David Korn致力于
KornShell
或“ksh
”,它试图通过向后兼容Bourne shell的语言来改进这种情况,但增加了csh shell的许多功能。 - 它在1983年发布,但是根据专有许可。 它不是自由软件,直到2000年代,当它发布了各种开源许可证。
- 贝尔实验室的David Korn致力于
Bash
:是Bourne shell的后继兼容版本与开放源代码版本,于1989年发布第一个正式版本,原先是计划用在GNU操作系统上,但能运行于大多数[类Unix系统的操作系统之上,包括Linux与Mac OS X v10.4都将它作为默认shell。ash
dash
:设计是符合POSIX的和轻量级的,所以它比Bash快,但不会有所有的功能Z shell(zsh)
:目前非常流行,1990年创建的,它是一个Bourne风格的shell。- 这个较新的shell与bash兼容,但包括更多的功能。
- zsh shell提供内置的拼写校正,改进的命令行完成,充当shell插件的可加载模块,允许您在命令行上别名文件名或其他任何东西的全局别名,而不仅仅是命令,以及更多主题支持。 它像bash,但有很多附加功能,附加功能和可配置选项,你可能会喜欢,如果你在命令行上花费大量的时间。
- 如果你熟悉bash,你可以切换到zsh,而不学习不同的语法,你只会获得额外的功能。
fish
:更新的一个shell,2005年发布,它有一个独特的命令行语法,旨在更容易学习,但不是从Bourne shell或C shell派生。
2.3 不同操作系统的Shell举例
Unix:第一个Unix壳层(Unix shell)是以Multics上的shell为范本所写出的Thompson shell
Windows:同时提供了图形壳层、命令行壳层的功能
- Windows 95/98下的command.com
- Windows NT内核下的cmd.exe以及PowerShell
- 图形界面壳层即为explorer.exe
Mac OS:
- macOS开源了内核,而GUI没有开源(否则,可能我们会看到各种“自主知识产权”的,长得像 OS X 的 Linux,以及其他各种不知所谓的 Mod)。
- mac的GUI 层又称 Aqua 层,基于 Cocoa,在系统的架构的最顶端,也是直接面向用户和大部分应用程序开发者的层面。
- mac内置的shell壳层有很多(对命令行的支持力度很大)。
Unix/类Unix(Linux、Mac OS等):
- GUI Shell:X window manager (BlackBox和FluxBox),以及功能更强大的CDE、GNOME、KDE、 XFCE
- CLI Shell:bash / sh / ksh / csh / zsh
2.4 Shell与Shell脚本
- CLI Shell — Shell脚本
- GUI Shell — GUI软件
这两对概念之间的关系是有区别:
- 前者中Shell脚本是利用CLI Shell提供的命令编写的脚本程序(只能调用Shell提供的命令),CLI Shell充当的是解释器的角色。
- 而GUI软件与GUI Shell、CUI Shell更类似于同级的关系,都是基于系统内核提供的一些API进行开发的。
三、终端(Terminal)
终端(Terminal) — 人与机器交互的接口。
终端 (Terminal),词汇本身的意义为「终点站;末端;(电路)的端子,线接头」。而在计算机领域,终端则是一种用来让用户输入数据至计算机,以及显示其计算结果的机器。
也就是说,终端只是一种用于与计算机进行交互的输入输出设备,其本身并不提供运算处理功能。在计算资源紧张的时代,一台计算机只有一个控制台,人们想共享一台计算机,可以通过终端连接到计算机上,将指令输入终端,终端传送给计算机,计算机完成指令后,将输出传送给终端,终端将结果显示给用户。
3.1 现在的一些叫法名词
- 在存在真实的物理终端时,一台主机有一个控制台,多个终端(控制台是一个权限更高的特殊终端)
- 最早的终端设备是电传打字机(tty),后来将tty作为终端的统称,有时也指Linux的tty子系统
- 终端和控制台都不是目前个人电脑普遍时代的概念,而是多人共用的小型中型大型计算机上的概念。
- 我们经常所使用的终端软件,都是终端模拟器(真实的终端是硬件)
- 终端模拟器有两种:
- 运行在用户态(GUI下的一个软件)——又称终端窗口、伪终端
- 运行在内核态——又叫虚拟控制台
- 终端模拟器一般都是模拟的字符终端,在里面可以使用CLI程序
- CLI程序有很多:CLI Shell、Shell脚本。区别于GUI软件提供给用户的使用方式是点击、输入。CLI程序提供给用户的是一条条命令,用户通过输入命令、参数来使用功能
3.2 历史
3.2.1 早期的终端
早期,一台计算机配置多个终端,人们通过终端共享计算资源。
个人计算机是上世纪70年代末开始出现的。在那之前,是大型机 (Mainframe) 和小型机 (Minicomputer) 的时代,计算机非常昂贵且巨大(Unix创始人肯•汤普逊和丹尼斯•里奇使用的PDP-7小型机当年的价格为72000美元,GE-45大型机价格高达1000万美元),不像现在这样人手一台。这些笨重的计算机通常被安置在单独的房间内,而操作计算机的人们坐在另外的房间里,通过某些设备与计算机进行交互。这种设备就叫做终端 (Terminal),也叫终端机。
肯•汤普逊和丹尼斯•里奇想让Unix成为一个多用户系统。多用户系统意味着要给每个用户配置一个终端,每个用户都要有一个显示器、一个键盘。但当时所有的计算机设备(包括显示器)价格都非常昂贵,而且键盘和主机是集成在一起的,根本没有独立的键盘。
最后他们找到了一样东西,那就是ASR33电传打字机(Teletype)。ASR是英文Automatic Send-Receive的首字母缩写。虽然电传打字机的用途是在电报线路上收发电报,但是它也可以作为人与计算机的接口,而且价格低廉。ASR33打字机的键盘用来输入信息,打印纸用来输出信息。如图:
以他们把ASR33电传打字机作为终端,很多个ASR33连接到同一个主机,每个用户都可以在终端输入用户名和密码登录主机。这样他们创造了计算机历史上的第一个真正的多用户系统Unix,而ASR33成为第一个Unix终端。
3.2.2 字符终端(哑终端、智能终端)
随着终端的发展,也可以分为不同的种类。
字符终端(Character Terminal) 也叫文本终端(Text Terminal),是只能接收和显示文本信息的终端。早期的终端全部是字符终端。
字符终端也分为哑终端(Dumb Terminal) 和所谓的智能终端(Intelligent Terminal),因为后者可以理解转义序列、定位光标和显示位置,比较聪明,而哑终端不行。
DEC 公司在 1978 年制造的 VT100(DEC VT100 终端,上图),由于其设计良好并且是第一批支持 ANSI 转义序列与光标控制的智能终端,获得了空前的成功。VT100 不仅是史上最流行的字符终端,更是成为了字符终端事实上的标准。
3.2.3 图形终端
随着技术的进步,图形终端 (Graphical Terminal) 也开始出现在公众的视野中。图形终端不但可以接收和显示文本信息,也可以显示图形与图像。著名的图形终端有 Tektronix 4010 系列。
不过现在专门的图形终端已经极为少见,他们基本上已经被全功能显示器所取代。
3.3 现代: 终端模拟器
3.3.1 作用
随着图形化界面GUI的兴起,时至今日,图形终端已经完全被全功能视频显示器代替。
随着个人计算机的进化:
- 终端也发生了巨大的变化:从电传打字机(tty)、哑终端、智能终端、图形化终端到现代。现代的终端:
- 输入设备包括键盘、鼠标、麦克风等
- 输出设备包括显示器、扬声器等
- 计算机系统的使用方式也变了:
- GUI之前,操作系统提供命令,用户通过调用命令完成功能
- GUI后,操作系统的中心转到了图形化界面,用户可以通过鼠标点击来完成很多功能
前面提到过,GUI软件好用简单,但CLI工具也有其独特的优点,所以很有保留的必要。在现在大部分操作系统默认展示GUI Shell的情况下,CLI工具并不能使用,因为CLI工具通常不支持鼠标,而且不能将输出直接展示在图形化界面上。
为了能使用那些命令行工具,这时候我们就需要一个程序来模拟传统终端的行为,即终端模拟器 (Terminal Emulator)。(模拟智能终端即可)
3.3.2 工作流程
一个终端模拟器的标准工作流程是这样的:
- 捕获你的键盘输入;
- 将输入发送给命令行程序(程序会认为这是从一个真正的终端设备输入的);
- 拿到命令行程序的输出结果(STDOUT 以及 STDERR);
- 调用图形接口(比如 X11),将输出结果渲染至显示器。
3.3.3 常见的终端模拟器
终端模拟器有很多,这里就举几个经典的例子:
- GNU/Linux:gnome-terminal、Konsole;
- macOS:Terminal.app、iTerm2;
- Windows:Win32 控制台、ConEmu 等。
终端模拟程序可以模拟任何终端,包括真实终端和虚拟(模拟)终端。但实际上只有四种终端被作为模拟的对象:
- 最常见的两个模拟对象:
- DEC公司1978年制造的VT100终端
- xterm终端模拟程序(xterm模拟的是DEC公司制造的VT102终端)
- 另外两个不太常见的模拟对象是:
- DEC公司生产的VT220终端
- 用于IBM大型机的3270终端
3.3.4 两种形式
终端模拟器的两种形式:终端窗口、虚拟控制台
- 运行在GUI下(用户态)的终端——又叫伪终端、终端窗口(Terminal Window)。服务依赖于GUI。
- 比如Mac中的Terminal.app、iTerm2,Windows中的cmd等
- 如果图形化页面一旦宕掉,CLI也不能用了。
- 运行在内核态的终端——又叫虚拟控制台(Virtual Console)。服务是由操作系统内核直接提供的。
- 比如在 GNU/Linux 操作系统中,按下
Ctrl+Alt+F1,F2...F6
等组合键可以切换出好六个黑色背景的全屏字符终端界面,而按下Ctrl+Alt+F7
才是切换回图形界面 - 如果图形化页面宕掉,还可以切换到 Virtual Console 去救火,因为它们由内核直接提供,只要系统本身不出问题一般都可用
- 比如在 GNU/Linux 操作系统中,按下
下图就是一个正在显示系统启动信息的虚拟控制台:
因为终端窗口是跑在图形界面上的,所有如果图形界面宕掉了那它们也就跟着卡死了。当图形终端崩溃时,我们可以按快捷键切换到这六个 Virtual Console的其中一个,然后输入命令修复问题或重启系统,因为它们由内核直接提供,只要系统本身不出问题一般都可用。
3.4 补充: 控制台、TTY、终端服务器
3.4.1 控制台(Console)
控制台 —— 一个特殊的能直接显示系统消息的终端
控制台与普通终端的区别:
- 结构上:
- 控制台是计算机的基本设备。控制台与主机是一体的,不需要连线。
- 终端是是计算机的附加设备。通常终端都是通过线路连接到主机。
- 一台计算机上可以连接很多个终端,但是一般只有一个控制台。两者在外表上没什么区别。
- 功能上:控制台是用于管理主机的,只能给系统管理员使用,有着比普通终端更大的权限。
- 比如计算机操作系统中,与终端不相关的消息,比如BIOS的输出、内核的输出、后台服务的消息等,只会显示在控制台上,而不会显示到终端上。
- 简而言之:能直接显示系统消息的那个终端称为控制台,其他的则称为终端。
下图左侧立式的是Console,桌子上的是Terminal:
随着个人计算机的普及,控制台 (Console) 与终端 (Terminal) 的概念已经逐渐模糊。(也有说法:终端和控制台都不是个人电脑的概念,而是多人共用的小型中型大型计算机上的概念。不必强行对应到个人计算机上。)
在现代,我们的键盘与显示器既可以认为是控制台,也可以认为是普通的终端。
- 当你在管理系统时,它们是Console;
- 当你在做一般的工作时(浏览网页、编辑office文档等),它们是终端。
- 自己既是一般用户,也是系统管理员。
因此,现在 Console 与 Terminal 基本被看作是同义词。
3.4.2 TTY
1. TTY是什么?
TTY:来源是电传打字机 (Teletype/Teletypewriter),因为最初的终端都是tty,通过线缆与计算机连接,并完成计算机的输入输出功能。
Unix 系统为了支持这些TTY设备,就设计了名为 tty 的子系统,将具体的硬件设备抽象为操作系统内部位于 /dev/tty*
的设备文件。所以 TTY 既指终端,也指 Linux 的 TTY 子系统。
随着计算机的发展,终端设备已经不再限制于电传打字机,取而代之的是键盘和显示器等设备,但是 tty 这个名称还是就这么留了下来。久而久之,它们的概念就混淆在了一起。所以在现代,TTY 设备泛指计算机的终端设备,终端设备统称为 TTY 设备,无需区分。
由于早期计算机上的串行端口 (Serial Port)最大的用途就是连接终端设备,所以当时的 Unix 会把串口上的设备也同样抽象为 tty 设备(位于 /dev/ttyS*
)。因此,现在人们也经常将串口设备称呼为 tty 设备。
2. TTY设备结构
从历史上看,终端刚开始就是终端机,配有打印机,键盘,带有一个串口,通过串口传送数据到主机端,然后主机处理完交给终端打印出来。
- UART 驱动:如上图所示,物理终端通过电缆连接到计算机上的 UART(通用异步接收器和发射器)。操作系统中有一个 UART 驱动程序用于管理字节的物理传输。
- 行规范:上图中内核中的 Line discipline(行规范)用来提供一个编辑缓冲区和一些基本的编辑命令(退格,清除单个单词,清除行,重新打印),主要用来支持用户在输入时的行为(比如输错了,需要退格)。
- TTY 驱动:TTY 驱动用来进行会话管理,并且处理各种终端设备。
UART 驱动、行规范和 TTY 驱动都位于内核中,它们的一端是终端设备,另一端是用户进程。因为在 Linux 下所有的设备都是文件,所以它们三个加在一起被称为 “TTY 设备”,即我们常说的 TTY。
3.4.3 终端服务器
当公司或大学的资金只能买一台大型机或小型机时,所有的终端都直接连到这台主机。随着时间的推移,主机价格逐渐下降,这时一个机构内部有多个主机。如何才能让每个终端都能连接到任何一个主机呢?在各个终端和主机之间牵一根线代价太大,而且线路容易搞混。这时候终端服务器就派上用场了。
各个终端与终端服务器相连,各个主机也与终端服务器相连。当终端启动时,终端服务器询问用户要登录哪个主机,用户指定主机后,再输入用户名和密码登录相应的主机。这种拓扑结构很像今天的家庭网络,终端服务器相当于路由器。
四、Shell与终端的分工
现在我们知道:
- 终端只负责信息的输入和输出,没有任何的运算处理功能。
- 从用户这里接收输入(键盘、鼠标等输入设备),扔给 Shell。
- 把 Shell 返回的结果展示给用户(比如通过显示器)
- 而 Shell 干的活儿是从终端那里拿到用户输入的命令,解析后交给操作系统内核去执行,并把执行结果返回给终端。
以Mac为例,可以在终端中:
cat /etc/shells
:查看所有的shell,打印如下:- /bin/bash、/bin/csh、/bin/ksh、/bin/sh、/bin/tcsh、/bin/zsh
echo $SHELL
:查看当前窗口使用的shell版本cat /etc/passwd | grep sh
:查看系统用户默认shellchsh -s /bin/zsh(bash)
:切换终端中使用的Shell
Shell有多种,终端也有多种,两者是搭配工作的关系,并无固定的对应、依赖配置。
系统默认会为终端设置一种Shell来解释输入的命令,当编写脚本时,应该指定脚本语言对应的解释器(Shell),脚本第一行决定了需要启动哪个shell来解释,写法比如#!/bin/bash
(#!后紧跟解释后面命令的shell的路径)
不过 Shell 与终端的分工有一些容易混淆的地方,这里以例子进行说明:
- 终端将用户的键盘输入转换为控制序列(除了字符以外的按键,比如
左方向键 → ^[[D
),Shell 则解析并执行收到的控制序列(比如^[[D → 将光标向左移动
);- 不过也有例外,比如终端在接收到
Ctrl + C
组合键时,不会把这个按键转发给当前的程序,而是会发送一个SIGINT
信号(默认情况下,这会导致进程终止)。 - 其他类似的特殊组合键有
Ctrl-Z
与Ctrl-\
等,可以通过stty -a
命令查看当前终端的设置。
- 不过也有例外,比如终端在接收到
- Shell 发出类似「把前景色改为红色(控制序列为
\033[31m
)」「显示 foo」等指令;终端接收这些指令,并且照着 Shell 说的做,于是你就看到了终端上输出了一行红色的 foo。 - 除非被重定向,否则 Shell 永远不会知道它所执行命令的输出结果。我们可以在终端窗口中上下翻页查看过去的输出内容,这完全是终端提供的 feature,与 Shell 没有半毛钱关系;
- 命令提示符 (Prompt) 是一个完全的 Shell 概念,与终端无关;
- 行编辑、输入历史与自动补全等功能是由 Shell 提供的(比如 fish 这个 Shell 就有着很好用的历史命令与命令自动补全功能)。不过终端也能自己实现这些功能,比如说 XShell 这个终端模拟器就可以在本地写完一行命令,然后整条发送给远程服务器中的 Shell(在连接状况不佳时很有用,不然打个字都要卡半天);
- 终端中的复制粘贴功能(
Shift + Insert
或者鼠标右键等)基本上都是由终端提供的。举个例子,Windows 默认的终端对于复制粘贴的支持很屎,而换一个终端(例如 ConEmu)后就可以很好地支持复制粘贴。不过 Shell 以及其他命令行程序也可以提供自己的复制粘贴机制(例如 vim)。
五、脚本Script
5.1 概述
早期,脚本语言经常被称为批处理语言或工作控制语言。百度百科中,也称为扩建的语言或动态语言。编写出的脚本也只是为了实现一些简单任务的自动化,比如使得本来要用键盘进行的相互式操作自动化。
比如:一个Shell脚本主要由原本需要在命令行输入的命令组成。在一个文本编辑器中,用户可以使用脚本来把一些常用的操作组合成一组序列。
用来书写这种脚本的语言叫做脚本语言。
5.2 脚本、程序、软件
脚本(Script)语义较抽象,可以借助脚本的另一个场景语义来理解:(脚本是戏剧、电影的发展大纲,用以确定故事的发展方向。与剧本不同,脚本并没有明确地指出演出者(包括动画、游戏人物等)究竟该说什么话,只是将人物需要做的任务安排下去。如:小明走在街上;碰到同事,打招呼。)
- 脚本:是批处理文件的延伸,是一种纯文本保存的
程序
,是确定的一系列控制计算机进行运算操作动作的组合(命令集合)- 这些命令通常会写入一个文件,这个文件就叫脚本文件,类似剧本,说明都做什么;
- 脚本文件明显小于如同类C程序文件
- 语法结构、学习使用通常比较简单
- 通常是解释执行而非编译
- 通常是以文本(TEXT)格式保存
- 程序:是为了完成特定的功能,解决特定的问题而用计算机语言编写的命令序列集合。
- 软件:软件工程领域对于软件的定义是程序以及文档的集合体
不用纠结脚本、解释型语言程序的区别,如果是个纯文本文件、解释执行、代码量不大,功能较简练,可以叫脚本,也可以叫程序。 脚本和传统编程语言程序之间的界限越来越模糊。
5.3 脚本语言及分类
5.3.1 发展
脚本语言是为了缩短传统的“编写、编译、链接、运行”过程而创建的计算机编程语言。早期,脚本语言经常被称为批处理语言或工作控制语言,实现一些简单任务的自动化。百度百科中,也称为扩建的语言或动态语言。
一个脚本通常是解释运行而非编译。脚本语言通常都有简单、易学、易用的特性,目的就是希望能让程序员快速完成程序的编写工作。宏语言则可视为脚本语言的分支,两者也有实质上的相同之处。在许多方面,高级编程语言和脚本语言之间互相交叉,二者之间没有明确的界限。
现在,虽然许多脚本语言都超越了计算机简单任务自动化的领域,可以脱离被扩建的程序单独存在(有自己的运行库、标准语言库函数等),成熟到可以自己编写精巧的程序,但仍然还是被称为脚本。
5.3.2 分类
脚本的使用有很多,时至今日,脚本语言也各种各样。有脚本,必然有与之对应的解释器,进而对应的用途也很广泛,根据解释器/用途,可以将脚本语言分为很多种:
- Shell语言 —— 此类脚本用于自动化工作控制,即启动和控制系统程序的行为,解释器为
CLI Shell
。- Shell与Shell Script:
Shell
是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务,同时也是一门语言,可以编写Shell脚本(script),使用Shell来解释执行。Shell与Shell Script是两个不同的概念,Shell编程是指开发后者,不是指开发Shell本身。 - Shell Script:又称Shell命令稿、程序化脚本,是一种电脑程序使用的文本文件,内容由一连串的shell命令组成。运作方式与解释型语言相当,由CLI Shell充当命令行解释器的角色,在读取shell脚本之后,依序运行其中的shell命令,之后输出结果。利用shell脚本可以进行系统管理,文件操作等。
- Shell与Shell Script:
- GUI脚本语言 —— GUI出现带来一种专业的控制计算机的脚本语言。它在用户和图形界面,菜单,按钮等之间互动。它经常用来自动化重复性动作,或设置一个标准状态。理论上它可以用来控制运行于基于GUI的计算机上的所有应用程序,但实际上这些语言是否被支持还要看应用程序和操作系统本身。当通过键盘进行互动时,这些语言也被称为宏语言。
- 应用程序定制的脚本语言 —— 许多大型的应用程序都包括根据用户需求而定制的惯用脚本语言。同样地,许多电脑游戏系统使用一种自定义脚本语言来表现NPC和游戏环境的预编程动作。
- 此类语言通常是为一个单独的应用程序所设计,虽然它们貌似一些通用语言(如QuakeC, modeled after C),但它们有自定义的功能。
- 通用动态语言 —— 一些语言,比如Perl、PHP、Python、Ruby、Lua、Smalltalk等,从一门脚本语言发展成了更通用的编程语言。由于“解释执行,内存管理,动态”等特性,它们仍被称为脚本语言。但它们已经用于应用程序编写,用户也不把它们看作脚本语言。
- 扩展/可嵌入语言 —— 少数的语言被设计通过嵌入应用程序来取代应用程序定制的脚本语言。开发者(如使用C等其它系统语言)包入使脚本语言可以控制应用程序的hook。
- 这些语言和应用程序定制的脚本语言是同种用途,但优点在于可以在应用程序之间传递一些技能。比如:JavaScript、Lua、Ch(C/C++ interpreter)、Dao、Tcl
- JavaScript直到现在仍然是网页浏览器内的主要编程语言,它的ECMAScript标准化保证了它成为流行的通用嵌入性语言。
- Tcl作为一种扩展性语言而创建,但更多地被用作通用性语言,就如同Python, Perl, Ruby一样。
脚本语言工作必须依赖对应的解释器,创造一门语言的同时,需要创建(使用已有的)对应的编译器、解释器。
六、Shell脚本编程
上面已经说过,不管是正流行的bash、zsh本质上都是解释器,它们都服务的是shell语言,因此bash脚本、zsh脚本程序在使用shell commands(类比SDK API)编程时,语法上基本相同,部分兼容性差异可参考:zsh和bash的兼容性差异。(这个类似JS代码在Chrome、Safari、Edge等浏览器平台的兼容性)
6.1 基本教程
6.1.1 Bash 脚本教程 — 阮一峰
- 简介
- 基本语法
- 模式扩展
- 引号和转义
- 变量
- 字符串操作
- 算术运算
- 行操作
- 目录堆栈
- 脚本入门
- read 命令
- 条件判断
- 循环
- 函数
- 数组
- set 命令,shopt 命令
- 脚本除错
- mktemp 命令,trap 命令
- 启动环境
- 命令提示符
6.1.2 补充
1. 知识点
变量替换:一般情况下,$var与${var}是没有区别的,但是用${ }会比较精确的界定变量名称的范围
命令替换:在bash中,$( )与反引号
` `
都是用来作命令替换的。命令替换与变量替换差不多,都是用来重组命令行的,先完成引号里的命令行,然后将其结果替换出来,再重组成新的命令行。` `
很容易与''
搞混乱,尤其对初学者来说,而$( )比较直观。但$( )的弊端是,并不是所有的类unix系统都支持这种方式,而反引号是肯定支持的。(Mac上就不支持$())1
$ RESULT=`curl -s www.baidu.com` #此时curl命令的返回数据不会显示在控制台,而是赋值该给RESULT变量
Bash 允许字符串放在单引号或双引号之中,加以引用。
- 单引号用于保留字符的字面含义,各种特殊字符在单引号里面,都会变为普通字符,比如星号(
*
)、美元符号($
)、反斜杠(\
)等。 - 双引号比单引号宽松,大部分特殊字符在双引号里面,都会失去特殊含义,变成普通字符。三个特殊字符除外:美元符号(
$
)、反引号(` `
)和反斜杠(\
)。这三个字符在双引号之中,依然有特殊含义,会被 Bash 自动扩展。
- 单引号用于保留字符的字面含义,各种特殊字符在单引号里面,都会变为普通字符,比如星号(
当命令太长时,一般可用续行符 “
\
“ 进行换行/续行。反斜杠\
后面紧跟回车,表示下一行是当前行的续行。- 注意:
\
后面紧接着是enter换行符,即使用\回车
的方式,不能有空格之类的任何符号,否则会造成解析错误。
- 注意:
分号(
;
)是命令的结束符,使得一行可以放置多个命令,上一个命令执行结束后(不管成功或失败),再执行第二个命令。除了分号,Bash 还提供两个命令组合符
&&
和||
,允许更好地控制多个命令之间的继发关系。&&
:表示前面的命令成功后,执行后面的。||
:表示前面的命令失败后,执行后面的。- 结合实现三目运算符的效果:
1
2
3
4
5command1 && command2 || command3
# 如果 command 是一连串的组合,那么可以使用 { } 将commands 括起来。
command1
&& { command2_1; command2_2; command2_3; }
|| { command3_1; command3_3; command3_3; }
字符串拼接:在 Shell 中你不需要使用任何运算符,将两个字符串并排放在一起就能实现拼接,非常简单粗暴。
1
2
3
4
5
6
7name="Shell"
url="http://c.biancheng.net/shell/"
str1=$name$url #中间不能有空格
str2="$name $url" #如果被双引号包围,那么中间可以有空格
str3=$name": "$url #中间可以出现别的字符串
str4="$name: $url" #这样写也可以 冒号不会认为是变量名的一部分。因为Bash中变量是由字母、数字和下划线字符组成。不允许出现空格和标点符号。
str5="${name}Script: ${url}index.html" #这个时候需要给变量名加上大括号
2. 2>&1
的解释
结论:2>&1的意思是将标准错误(2)也定向到标准输出(1)的输出文件中。
我们来具体了解下:Linux 中三种标准输入输出,分别是STDIN,STDOUT,STDERR,对应的数字是0,1,2。
- STDIN就是标准输入,默认从键盘读取信息;
- STDOUT是标准输出,默认将输出结果输出至终端,也就是显示器之类的东西;
- STDERR是标准的错误信息,默认也会显示在终端上。
由于STDOUT与STDERR都会默认显示在终端上,为了区分二者的信息,就有了编号的0,1,2的定义,用1表示STDOUT,2表示STDERR。
从command>/dev/null说起
- 其实这条命令是一个缩写版,对于一个重定向命令,肯定是
a > b
这种形式,那么command > /dev/null
难道是command充当a的角色,/dev/null充当b的角色。 - 这样看起来比较合理,其实一条命令肯定是充当不了a,肯定是command执行产生的输出来充当a,其实就是标准输出stdout。
- 所以
command > /dev/null
相当于执行了command 1 > /dev/null
,执行command产生了标准输出stdout(1表示标准输出,可以省略),重定向到/dev/null的设备文件中。
说说2>&1
- 通过上面
command > /dev/null
等价于command 1 > /dev/null
,那么对于2>&1
也就好理解了,2就是标准错误,1是标准输出,那么这条命令不就是相当于把标准错误重定向到标准输出么。 - 等等是&1而不是1,这里&是什么?
&
是一个描述符,如果1或2前加上&
,表示标准输出/标准错误要输出到的文件。反之,如果1或2前不加&
,会被当成一个普通文件。
command>a 2>a 与 command>a 2>&1的区别
- 通过上面的分析,对于
command>a 2>&1
(等价于command 1>a 2>&1
)这条命令,可以理解为执行command产生的标准输入重定向到文件a中,标准错误也重定向到文件a中。 - 那么是否就说
command 1>a 2>&1
等价于command 1>a 2>a
呢。其实不是,command 1>a 2>&1
与command 1>a 2>a
还是有区别的,区别就在于前者只打开一次文件a,后者会打开文件两次,并导致stdout被stderr覆盖。&1
的含义就可以理解为用标准输出的引用,引用的就是重定向标准输出产生打开的a。从IO效率上来讲,command 1>a 2>&1
比command 1>a 2>a
的效率更高
再思考一下
- 为什么2>&1 要放在后边呢? 我的理解是因为2(也就是错误输出)要重定向到&1,也就是标准输出的引用中,也就是标准输出打开的文件中,所以需要先在前面打开1的输出文件。
6.2 网络请求
curl 是常用的命令行工具,用来请求 Web 服务器。它的名字就是客户端(client)的 URL 工具的意思。curl 的用法指南 by 阮一峰
curl 与 wget的比较:
- 相似之处:
- 都可以向互联网发送请求并返回请求项。这可以是文件、图片或者是其他诸如网站的原始 HTML 之类。
- 都可以进行 HTTP POST 请求。
- 因为都是命令行工具,所以皆可脚本化,可以写进 Bash 脚本
- wget的优势:
- 简单直接。wget 是一个独立的程序,无需额外的资源库,更不会做其范畴之外的事情。
- 是专业的直接下载程序,支持递归下载。同时,允许你下载网页中或是 FTP 目录中的任何内容。
- 拥有智能的默认设置。它规定了很多在常规浏览器里的事物处理方式,比如 cookies 和重定向,这都不需要额外的配置。可以说,wget 简直就是无需说明,开罐即食!
- curl的优势:
- 是一个多功能工具。当然,它可以下载网络内容,但同时它也能做更多别的事情。
- 技术支持库是:libcurl。这就意味着你可以基于 libcurl 库编写图形环境的下载程序,访问它所有的功能。
- 宽泛的网络协议支持是其很大的卖点。curl 支持访问 HTTP 和 HTTPS 协议,能够处理 FTP 传输。它支持 LDAP 协议,甚至支持 Samba 分享、收发邮件。
- 也有一些简洁的安全特性。curl 支持安装许多 SSL/TLS 库,也支持通过网络代理访问,包括 SOCKS。这意味着,你可以越过 Tor 来使用curl。
- curl 同样支持让数据发送变得更容易的 gzip 压缩技术。
- 小结
- curl 与 wget 的选择得看实际用途。如果想快速下载并且没有担心参数标识的需求,那应该使用轻便有效的 wget。如果想做一些更复杂的使用,应该选择 curl。
- cURL 支持做很多事情。可以把 cURL 想象成一个精简的命令行网页浏览器。它支持几乎所有能想到的协议,可以交互访问几乎所有在线内容。唯一和浏览器不同的是,curl 不会渲染接收到的相应信息。
6.3 数据解析
- 使用jq进行json的解析