Unix Shell


Unix Shell是一种外壳与命令行界面,系UNIX操作系统下传统的用户和计算机的交互界面。第一个用户直接输入命令来执行各种各样的任务。“Shell”的名字和概念是从Unix的前身Multics发展过来的,应用程序通过shell来进行调用并被系统执行。Shell是一块包裹着系统核心的壳,处于操作系统的最外层,与用户直接对话,把用户的输入,解释给操作系统,然后处理操作系统的输出结果,输出到屏幕给与用户看到结果。是用户与系统交互的主要方式之一。

普通意义上的shell就是可以接受用户输入命令的程序。它之所以被称作shell是因为它隐藏了操作系统低层的细节。同样的Unix下的图形用户界面GNOME和KDE等桌面环境,有时也被叫做“虚拟shell”或“图形shell”。
Unix操作系统下的shell既是用户交互的界面,也是控制系统的脚本语言。在这点上有别于Windows下的命令行,虽然也提供了很简单的控制语句。在Windows操作系统下,可能有些用户从来都不会直接的使用shell,然而在Unix系列操作系统下,shell仍然是控制系统启动、视窗系统启动和很多其他实用工具的脚本解释程序。
Shell的作用
• 解释执行用户输入的命令或程序等
• 用户输入一条命令,Shell 就解释一条键盘输入命令,Linux 给与响应的方式,称之为交互式
想要获取计算机的数据,不可能每次都编写程序,编译后,再运行,再得到想要的,例如想找到一个文件,可以先写一段 C语言的代码,然后调用系统函数,通过 gcc 编译后,运行程序才能找到文件。
因此有开发出了 Shell 解释器,能够让人方便的使用 Linux,例如只要敲下 ls -lh 这样的字符串,Shell 解释器就会针对这句话翻译,解释成 ls -l -h 然后执行 ,通过终端输出结果,无论是图形化或是命令行界面。

即使使用的图形化界面,区别是:
• 命令行操作,Shell 解释执行后,输出结果到黑屏命令行界面;
• 图形化操作,Shell 接受点击动作,输出图案数据。
各种Unix shell
第一个Unix shell是由肯·汤普逊,仿效Multics上的shell所实现出来,称为sh。
Bourne shell兼容
Bourne shell(sh)史蒂夫·伯恩在贝尔实验室时编写,1978年随Version 7 Unix首次发布。多数系统Unix/Linux系统的/bin/sh,是事实意义上的shell;最早的 Unix Shell,是 Bash 的前身,功能相对简单。下文还会其介绍。
Almquist shell(ash)
Bourne-Again shell(bash):最广泛使用的 Shell,是 GNU 项目的一部分,提供了丰富的功能和强大的脚本支持。默认为大多数 Linux 发行版和 macOS(直到 macOS Mojave)。
Debian Almquist shell(dash)
Korn shell(ksh)由David Korn在贝尔实验室时编写。
Tcsh (C Shell) :提供了类似于 C 语言的语法,适合熟悉 C 语言的用户。
Z shell(zsh):提供了许多高级功能,如自动补全、主题支持等,是 macOS Catalina 及以后版本的默认 Shell。
Fish (Friendly Interactive Shell) :一个现代的 Shell,强调用户友好性和交互性,提供了智能的自动补全和语法高亮等功能。
C shell兼容
C shell(csh)比尔·乔伊在加州大学伯克利分校时编写。1979年随BSD首次发布。
TENEX C shell(tcsh)
其他
fish,第一次发布于2005年。
rc shell(rc)九号项目系统的shell,由Tom Duff在贝尔实验室时编写。随后移植回Unix和其他的操作系统。
es shell(es)一个函数式编程的rc兼容shell,编写于二十世纪九十年代中期。
scsh(Scheme Shell)
仅存于历史的shell
Thompson shell(sh)第一个Unix shell,由肯·汤普逊在贝尔实验室时编写。1971年至1975年随Unix第一版至第六版发布。
PWB shell(sh)Thompson shell的一个版本,由John Mashey和他人在贝尔实验室时改进。1976年随PWB UNIX发布。
Bourne shell
Bourne shell(伯恩壳),或sh,是Version 7 Unix默认的Unix shell,替代执行文件同为sh的Thompson shell。它由AT&T贝尔实验室的史蒂夫·伯恩在1977年左右在Version 7 Unix中针对大学与学院发布的。它的二进制程序文件在大多数Unix系统上位于/bin/sh,在很多Unix版本中,它仍然是root的默认shell。首发于1979年。
其concise(简洁),compact(紧凑),fast(高效)是非常适合系统管理shell。
Almquist shell(ash)
经常被简称为 A Shell 或 ash,一种Unix shell,源自于Bourne shell,由肯尼斯·艾昆斯特(Kenneth Almquist)在SVR4上建立了这个分支,并于1989年5月下旬始发。它的特色是快而且轻巧,兼容于POSIX,在后来的BSD版本上,它取代了Bourne shell。
在FreeBSD、NetBSD、DragonFly BSD、Minix上,预设安装的shell(/bin/sh) ,都是衍生自 ash。在Linux上,发展出了 Debian Almquist shell。
Debian Almquist shell(dash)
缩写为dash,一种 Unix shell,兼容于POSIX标准。比 Bash 小,只需要较少的磁盘空间,但是它的功能也较少。它由 NetBSD 版本的Almquist shell (ash)发展而来,于1997年7月由赫伯特·许(Herbert Xu)移植到Linux上,于2002年改名为 dash。
Dash 类似于 ash,其执行 shell scripts 的速度比bash快,依赖库也较少。
KornShell(ksh)
由大卫·科恩于二十世纪八十年代早期在贝尔实验室开发的Unix shell,并在1983年7月14日的USENIX年度技术会议上发布。起初此软件基于Bourne shell的源代码编写。其他贡献者有贝尔实验室的开发者,如迈克·维奇(Mike Veach)和帕特·沙利文(Pat Sullivan);他们分别编写了Emacs和vi风格的行编辑模式代码。其向后兼容Bourne shell,同时还根据贝尔实验室用户的需求添加了诸多C shell的功能。

KornShell实现了POSIX.2,同时拥有壳层及实用工具、命令解释器(IEEE Std 1003.2-1992.)。与传统的Bourne shell的主要差别有:
根据C shell所设计的作业控制、命令别名及命令历史特性。作业控制功能于1989年才添加至Bourne Shell。
提供基于vi、Emacs和XEmacs的三种命令行编辑风格。
关联数组及内置的浮点运算操作(仅在ksh93版KornShell中可用)。
内置指令的动态可扩展(截至ksh93版本)。
KornShell起初是一款专有软件。在2000年,其源代码以AT&T独有的许可发布;但在2005年年初,93q版本使用Eclipse公共许可证发布。由于KornShell起初为私有授权软件,在当时的人们创造了自由开源的替代品(pdksh、mksh、GNU bash及zsh)。KornShell被收录进了AT&T软件技术(AST)开源软件集。
KornShell的原始版本(ksh88)被当做POSIX.2、壳层及实用工具、命令解释器(IEEE Std 1003.2-1992.)的基础。
部分供应商有时会提供其自己根据老版本ksh88改编的变体,有时还会在其基础上添加扩展。ksh93现仍被作者维护,并通过在其后添加字母的方式命名发布版本;截至2017年1月16日的最新版本为ksh93u+(ksh93u及ksh93t+的更新版);ksh93v仍处于测试阶段(截至2017年1月)。
作为“桌面版KornShell”(dtksh),ksh93作为通用桌面环境的一部分发布。此版本同时提供了对Motif挂件的壳层级映射。它的目的是成为Tcl/Tk的竞争对手。
原版KornShell(ksh88)成为了AIX第四版的默认壳层,但同时也可使用ksh93。
UnixWare 7中既包括ksh88,也包括ksh93。默认的Kornshell版本为ksh93(/usr/bin/ksh),老版本则在/usr/bin/ksh88下可用。UnixWare同时在安装了CDE的情况下提供{{mono|dtksh}。
OpenBSD下的ksh(rksh,pd ksh)则是公有领域的KornShell。由于上述原因,基于其派生了不少分支产品(各使用了较为开放的授权协议):
dtksh – ksh93的分支,为CDE的一部分;Eclipse公共许可证,部分使用LGPL。
tksh – ksh93的分支,提供对Tk部件工具箱的访问。
oksh – OpenBSD风味KornShell的Linux版分支,为DeLi Linux的默认壳层。
pdksh - 公有领域,含部分类ISC代码。
mksh – KornShell语言的自由软件实现,为pdksh的分支。其起初为MirOS BSD开发,以宽松的授权条款发布(MirOS许可证)。除了用在BSD上外,此变体还替代了Debian上的pdksh,更是Android的默认壳层。
SKsh – AmigaOS风味的版本,提供了多个Amiga的特有功能(如ARexx互用性)
MKS Inc.的MKS Korn shell – 来自用于UNIX的微软Windows服务(SFU)对KornShell语言的专有实现(版本2.0及以下);据大卫·科恩所述,MKS Korn shell与1998年时的Kornshell不完全兼容。在SFU版本3.0中,微软使用Interix中的POSIX.2兼容版壳层替换了MKS Korn shell。
大卫·科恩的Unix兼容包UWIN中含有KornShell。
Z shell(Zsh)
一款可用作交互式登录的shell及脚本编写的命令解释器,它对Bourne shell做出了大量改进,同时加入了Bash、ksh及tcsh的某些功能。由保罗·弗斯塔德(Paul Falstad)于1990年在普林斯顿大学求学时编写了Zsh的初版,其名称源于耶鲁大学教授邵中(Zhong Shao,后转为普林斯顿大学教授) — 保罗将教授的用户名"zsh"作为此Shell的名称。采用MIT协议授权。

2019年时,在MacOS上由于Bash的版本已经很旧(v3.2.57),而新版本的Bash v5改采GPLv3授权,这是Apple公司无法接受的。于是自当时起,macOS的默认Shell已从Bash改为Zsh。
Kali Linux也使用zsh作为默认shell。用户社区网站"Oh My Zsh"收集Z shell的第三方插件及主题。同时也带有更新已安装插件及主题的自动更新工具。
特性:
可帮助用户键入常用命令选项及参数的可编程命令行补全功能,自带对数百条命令的支持
可与任意Shell共享命令行历史记录
可在无需运行外部程序(如find)的情况下通过文件扩展匹配文件
改进变量/数组处理方式
在单缓冲区内编辑多行命令与拼写检查
多种兼容模式(例如,Zsh可在运行为/bin/sh的情况下伪装成Bourne shell)
可编程的命令行界面,包括将提示行信息显示在屏幕右侧并在输入过长指令时自动隐藏的功能
可加载模块可提供额外支持:完整传输控制协议、Unix域套接字控制、FTP客户端及扩展数学函数。
自带where命令,其与which命令类似,但是显示指定于$PATH中所指定指令的全部位置,而不是仅显示所使用指令的位置。
目录名称。此功能可让用户设置快捷方式,(如~mydir,与~及~user的工作方式相似)。
Thompson shell
这是历史上第一个Unix shell,1971年由肯·汤普逊写作出第一版并加入UNIX之中。它是一个简单的命令行解释器,但不能被用来执行指令稿(Shell script)。它的许多特征影响了以后命令行界面的发展。至Version 7 Unix之后,被Bourne shell取代。它所使用的重导向命令“<”与“>”,以及流水线命令“|”影响到之后的Unix shell与MS DOS,绝大多数Unix shell和其他系统的命令行(包括DOS、OS/2、Windows等)都支持这三个符号。
Thompson shell设计之初就为输入和输出重定向设计了简单的语法。在设计Multics系统时,输入输出的重定向需要用特别的命令来表示重定向开始和重定向结束,而在Unix系统中,用户只需要简单地在命令后面附加一个“<”或“>”符号,再附上文件名,shell就可以在执行命令时处理输入或输出重定向。在1971年Unix发布第一个版本时,这个语法就已经被实现了。后来又加入了管道的功能。在道格拉斯·麦克罗伊的建议下,重定向的功能被扩展,这样一条命令的输出就可以用作另一条命令的输入。
Thompson shell是按照极简主义进行设计的,语法非常简单,即使是不太复杂的“if”和“goto”也需要多条命令来实现。在1975年Version 6 Unix发布之际,这个shell已经无法满足大多数编程任务的需要。这时,PWB/UNIX的开发者,特别是约翰·马沙开始修改Thompson shell的源代码,使其更适合编程。修改之后的shell被称为PWB shell或Mashey shell,包括了高级的控制流,并引入了变量,但新功能也有所限制,以保证与Thompson shell的兼容性。
最终在1978年,Version 7 Unix已经把默认的shell换成了Bourne shell,而1979年时2BSD把默认shell换成了C shell。因为所有现代Unix系统和类Unix系统都是从Version 7 Unix和2BSD继承而来,所以Thompson shell基本上已经不再被使用。但Thompson shell作为Ancient UNIX系统的一部分,并且开放源代码,它也被移植到了现代Unix系统中,作为历史展览而出现。
当命令或者程序语句写在文件中并将该脚本赋于可执行权限,读取其中的代码解释执行的这个程序文件就可称之为 Shell 脚本。在 Shell 脚本里定义多条 Linux 命令以及循环控制语句,然后将这些 Linux 命令一次性执行完毕,执行脚本文件的方式称之为,非交互式方式,如:
• windows 中存在 *.bat 批处理脚本
• Linux 中常用 *.sh 脚本文件
Shell脚本语言属于一种弱类型语言,无需声明变量类型,直接定义使用强类型语言,必须先定义变量类型,确定是数字、字符串等,之后再赋予同类型的值 centos7 系统中支持的 Shell 情况,有如下种类:
# cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/bin/dash
/usr/bin/dash
/usr/bin/tmux
/usr/bin/screen
Linux 默认 Shell
# echo $SHELL
/bin/bash
在 Linux 系统中,Shell 脚本或者称之为 (bash shell程序) 通常都是 vim 编辑,由 Linux 命令、bash shell 指令、逻辑控制语句和注释信息组成。
Shebang
计算机程序中 Shebang 指的是出现在文本文件的第一行前两个字符 #!
• 指明这个代码文件用哪个解释器去读
在 Unix 系统中,程序会分析 Shebang 后面的内容,作为解释器的指令,例如
• 以 #!/bin/bash 开头的文件,程序在执行的时候会调用 /bin/bash ,也就是 bash 解释器
• 以 #!/usr/bin/perl 开头的文件,代表指定perl解释器去执行
• 以 #!/usr/bin/python 开头的文件,代表指定python解释器去执行
• 以 #!/usr/bin/env 解释器名称 ,是一种在不同平台上都能正确找到解释器的办法
注意事项:
• 如果脚本未指定 Shebang ,脚本执行的时候,默认用当前 shell 去解释脚本,即 $SHELL。
• 如果 Shebang 指定了可执行的解释器,如 /bin/bash /usr/bin/perl,脚本在执行时文件名会作为参数传递给解释器
• 如果 #! 指定的解释程序没有可执行权限,则会报错“bad interpreter: Permission denied”。
• 如果 #! 指定的解释程序不是一个可执行文件,那么指定的解释程序会被忽略,转而交给当前的 SHELL 去执行这个脚本。
• 如果 #! 指定的解释程序不存在,那么会报错 “bad interpreter: No such file or directory”。
• #! 之后的解释程序,需要写其绝对路径 (如:#!/bin/bash),它是不会自动到 $PATH 中寻找解释器的。
• 如果你使用 “bash test.sh” 这样的命令来执行脚本,那么 #! 这一行将会被忽略掉,解释器当然是用命令行中显式指定的 bash。
脚本的常用执行方式
第一种:采用 bash 或 sh + 脚本的相对路径或绝对路径(不用赋予脚本 +x 权限)
sh + 脚本的相对路径
$ sh helloworld.sh
Helloworld
sh + 脚本的绝对路径
$ sh /home/freeoa/datas/helloworld.sh
helloworld
bash+脚本的相对路径
$ bash helloworld.sh
Helloworld
bash + 脚本的绝对路径
$ bash /home/freeoa/datas/helloworld.sh
Helloworld
第二种:采用输入脚本的绝对路径或相对路径执行脚本(必须具有可执行权限 +x)
(a)首先要赋予 helloworld.sh 脚本的 + x权限
$ chmod 755 helloworld.sh
(b)执行脚本
相对路径(推荐使用)与绝对路径。
注意:第一种执行方法,本质是 bash解析器帮助执行脚本,所以脚本本身不需要执行权限。第二种执行方法,本质是脚本需要自己执行,所以需要执行权限。
Shell 的优势
虽然有诸多脚本编程语言,但是对于 Linux 操作系统内部应用而言,Shell 是最好的工具,Linux 底层命令都支持 Shell 语句,以及结合三剑客 (grep、sed、awk) 进行高级用法。其擅长系统管理脚本开发,如软件启停脚本、监控报警脚本、日志分析脚本;每个语言都有自己擅长的地方,扬长避短,达到高效运维的目的是最合适的。
Shell 中的变量
• 变量定义与赋值,注意变量与值之间不得有空格
name="freeoa"
变量名
变量类型,bash默认把所有变量都认为是字符串
bash变量是弱类型,无需事先声明类型,是将声明和赋值同时进行。
• 变量替换写弓|用
# name="freeoa带你学bash"
# echo ${name}
freeoa带你学bash
# echo $name # 可以省略花括号
freeoa带你学bash
• 变量名规则
• 名称定义要做到见名知意,切按照规则来,切不得引用保留关键字(help检查保留字)
• 只能包含数字、字母、下划线
• 不能以数字开头
• 不能用标点符号
• 变量名严格区分大小写
• 变量的作用域
• 本地变量,只针对当前的 shell 进程
系统变量
1)常用系统变量
HOME、PWD、SHELL、USER等。
2)案例实操
(1)查看系统变量的值
$ echo $HOME
/home/freeoa
(2)显示当前 Shell 中所有变量:set
$ set
BASH=/bin/bash
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
自定义变量
基本语法如下:
(1)定义变量:变量=值
(2)撤销变量:unset 变量
(3)声明静态变量:readonly 变量,注意:不能unset
单引号 双引号区别
单引号变量,不识别特殊语法
双引号变量,能识别特殊符号
反引号,引用命令执行结果,等于 $() 用法
特殊变量
• $?
• 0 成功
• 1-255 错误码
Shell 的特殊变量,用在如脚本,函数传递参数时使用,有如下特殊的位置参数变量:
$0 获取shel1脚本文件名,以及脚本路径
$n 获取shel1脚本的第n个参数,n在1~9之间,如$1,$2,$9,大于9则需要写,${10},参数空格隔开
$# 获取执行的she11脚本后面的参数总个数
$* 获取she11脚本所有参数,不加引号等同于 $@ 作用,加上引号 "$*" 作用是 接收所有参数为单个字符串,"$1 $2.
$@ 不加引号,效果同上,加引号,是接收所有参数为独立字符串,如"$1" "$2" "$3”...,空格保留
注意:单引号和双引号的区别。
#! /bin/bash
# $* 和 $@ 的区别
echo 'print each param from $*'
for var in"$*"
do
echo "$var"
done
echo 'print each param from $@'
for var in"$@"
do
echo "$var"
done
$*和$@的区别了解吗?
$*和 $@ 都表示传递给函数或脚本的所有参数
当 $*和 $@ 不被双引号""包围时,它们之间没有任何区别,都是将接收到的每个参数看做一份数据,彼此之间以空格来分隔。
但是当它们被双引号""包含时,就会有区别了:
"$*"会将所有的参数从整体上看做一份数据,而不是把每个参数都看做一份数据。'yu chao 180 180 180 180"
"$@"仍然将每个参数都看作一份数据,彼此之间是独立的。
比如传递了 5个参数,那么对于"$*"来说,这 5 个参数会合并到一起形成一份数据,它们之间是无法分割的;而对于"$@"来说,这5 个参数是相互独立的,它们是 5 份数据。
如果使用 echo 直接输出"$*"和"$@"做对比,是看不出区别的;但如果使用 for 循环来逐个输出数据,立即就能看出区别来。
在 linux 中反引号中的命令执行结果会被保留下来
# name=`ls`
# echo $name
test1.sh test2.sh test3.sh
解答:
1. 每次调用 bash/sh 解释器执行脚本,都会开启一个子 shell,因此不保留当前的 shell 变量,可通过 pstree 命令检查进程树;
2. 调用 source 或者点符号,在当前 shell 环境加载脚本,因此保留变量。
环境变量设置
环境变量一般指的是用 export 内置命令导出的变量,用于定义 shell 的运行环境、保证 shell 命令的正确执行。shell 通过环境变量确定登录的用户名、PATH路径、文件系统等各种应用。
环境变量可以在命令行中临时创建,但是用户退出shell终端,变量即丢失,如要永久生效,需要修改环境变量配置文件:
• 用户个人配置文件 ~/.bash profile、 ~/.bashrc 远程登录用户特有文件
• 全局配置文件 /etc/profile/etc/bashrc,且系统建议最好创建在 /etc/profile.d/,而非直接修改主文件,修改全局配置文件,影响所有登录系统的用户
检查系统环境变量的命令
• set,输出所有变量,包括全局变量、局部变量
• env,当前用户的所有环境变量,包括全局环境变量和局部环境变量
• declare,输出所有的变量,如同 set
• export,显示和设置环境变量值
撤销环境变量
• unset 变量名,删除变量或函数
设置只读变量
• readonly,只有shell结束,只读变量失效
直接执行 readonly 显示当前系统只读变量
# readonly name="FreeOA"
# name="Net"
-bash:name:只读变量
环境变量初始化与加载顺序(bash)

Shell 子串
bsah 一些基础的内置命令
echo、eval、exec、export、read、shift
echo 命令
-n 不换行输出
-e 解析字符串中的特殊符号
\n 换行
\r 回车
\t 制表符 四个空格
\b 退格
# 类似还有printf指令
eval
执行多个命令
exec
不创建子进程,执行后续命令,且执行完毕后,自动退出。
# exec date
子串的花式用法
${变量} 返回变量值
${#变量} 返回变量长度,字符长度
${变量:start} 返回变量 offset 数值之后的字符
${变量:start:length} 提取 offset 之后的 length 限制的字符
${变量#word} 从变量开头删除最短匹配的 word 子串
${变量##word} 从变量开头,删除最长匹配的 word
${变量%word} 从变量结尾删除最短的 word
${变量%%word} 从变量结尾开始删除最长匹配的 word
${变量/pattern/string} 用 string 代替第一个匹配的 pattern
${变量//pattern/string) 用 string 代替所有的 pattern
# name="yuchao180'
# echo ${#name}
9
# 截取子串的用法
# echo ${name:3}
hao180
# echo ${name:5}
o180
#设置起点,以及元素长度
# echo ${name:2:4}
chao
• 统计命令执行速度
字符串长度统计方式这么多,谁最快?
time 命令,统计命令执行时长
for 循环的 shell 编程知识语法
for number in {1..100}
do
echo $number
done
一行式:for num in {1..100}; do echo $num; done
Shell编程尽量使用内置的指令,内置的操作,和内置的函数;它们由C语言开发,效率最高,尽可能的减少管道符的操作。
字符串截取
删除匹配到的子串
# name="I am chaoge"
# echo ${name:2:2}
am
# echo ${name:3:5}
m cha
# name2="abcABC123ABcabc"
# #从开头匹配字符删除
# echo ${name2#a*c}
ABC123ABCabc
# 利用%形式,从后向前匹配截取
# name2=abcABC123ABcabc
# echo ${name2%a*c}
abCABC123ABC
# echo ${name2%%a*c}
# echo ${name2%%a*C}
abcABC123ABCabc
# echo ${name2%a*C}
abcABC123ABCabc
替换字符串
# str1="Hello,man,i am your brother."
# echo $str1
Hello,man,i am your brother.
# echo ${str1/man/boy}
Hello,boy,i am your brother.
#多次匹配替换
# echo $str1
Hello,man,i am your brother.
# echo ${str1/o/0}
HellO,man,i am your brother.
# echo ${str1//o/O}
HellO,man,i am yOur brOther.
应用:批量修改文件名
创建一批文件
# touch freeoa_{1..5}_finished.jpg
特殊 Shell 扩展变量
如果parameter变量值为空,返回word字符串,赋值给result变量
result=${parameter:-word}
如果para变量为空,则word替代变量值,且返回其值
result=${parameter:=word}
如果para变量为空,word当作stderr输出,否则输出变量值
用于设置变量为空导致错误时,返回的错误信息
result=$fparameter:?word?
如果para变量为空,什么都不做,否则word返回
result=${parameter:+word}

普通意义上的shell就是可以接受用户输入命令的程序。它之所以被称作shell是因为它隐藏了操作系统低层的细节。同样的Unix下的图形用户界面GNOME和KDE等桌面环境,有时也被叫做“虚拟shell”或“图形shell”。
Unix操作系统下的shell既是用户交互的界面,也是控制系统的脚本语言。在这点上有别于Windows下的命令行,虽然也提供了很简单的控制语句。在Windows操作系统下,可能有些用户从来都不会直接的使用shell,然而在Unix系列操作系统下,shell仍然是控制系统启动、视窗系统启动和很多其他实用工具的脚本解释程序。
Shell的作用
• 解释执行用户输入的命令或程序等
• 用户输入一条命令,Shell 就解释一条键盘输入命令,Linux 给与响应的方式,称之为交互式
想要获取计算机的数据,不可能每次都编写程序,编译后,再运行,再得到想要的,例如想找到一个文件,可以先写一段 C语言的代码,然后调用系统函数,通过 gcc 编译后,运行程序才能找到文件。
因此有开发出了 Shell 解释器,能够让人方便的使用 Linux,例如只要敲下 ls -lh 这样的字符串,Shell 解释器就会针对这句话翻译,解释成 ls -l -h 然后执行 ,通过终端输出结果,无论是图形化或是命令行界面。

即使使用的图形化界面,区别是:
• 命令行操作,Shell 解释执行后,输出结果到黑屏命令行界面;
• 图形化操作,Shell 接受点击动作,输出图案数据。
各种Unix shell
第一个Unix shell是由肯·汤普逊,仿效Multics上的shell所实现出来,称为sh。
Bourne shell兼容
Bourne shell(sh)史蒂夫·伯恩在贝尔实验室时编写,1978年随Version 7 Unix首次发布。多数系统Unix/Linux系统的/bin/sh,是事实意义上的shell;最早的 Unix Shell,是 Bash 的前身,功能相对简单。下文还会其介绍。
Almquist shell(ash)
Bourne-Again shell(bash):最广泛使用的 Shell,是 GNU 项目的一部分,提供了丰富的功能和强大的脚本支持。默认为大多数 Linux 发行版和 macOS(直到 macOS Mojave)。
Debian Almquist shell(dash)
Korn shell(ksh)由David Korn在贝尔实验室时编写。
Tcsh (C Shell) :提供了类似于 C 语言的语法,适合熟悉 C 语言的用户。
Z shell(zsh):提供了许多高级功能,如自动补全、主题支持等,是 macOS Catalina 及以后版本的默认 Shell。
Fish (Friendly Interactive Shell) :一个现代的 Shell,强调用户友好性和交互性,提供了智能的自动补全和语法高亮等功能。
C shell兼容
C shell(csh)比尔·乔伊在加州大学伯克利分校时编写。1979年随BSD首次发布。
TENEX C shell(tcsh)
其他
fish,第一次发布于2005年。
rc shell(rc)九号项目系统的shell,由Tom Duff在贝尔实验室时编写。随后移植回Unix和其他的操作系统。
es shell(es)一个函数式编程的rc兼容shell,编写于二十世纪九十年代中期。
scsh(Scheme Shell)
仅存于历史的shell
Thompson shell(sh)第一个Unix shell,由肯·汤普逊在贝尔实验室时编写。1971年至1975年随Unix第一版至第六版发布。
PWB shell(sh)Thompson shell的一个版本,由John Mashey和他人在贝尔实验室时改进。1976年随PWB UNIX发布。
Bourne shell
Bourne shell(伯恩壳),或sh,是Version 7 Unix默认的Unix shell,替代执行文件同为sh的Thompson shell。它由AT&T贝尔实验室的史蒂夫·伯恩在1977年左右在Version 7 Unix中针对大学与学院发布的。它的二进制程序文件在大多数Unix系统上位于/bin/sh,在很多Unix版本中,它仍然是root的默认shell。首发于1979年。
其concise(简洁),compact(紧凑),fast(高效)是非常适合系统管理shell。
Almquist shell(ash)
经常被简称为 A Shell 或 ash,一种Unix shell,源自于Bourne shell,由肯尼斯·艾昆斯特(Kenneth Almquist)在SVR4上建立了这个分支,并于1989年5月下旬始发。它的特色是快而且轻巧,兼容于POSIX,在后来的BSD版本上,它取代了Bourne shell。
在FreeBSD、NetBSD、DragonFly BSD、Minix上,预设安装的shell(/bin/sh) ,都是衍生自 ash。在Linux上,发展出了 Debian Almquist shell。
Debian Almquist shell(dash)
缩写为dash,一种 Unix shell,兼容于POSIX标准。比 Bash 小,只需要较少的磁盘空间,但是它的功能也较少。它由 NetBSD 版本的Almquist shell (ash)发展而来,于1997年7月由赫伯特·许(Herbert Xu)移植到Linux上,于2002年改名为 dash。
Dash 类似于 ash,其执行 shell scripts 的速度比bash快,依赖库也较少。
KornShell(ksh)
由大卫·科恩于二十世纪八十年代早期在贝尔实验室开发的Unix shell,并在1983年7月14日的USENIX年度技术会议上发布。起初此软件基于Bourne shell的源代码编写。其他贡献者有贝尔实验室的开发者,如迈克·维奇(Mike Veach)和帕特·沙利文(Pat Sullivan);他们分别编写了Emacs和vi风格的行编辑模式代码。其向后兼容Bourne shell,同时还根据贝尔实验室用户的需求添加了诸多C shell的功能。

KornShell实现了POSIX.2,同时拥有壳层及实用工具、命令解释器(IEEE Std 1003.2-1992.)。与传统的Bourne shell的主要差别有:
根据C shell所设计的作业控制、命令别名及命令历史特性。作业控制功能于1989年才添加至Bourne Shell。
提供基于vi、Emacs和XEmacs的三种命令行编辑风格。
关联数组及内置的浮点运算操作(仅在ksh93版KornShell中可用)。
内置指令的动态可扩展(截至ksh93版本)。
KornShell起初是一款专有软件。在2000年,其源代码以AT&T独有的许可发布;但在2005年年初,93q版本使用Eclipse公共许可证发布。由于KornShell起初为私有授权软件,在当时的人们创造了自由开源的替代品(pdksh、mksh、GNU bash及zsh)。KornShell被收录进了AT&T软件技术(AST)开源软件集。
KornShell的原始版本(ksh88)被当做POSIX.2、壳层及实用工具、命令解释器(IEEE Std 1003.2-1992.)的基础。
部分供应商有时会提供其自己根据老版本ksh88改编的变体,有时还会在其基础上添加扩展。ksh93现仍被作者维护,并通过在其后添加字母的方式命名发布版本;截至2017年1月16日的最新版本为ksh93u+(ksh93u及ksh93t+的更新版);ksh93v仍处于测试阶段(截至2017年1月)。
作为“桌面版KornShell”(dtksh),ksh93作为通用桌面环境的一部分发布。此版本同时提供了对Motif挂件的壳层级映射。它的目的是成为Tcl/Tk的竞争对手。
原版KornShell(ksh88)成为了AIX第四版的默认壳层,但同时也可使用ksh93。
UnixWare 7中既包括ksh88,也包括ksh93。默认的Kornshell版本为ksh93(/usr/bin/ksh),老版本则在/usr/bin/ksh88下可用。UnixWare同时在安装了CDE的情况下提供{{mono|dtksh}。
OpenBSD下的ksh(rksh,pd ksh)则是公有领域的KornShell。由于上述原因,基于其派生了不少分支产品(各使用了较为开放的授权协议):
dtksh – ksh93的分支,为CDE的一部分;Eclipse公共许可证,部分使用LGPL。
tksh – ksh93的分支,提供对Tk部件工具箱的访问。
oksh – OpenBSD风味KornShell的Linux版分支,为DeLi Linux的默认壳层。
pdksh - 公有领域,含部分类ISC代码。
mksh – KornShell语言的自由软件实现,为pdksh的分支。其起初为MirOS BSD开发,以宽松的授权条款发布(MirOS许可证)。除了用在BSD上外,此变体还替代了Debian上的pdksh,更是Android的默认壳层。
SKsh – AmigaOS风味的版本,提供了多个Amiga的特有功能(如ARexx互用性)
MKS Inc.的MKS Korn shell – 来自用于UNIX的微软Windows服务(SFU)对KornShell语言的专有实现(版本2.0及以下);据大卫·科恩所述,MKS Korn shell与1998年时的Kornshell不完全兼容。在SFU版本3.0中,微软使用Interix中的POSIX.2兼容版壳层替换了MKS Korn shell。
大卫·科恩的Unix兼容包UWIN中含有KornShell。
Z shell(Zsh)
一款可用作交互式登录的shell及脚本编写的命令解释器,它对Bourne shell做出了大量改进,同时加入了Bash、ksh及tcsh的某些功能。由保罗·弗斯塔德(Paul Falstad)于1990年在普林斯顿大学求学时编写了Zsh的初版,其名称源于耶鲁大学教授邵中(Zhong Shao,后转为普林斯顿大学教授) — 保罗将教授的用户名"zsh"作为此Shell的名称。采用MIT协议授权。

2019年时,在MacOS上由于Bash的版本已经很旧(v3.2.57),而新版本的Bash v5改采GPLv3授权,这是Apple公司无法接受的。于是自当时起,macOS的默认Shell已从Bash改为Zsh。
Kali Linux也使用zsh作为默认shell。用户社区网站"Oh My Zsh"收集Z shell的第三方插件及主题。同时也带有更新已安装插件及主题的自动更新工具。
特性:
可帮助用户键入常用命令选项及参数的可编程命令行补全功能,自带对数百条命令的支持
可与任意Shell共享命令行历史记录
可在无需运行外部程序(如find)的情况下通过文件扩展匹配文件
改进变量/数组处理方式
在单缓冲区内编辑多行命令与拼写检查
多种兼容模式(例如,Zsh可在运行为/bin/sh的情况下伪装成Bourne shell)
可编程的命令行界面,包括将提示行信息显示在屏幕右侧并在输入过长指令时自动隐藏的功能
可加载模块可提供额外支持:完整传输控制协议、Unix域套接字控制、FTP客户端及扩展数学函数。
自带where命令,其与which命令类似,但是显示指定于$PATH中所指定指令的全部位置,而不是仅显示所使用指令的位置。
目录名称。此功能可让用户设置快捷方式,(如~mydir,与~及~user的工作方式相似)。
Thompson shell
这是历史上第一个Unix shell,1971年由肯·汤普逊写作出第一版并加入UNIX之中。它是一个简单的命令行解释器,但不能被用来执行指令稿(Shell script)。它的许多特征影响了以后命令行界面的发展。至Version 7 Unix之后,被Bourne shell取代。它所使用的重导向命令“<”与“>”,以及流水线命令“|”影响到之后的Unix shell与MS DOS,绝大多数Unix shell和其他系统的命令行(包括DOS、OS/2、Windows等)都支持这三个符号。
Thompson shell设计之初就为输入和输出重定向设计了简单的语法。在设计Multics系统时,输入输出的重定向需要用特别的命令来表示重定向开始和重定向结束,而在Unix系统中,用户只需要简单地在命令后面附加一个“<”或“>”符号,再附上文件名,shell就可以在执行命令时处理输入或输出重定向。在1971年Unix发布第一个版本时,这个语法就已经被实现了。后来又加入了管道的功能。在道格拉斯·麦克罗伊的建议下,重定向的功能被扩展,这样一条命令的输出就可以用作另一条命令的输入。
Thompson shell是按照极简主义进行设计的,语法非常简单,即使是不太复杂的“if”和“goto”也需要多条命令来实现。在1975年Version 6 Unix发布之际,这个shell已经无法满足大多数编程任务的需要。这时,PWB/UNIX的开发者,特别是约翰·马沙开始修改Thompson shell的源代码,使其更适合编程。修改之后的shell被称为PWB shell或Mashey shell,包括了高级的控制流,并引入了变量,但新功能也有所限制,以保证与Thompson shell的兼容性。
最终在1978年,Version 7 Unix已经把默认的shell换成了Bourne shell,而1979年时2BSD把默认shell换成了C shell。因为所有现代Unix系统和类Unix系统都是从Version 7 Unix和2BSD继承而来,所以Thompson shell基本上已经不再被使用。但Thompson shell作为Ancient UNIX系统的一部分,并且开放源代码,它也被移植到了现代Unix系统中,作为历史展览而出现。
当命令或者程序语句写在文件中并将该脚本赋于可执行权限,读取其中的代码解释执行的这个程序文件就可称之为 Shell 脚本。在 Shell 脚本里定义多条 Linux 命令以及循环控制语句,然后将这些 Linux 命令一次性执行完毕,执行脚本文件的方式称之为,非交互式方式,如:
• windows 中存在 *.bat 批处理脚本
• Linux 中常用 *.sh 脚本文件
Shell脚本语言属于一种弱类型语言,无需声明变量类型,直接定义使用强类型语言,必须先定义变量类型,确定是数字、字符串等,之后再赋予同类型的值 centos7 系统中支持的 Shell 情况,有如下种类:
# cat /etc/shells
# /etc/shells: valid login shells
/bin/sh
/bin/bash
/usr/bin/bash
/bin/rbash
/usr/bin/rbash
/bin/dash
/usr/bin/dash
/usr/bin/tmux
/usr/bin/screen
Linux 默认 Shell
# echo $SHELL
/bin/bash
在 Linux 系统中,Shell 脚本或者称之为 (bash shell程序) 通常都是 vim 编辑,由 Linux 命令、bash shell 指令、逻辑控制语句和注释信息组成。
Shebang
计算机程序中 Shebang 指的是出现在文本文件的第一行前两个字符 #!
• 指明这个代码文件用哪个解释器去读
在 Unix 系统中,程序会分析 Shebang 后面的内容,作为解释器的指令,例如
• 以 #!/bin/bash 开头的文件,程序在执行的时候会调用 /bin/bash ,也就是 bash 解释器
• 以 #!/usr/bin/perl 开头的文件,代表指定perl解释器去执行
• 以 #!/usr/bin/python 开头的文件,代表指定python解释器去执行
• 以 #!/usr/bin/env 解释器名称 ,是一种在不同平台上都能正确找到解释器的办法
注意事项:
• 如果脚本未指定 Shebang ,脚本执行的时候,默认用当前 shell 去解释脚本,即 $SHELL。
• 如果 Shebang 指定了可执行的解释器,如 /bin/bash /usr/bin/perl,脚本在执行时文件名会作为参数传递给解释器
• 如果 #! 指定的解释程序没有可执行权限,则会报错“bad interpreter: Permission denied”。
• 如果 #! 指定的解释程序不是一个可执行文件,那么指定的解释程序会被忽略,转而交给当前的 SHELL 去执行这个脚本。
• 如果 #! 指定的解释程序不存在,那么会报错 “bad interpreter: No such file or directory”。
• #! 之后的解释程序,需要写其绝对路径 (如:#!/bin/bash),它是不会自动到 $PATH 中寻找解释器的。
• 如果你使用 “bash test.sh” 这样的命令来执行脚本,那么 #! 这一行将会被忽略掉,解释器当然是用命令行中显式指定的 bash。
脚本的常用执行方式
第一种:采用 bash 或 sh + 脚本的相对路径或绝对路径(不用赋予脚本 +x 权限)
sh + 脚本的相对路径
$ sh helloworld.sh
Helloworld
sh + 脚本的绝对路径
$ sh /home/freeoa/datas/helloworld.sh
helloworld
bash+脚本的相对路径
$ bash helloworld.sh
Helloworld
bash + 脚本的绝对路径
$ bash /home/freeoa/datas/helloworld.sh
Helloworld
第二种:采用输入脚本的绝对路径或相对路径执行脚本(必须具有可执行权限 +x)
(a)首先要赋予 helloworld.sh 脚本的 + x权限
$ chmod 755 helloworld.sh
(b)执行脚本
相对路径(推荐使用)与绝对路径。
注意:第一种执行方法,本质是 bash解析器帮助执行脚本,所以脚本本身不需要执行权限。第二种执行方法,本质是脚本需要自己执行,所以需要执行权限。
Shell 的优势
虽然有诸多脚本编程语言,但是对于 Linux 操作系统内部应用而言,Shell 是最好的工具,Linux 底层命令都支持 Shell 语句,以及结合三剑客 (grep、sed、awk) 进行高级用法。其擅长系统管理脚本开发,如软件启停脚本、监控报警脚本、日志分析脚本;每个语言都有自己擅长的地方,扬长避短,达到高效运维的目的是最合适的。
Shell 中的变量
• 变量定义与赋值,注意变量与值之间不得有空格
name="freeoa"
变量名
变量类型,bash默认把所有变量都认为是字符串
bash变量是弱类型,无需事先声明类型,是将声明和赋值同时进行。
• 变量替换写弓|用
# name="freeoa带你学bash"
# echo ${name}
freeoa带你学bash
# echo $name # 可以省略花括号
freeoa带你学bash
• 变量名规则
• 名称定义要做到见名知意,切按照规则来,切不得引用保留关键字(help检查保留字)
• 只能包含数字、字母、下划线
• 不能以数字开头
• 不能用标点符号
• 变量名严格区分大小写
• 变量的作用域
• 本地变量,只针对当前的 shell 进程
系统变量
1)常用系统变量
HOME、PWD、SHELL、USER等。
2)案例实操
(1)查看系统变量的值
$ echo $HOME
/home/freeoa
(2)显示当前 Shell 中所有变量:set
$ set
BASH=/bin/bash
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
自定义变量
基本语法如下:
(1)定义变量:变量=值
(2)撤销变量:unset 变量
(3)声明静态变量:readonly 变量,注意:不能unset
单引号 双引号区别
单引号变量,不识别特殊语法
双引号变量,能识别特殊符号
反引号,引用命令执行结果,等于 $() 用法
特殊变量
• $?
• 0 成功
• 1-255 错误码
Shell 的特殊变量,用在如脚本,函数传递参数时使用,有如下特殊的位置参数变量:
$0 获取shel1脚本文件名,以及脚本路径
$n 获取shel1脚本的第n个参数,n在1~9之间,如$1,$2,$9,大于9则需要写,${10},参数空格隔开
$# 获取执行的she11脚本后面的参数总个数
$* 获取she11脚本所有参数,不加引号等同于 $@ 作用,加上引号 "$*" 作用是 接收所有参数为单个字符串,"$1 $2.
$@ 不加引号,效果同上,加引号,是接收所有参数为独立字符串,如"$1" "$2" "$3”...,空格保留
注意:单引号和双引号的区别。
#! /bin/bash
# $* 和 $@ 的区别
echo 'print each param from $*'
for var in"$*"
do
echo "$var"
done
echo 'print each param from $@'
for var in"$@"
do
echo "$var"
done
$*和$@的区别了解吗?
$*和 $@ 都表示传递给函数或脚本的所有参数
当 $*和 $@ 不被双引号""包围时,它们之间没有任何区别,都是将接收到的每个参数看做一份数据,彼此之间以空格来分隔。
但是当它们被双引号""包含时,就会有区别了:
"$*"会将所有的参数从整体上看做一份数据,而不是把每个参数都看做一份数据。'yu chao 180 180 180 180"
"$@"仍然将每个参数都看作一份数据,彼此之间是独立的。
比如传递了 5个参数,那么对于"$*"来说,这 5 个参数会合并到一起形成一份数据,它们之间是无法分割的;而对于"$@"来说,这5 个参数是相互独立的,它们是 5 份数据。
如果使用 echo 直接输出"$*"和"$@"做对比,是看不出区别的;但如果使用 for 循环来逐个输出数据,立即就能看出区别来。
在 linux 中反引号中的命令执行结果会被保留下来
# name=`ls`
# echo $name
test1.sh test2.sh test3.sh
解答:
1. 每次调用 bash/sh 解释器执行脚本,都会开启一个子 shell,因此不保留当前的 shell 变量,可通过 pstree 命令检查进程树;
2. 调用 source 或者点符号,在当前 shell 环境加载脚本,因此保留变量。
环境变量设置
环境变量一般指的是用 export 内置命令导出的变量,用于定义 shell 的运行环境、保证 shell 命令的正确执行。shell 通过环境变量确定登录的用户名、PATH路径、文件系统等各种应用。
环境变量可以在命令行中临时创建,但是用户退出shell终端,变量即丢失,如要永久生效,需要修改环境变量配置文件:
• 用户个人配置文件 ~/.bash profile、 ~/.bashrc 远程登录用户特有文件
• 全局配置文件 /etc/profile/etc/bashrc,且系统建议最好创建在 /etc/profile.d/,而非直接修改主文件,修改全局配置文件,影响所有登录系统的用户
检查系统环境变量的命令
• set,输出所有变量,包括全局变量、局部变量
• env,当前用户的所有环境变量,包括全局环境变量和局部环境变量
• declare,输出所有的变量,如同 set
• export,显示和设置环境变量值
撤销环境变量
• unset 变量名,删除变量或函数
设置只读变量
• readonly,只有shell结束,只读变量失效
直接执行 readonly 显示当前系统只读变量
# readonly name="FreeOA"
# name="Net"
-bash:name:只读变量
环境变量初始化与加载顺序(bash)

Shell 子串
bsah 一些基础的内置命令
echo、eval、exec、export、read、shift
echo 命令
-n 不换行输出
-e 解析字符串中的特殊符号
\n 换行
\r 回车
\t 制表符 四个空格
\b 退格
# 类似还有printf指令
eval
执行多个命令
exec
不创建子进程,执行后续命令,且执行完毕后,自动退出。
# exec date
子串的花式用法
${变量} 返回变量值
${#变量} 返回变量长度,字符长度
${变量:start} 返回变量 offset 数值之后的字符
${变量:start:length} 提取 offset 之后的 length 限制的字符
${变量#word} 从变量开头删除最短匹配的 word 子串
${变量##word} 从变量开头,删除最长匹配的 word
${变量%word} 从变量结尾删除最短的 word
${变量%%word} 从变量结尾开始删除最长匹配的 word
${变量/pattern/string} 用 string 代替第一个匹配的 pattern
${变量//pattern/string) 用 string 代替所有的 pattern
# name="yuchao180'
# echo ${#name}
9
# 截取子串的用法
# echo ${name:3}
hao180
# echo ${name:5}
o180
#设置起点,以及元素长度
# echo ${name:2:4}
chao
• 统计命令执行速度
字符串长度统计方式这么多,谁最快?
time 命令,统计命令执行时长
for 循环的 shell 编程知识语法
for number in {1..100}
do
echo $number
done
一行式:for num in {1..100}; do echo $num; done
Shell编程尽量使用内置的指令,内置的操作,和内置的函数;它们由C语言开发,效率最高,尽可能的减少管道符的操作。
字符串截取
删除匹配到的子串
# name="I am chaoge"
# echo ${name:2:2}
am
# echo ${name:3:5}
m cha
# name2="abcABC123ABcabc"
# #从开头匹配字符删除
# echo ${name2#a*c}
ABC123ABCabc
# 利用%形式,从后向前匹配截取
# name2=abcABC123ABcabc
# echo ${name2%a*c}
abCABC123ABC
# echo ${name2%%a*c}
# echo ${name2%%a*C}
abcABC123ABCabc
# echo ${name2%a*C}
abcABC123ABCabc
替换字符串
# str1="Hello,man,i am your brother."
# echo $str1
Hello,man,i am your brother.
# echo ${str1/man/boy}
Hello,boy,i am your brother.
#多次匹配替换
# echo $str1
Hello,man,i am your brother.
# echo ${str1/o/0}
HellO,man,i am your brother.
# echo ${str1//o/O}
HellO,man,i am yOur brOther.
应用:批量修改文件名
创建一批文件
# touch freeoa_{1..5}_finished.jpg
特殊 Shell 扩展变量
如果parameter变量值为空,返回word字符串,赋值给result变量
result=${parameter:-word}
如果para变量为空,则word替代变量值,且返回其值
result=${parameter:=word}
如果para变量为空,word当作stderr输出,否则输出变量值
用于设置变量为空导致错误时,返回的错误信息
result=$fparameter:?word?
如果para变量为空,什么都不做,否则word返回
result=${parameter:+word}