计算机教育中缺失的一课-极简版
操作系统(Operating System)
操作系统的定义是一组主管并控制计算机操作、运用和运行硬件、软件资源和提供公共服务来组织用户交互的相互关联的系统软件程序,同时也是计算机系统的内核与基石。操作系统需要处理如管理与配置内存、决定系统资源供需的优先次序、控制输入与输出设备、操作网络与管理文件系统等基本事务。操作系统也提供一个让用户与系统交互的操作界面。
操作系统的类型非常多样,不同机器安装的操作系统可从简单到复杂,可从移动电话的嵌入式系统到超级计算机的大型操作系统。许多操作系统制造者对它涵盖范畴的定义也不尽一致,例如有些操作系统集成了图形用户界面,而有些仅使用命令行界面,将图形用户界面视为一种非必要的应用程序。
操作系统的历史
综观电脑之历史,操作系统与电脑硬件的发展息息相关。操作系统的本意是提供简单的工作排序能力,后为辅助更新更复杂的硬件设施而渐渐演化。从最早的批量模式开始,分时机制也随之出现,在多处理器时代来临时,操作系统也随之添加多处理器协调功能,甚至是分布式系统的协调功能
分时系统(英语:time-sharing)是计算机科学中对资源的一种共享方式,利用多道程序与多任务处理使多个用户可以同时使用一台计算机 分布式系统是一组电脑,透过网络相互连接传递消息与通信后并协调它们的行为而形成的系统。组件之间彼此进行交互以实现一个共同的目标。 总而言之,操作系统的历史就是一部解决电脑系统需求与问题的历史。
1980年代之前
第一部电脑并没有操作系统。这是由于早期电脑的建立方式(如同建造机械算盘)与性能不足以执行如此复杂的程序。但在1947年发明了晶体管,以及莫里斯·威尔克斯发明的微程序方法,使得电脑不再是机械设备,而是电子产品。系统管理工具以及简化硬件操作流程的程序很快就出现了,且成为操作系统的起源。 到了1960年代早期,商用电脑制造商制造了批处理系统,此系统可将工作的建置、调度以及执行序列化。此时,厂商为每一台不同型号的电脑创造不同的操作系统,因此为某台电脑而写的程序无法移植到其他电脑上执行,即使是同厂商的电脑也不行。 到了1964年,IBM 推出了型号为System/360的大型机,而它们都共享代号为OS/360的操作系统(而非每种产品都用量身订做的操作系统)。OS/360也包含另一个优点:永久贮存设备—硬盘的面世(IBM称为DASD)。 另一个关键是分时概念的建立:将大型机珍贵的时间资源适当分配到所有用户身上。分时也让用户有独占整部机器的感觉;而Multics的分时系统是此时众多新操作系统中实践此观念最成功的。
Multics,名称来自于多工信息与计算系统(英语:MULTiplexed Information and Computing System)的缩写,它是一套分时多工操作系统,是1964年由贝尔实验室、麻省理工学院及美国通用电气公司所共同参与研发,并安装在大型主机上。旨在连接1000部终端,支持300位用户同时上线。
1963年,通用电气与贝尔实验室合作建立的Multics操作系统,是激发1970年代众多操作系统建立的灵感来源,尤其是由AT&T贝尔实验室的丹尼斯·里奇与肯·汤普逊所建立的Unix系统
1980年代
1980年代,家用电脑开始普及。通常此时的电脑拥有8-bit处理器加上64KB存储器、显示器、键盘以及低音质喇叭。而80年代早期最著名的套装电脑为使用微处理器6510(6502芯片特别版)的Commodore C64。此电脑没有操作系统,而是以8KB只读存储器BIOS初始化彩色显示器、键盘以及软盘驱动器和打印机。它可用8KB只读存储器BASIC语言来直接操作BIOS,并依此撰写程序,大部分是游戏。此BASIC语言的解释器勉强可算是此电脑的操作系统,当然就没有内核或软硬件保护机制了。此电脑上的游戏大多跳过BIOS层次,直接控制硬件。
BASIC语言是1964年,美国数学家/计算机科学家 约翰.克米尼和Thomas E.Kurtz在FORTRAN语言的基础上创造的一种新语言,是一种适合初学者的人机交互式语言,BASIC是“Beginner’s All-purpose Symbolic Instruction Code”的首字母缩写。
随着时代和技术的发展,磁盘操作系统(Disk Operating System,DOS)因而诞生。此操作系统可以合并任意数量的扇区,因此可以在一张磁盘片上放置任意数量与大小的文件。文件之间以文件名来区别。IBM公司并没有很在意其电脑上的DOS,因此以向外部公司购买的方式获取操作系统。1980年微软公司获取了与IBM的合约,并且收购了一家公司出产的操作系统,在将之修改后以MS-DOS的名义出品,此操作系统可以直接让程序操作BIOS与文件系统。到了Intel-80286处理器的时代,才开始实现基本的存储设备保护措施。其后,MS-DOS成为了IBM PC上面最常用的操作系统(IBM自己也有推出DOS,称为IBM-DOS或PC-DOS)。MS-DOS的成功使得微软成为地球上最赚钱的公司之一。
而1980年代另一个崛起的操作系统异数是Mac OS,此操作系统紧紧与Mac电脑捆绑在一起。
1990年代
延续1980年代的竞争,1990年代出现了许多影响未来个人电脑市场深厚的操作系统。
经过许多失败的项目后,苹果于1997年发布新操作系统——Mac OS X的测试版,而后推出的正式版获取了巨大的成功。让原先失意离开苹果的史蒂夫·乔布斯风光再现
除了商业主流的操作系统外,从1980年代起在开放源代码的世界中,BSD系统也发展了非常久的一段时间,但在1990年代由于与AT&T的法律争端,使得远在芬兰赫尔辛基大学的另一股开源操作系统——Linux兴起。Linux内核是一个标准POSIX内核,其血缘可算是Unix家族的一支。Linux与BSD家族都搭配GNU项目所发展的应用程序,但是由于使用的许可证以及历史因素的作弄下,Linux获取了相当可观的开源操作系统市占率,而BSD则小得多。相较于MS-DOS的架构,Linux除了拥有傲人的可移植性(相较于Linux,MS-DOS只能运行在Intel CPU上),它也是一个分时多进程内核,以及良好的存储器空间管理(普通的进程不能访问内核区域的存储器)。想要访问任何非自己的存储器空间的进程只能透过系统调用来达成。一般进程是处于用户态(User mode)底下,而执行系统调用时会被切换成内核态(Kernel mode),所有的特殊指令只能在内核态执行,此措施让内核可以完美管理系统内部与外部设备,并且拒绝无权限的进程提出的请求。因此理论上任何应用程序执行时的错误,都不可能让系统崩潰。
另一方面,微软对于更强力的操作系统呼声的回应便是Windows NT于1993年的面世。
1983年开始微软就想要为MS-DOS建构一个图形化的操作系统应用程序,称为Windows(有人说这是比尔·盖茨被苹果的Lisa电脑上市所刺激)。一开始Windows并不是一个操作系统,只是一个应用程序,其背景还是纯MS-DOS系统,这是因为当时的BIOS设计以及MS-DOS的架构不甚良好之故。在1990年代初,微软与IBM的合作破裂,微软从OS/2(早期为命令行模式,后来成为一个技术很优秀但是曲高和寡的图形化操作系统)项目中抽身,并且在1993年7月27日推出Windows 3.1,一个以OS/2为基础的图形化操作系统。并在1995年8月15日推出Windows 95。这时的Windows系统依然是建立在MS-DOS的基础上,不过微软在这同时也在开发不依赖于DOS的NT系列Windows系统,并在后来完全放弃了DOS而转向NT作为Windows的基础。
现在
现代操作系统通常都有一个使用的绘图设备的图形用户界面(GUI),并附加如鼠标或触控面版等有别于键盘的输入设备。旧的OS或性能导向的服务器通常不会有如此亲切的接口,而是以命令行界面(CLI)加上键盘为输入设备。以上两种接口其实都是所谓的壳,其功能为接受并处理用户的指令(例如按下一按钮,或在命令提示列上键入指令)。
选择要安装的操作系统通常与其硬件架构有很大关系,只有Linux与BSD几乎可在所有硬件架构上执行,而Windows NT仅移植到了DEC Alpha与MIPS Magnum。在1990年代早期,个人电脑的选择就已被局限在Windows家族、类Unix家族以及Linux上,而以Linux及Mac OS X为最主要的另类选择,直至今日。
Shell
Shell 是什么?
如今的计算机有着多种多样的交互接口让我们可以进行指令的的输入,从炫酷的图像用户界面(GUI),语音输入甚至是 AR/VR 都已经无处不在。 这些交互接口可以覆盖 80% 的使用场景,但是它们也从根本上限制了您的操作方式——你不能点击一个不存在的按钮或者是用语音输入一个还没有被录入的指令。 为了充分利用计算机的能力,我们不得不回到最根本的方式,使用文字接口:Shell
Shell是一个英文单词,并不是缩写。通常我们不会将它翻译为中文,但如果一定要翻译的话,它应当被翻译为壳层。 Shell在计算机科学中指“为用户提供用户界面”的软件,通常指的是命令行界面的解析器,一般来说,Shell指的是操作系统中提供用来访问内核的程序。当然如今Shell也泛指为用户提供操作界面的程序,也就是用户和程序交互的层面。(与之相对应的是内核(Kernel),内核不提供和用户交互的功能)。
通常将Shell分为两类:命令行与图形界面,命令行壳层提供了一个命令行界面(CLI),而图形Shell提供了一个图形用户界面(GUI)
本节我们会使用 Bourne Again SHell 简称为bash,这是被最广泛使用的一种shell,他的语法和其他的shell都是类似的。
Shell 的历史
Shell有一段漫长的历史,他的发展同操作系统的发展密切相关,在上一节中我们已经讨论了操作系统的发展历史,现在我们从第一个UNIX Shell开始讨论shell的发展历史。
始于UNIX
Ken Thompson(肯 汤普逊 来自贝尔实验室)在 1971 年为 UNIX操作系统(最早的、广泛使用的、对现代操作系统产生深远影响的操作系统之一) 开发了第一个shell,称为 V6 shell(也可以叫做 Thompson shell)。与Multics上的shell类似,这个V6 shell(/bin/sh)在内核外运行,是一个独立的用户程序。
似于它在 Multics 中的前身,这个 shell(
/bin/sh
)是一个在内核外执行的独立用户程序。诸如通配(参数扩展的模式匹配,例如*.txt
)之类的概念是在一个名为glob
的单独的实用程序中实现的,就像用于计算条件表达式的if
命令一样。这种分离使 shell 变得更小,才不到 900 行的 C 源代码。shell 引入了紧凑的重定向(
<
、>
和>>
)和管道(|
或^
)语法,它们已经存在于现代 shell 中。你还可以找到对调用顺序命令(;
)和异步命令(&
)的支持。Thompson shell 缺少的是编写脚本的能力。它的唯一目的是作为一个交互式 shell(命令解释器)来调用命令和查看结果。
Bourne shell 前进一步
在 Thompson 发布 shell 六年后,1977 年,Stephen Bourne 发布了 Bourne shell,旨在解决Thompson shell 中的脚本限制。作为 Unix 系统的一部分,这是这个来自贝尔实验室的技术的自然演变。
Bourne shell 有两个主要目标:作为命令解释器以交互方式执行操作系统的命令,和用于脚本编程(编写可通过 shell 调用的可重用脚本)。除了替换 Thompson shell,Bourne shell 还提供了几个优于其前辈的优势。Bourne 将控制流、循环和变量引入脚本,提供了更具功能性的语言来(以交互式和非交互式)与操作系统交互。该 shell 还允许你使用 shell 脚本作为过滤器,为处理信号提供集成支持,但它缺乏定义函数的能力。最后,它结合了我们今天使用的许多功能,包括命令替换(使用后引号)和 HERE 文档(以在脚本中嵌入保留的字符串文字)。
Bourne 在某次采访中这样描述它:
最初的 shell (编程语言)不是一种真正的语言;它是一种记录 —— 一种从文件中线性执行命令序列的方法,唯一的控制流的原语是
GOTO
到一个标签。Ken Thompson 所编写的这个最初的 shell 的这些限制非常重要。例如,你无法简单地将命令脚本用作过滤器,因为命令文件本身是标准输入。而在过滤器中,标准输入是你从父进程继承的,不是命令文件。最初的 shell 很简单,但随着人们开始使用 Unix 进行应用程序开发和脚本编写,它就太有限了。它没有变量、它没有控制流,而且它的引用能力非常不足。
以自由软件来重新构思 Bourne Shell
在此之前,这个占主导地位的 shell 是由贝尔实验室拥有和管理的专有软件。幸运的话,你的大学可能有权访问 Unix shell。但这种限制性访问远非自由软件基金会(FSF)想要实现的世界。
Richard Stallman 和一群志同道合的开发人员那时正在编写所有的 Unix 功能,其带有可以在 GNU 许可证下免费获得的许可。其中一个开发人员的任务是制作一个 shell,那位开发人员是 Brian Fox。
而那时也恰逢人们在讨论 shell 标准是什么的时候。在这一历史背景和将来的竞争前景下,流行的 Bourne shell 被重新构想,并再次重生。
Bourne-Again SHell
自由软件的使命和竞争这两个催化剂使重制的 Bourne shell具有了生命。和之前不同的是,Fox 并没有把 shell 放到自己的名字之后命名,他专注于从 Unix 到自由软件的演变。于是这份被重制的Bourne shell被称为Bourne-Again SHell,简称为Bash
使用shell
我们以Ubuntu22.04上搭载的Bash为例,打开终端(快捷键是ctrl+shift+T
),您会看到这样的一个提示符,它一般看起来是这个样子的:
user@machine:~$
这是shell最主要的文本接口,它告诉你,你的用户名是user,主机名是machine
,你当前的工作目录(或者说你现在所在的位置)是~
,这表示”home”。$
符号表示您现在的身份不是root
用户。 在这个提示符中你可以输入命令
,命令最终会被shell解析,最简单的命令是执行一个程序:
taolin@tao-ubuntu:~$ date
2024年 11月 20日 星期三 23:58:23 CST
当然如果你在系统设置中用英文表示时间,这里显示的时间也会变成英文,Ubuntu自动识别了我的时区并将时间格式调整为为中文格式。
在这里,我们执行了date
这个程序,然后shell打印出了当前的日期和时间。然后,shell等待我们输入其他的命令。
我们可以在执行命令的同时向程序传递参数:
taolin@tao-ubuntu:~$ echo hello
hello
在这个例子中,我们让shell执行echo
,同时指定参数hello
。echo
程序将该参数打印出来。值得注意的事,shell基于空格分割命令进行解析,然后执行第一个单词代表的程序,并将后续的单词作为程序可以访问的参数。如果你需要传递的参数中包含空格,要么使用单引号或者双引号将其包裹起来,要么使用转义符号\
进行处理。
现在我们有一个小小的疑惑,shell是如何直到去哪里寻找date
或echo
的呢? 其实,类似于Python
或者Ruby
,shell是一个编程环境,所以它具备变量,条件,循环,函数。当你在shell中执行命令时,实际上是在执行一段shell可以解析的简短代码。 如果你要求shell执行某个指令,而该指令并不是shell自身所具备的编程关键字时,shell就会去查找环境变量$PATH
,它会列出shell接到某个命令时,进行程序搜索的路径:
# 我的路径就比较多,因为我的电脑不是新装的,我配过很多环境
taolin@tao-ubuntu:~$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin:/home/taolin/Software/Pycharm/bin:/home/taolin/.ubuntu-docker/bin/:/home/taolin/.fishros/bin/
taolin@tao-ubuntu:~$ which echo
/usr/bin/echo
taolin@tao-ubuntu:~$ /bin/echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/snap/bin:/home/taolin/Software/Pycharm/bin:/home/taolin/.ubuntu-docker/bin/:/home/taolin/.fishros/bin/
当我们执行echo
命令时,shell了解到需要执行echo
这个程序,随后它便在$PATH
中搜索由:分割的一系列目录,基于名字搜索该程序,当找到该程序时便执行。 确定某个程序名代表的是哪个具体的程序可以用which
,我们也可以绕过$PATH
,通过直接指定所需要的执行的程序的路径来执行该程序。
在shell中导航
shell中的路径是一组被分割的目录,在Linux和macOS上使用/
进行分割而在Windows上使用\
进行分割。 路径/
代表的是系统的根目录,所有的文件都在这个路径之下,在Windows上每个盘都又一个根目录(例如C盘的根目录C:\ ) 在Linux系统上,如果某个路径以/
开头,那么它是一个绝对路径,反之则是相对路径,相对路径是指相对于当前工作目录的路径。 当前工作目录可以使用pwd
来获取,此外,切换目录使用cd
命令。 在路径中,.
表示的是当前目录而..
表示的是上一级目录:
taolin@tao-ubuntu:~$ pwd
/home/taolin
taolin@tao-ubuntu:~$ cd /home/
taolin@tao-ubuntu:/home$ pwd
/home
taolin@tao-ubuntu:/home$ cd ..
taolin@tao-ubuntu:/$ pwd
/
taolin@tao-ubuntu:/$ cd ./home
taolin@tao-ubuntu:/home$ pwd
/home
taolin@tao-ubuntu:/home$ cd taolin
taolin@tao-ubuntu:~$ pwd
/home/taolin
taolin@tao-ubuntu:~$ ../../bin/echo hello
hello
一般而言,如果我们在运行一个程序时没有指定路径,则程序会在当前路径下执行。例如我们常常会搜索文件,并在需要时创建文件。 为了查看目录下包含那些文件,我们会使用ls
命令:
taolin@tao-ubuntu:~$ ls
'2024-11-16 22-32-39.mkv' Documents Pictures Software
'2024-11-16 22-33-42.mkv' Downloads Public Templates
Desktop Music snap Videos
taolin@tao-ubuntu:~$ cd /home
taolin@tao-ubuntu:/home$ ls
taolin
taolin@tao-ubuntu:/home$ cd ..
taolin@tao-ubuntu:/$ ls
bin dev lib libx32 mnt root snap sys var
boot etc lib32 lost+found opt run srv tmp
cdrom home lib64 media proc sbin swapfile usr
除非我们利用第一个参数指定目录,否则ls
会打印当前目录下的所有文件。大多数的命令接受标记和选项(带有值的标记),他们以-
开头,可以改变程序的行为。 通常,在执行程序时使用-h
或者--help
可以打印帮助信息,以便了解程序有那些可用的标记或选项。例如
ls --help
# 然后打印出一大堆东西,这里挑两个常用的
-a, --all do not ignore entries starting with
-l use a long listing format
它告诉我们,在执行ls
时追加-a
标记将会不忽略隐藏文件,将目录下所有的文件都显示出来;追加-l
标记会用长参数格式显示每个文件或文件夹的参数。
taolin@tao-ubuntu:~$ ls -a
. Desktop .npm .steampath
.. Documents .picgo .steampid
'2024-11-16 22-32-39.mkv' .dotnet Pictures .sudo_as_admin_successful
'2024-11-16 22-33-42.mkv' Downloads .pki .sys1og.conf
.bash_history .fishros .profile Templates
.bash_logout .gitconfig Public .ubuntu-docker
.bashrc .gnupg snap Videos
.cache .java Software .vscode
.ccache .local .ssh .xwechat
.config Music .steam
taolin@tao-ubuntu:~$ cd /home
taolin@tao-ubuntu:/home$ ls -l
drwxr-xrw- 28 taolin taolin 4096 11月 20 23:11 taolin
在ls -a
命令执行后,隐藏的文件或文件夹(通常他们以.
开头)都会被显示;在ls -l
命令执行后,目录下非隐藏文件或文件夹的信息会被更加下详细地列出来。 首先,本行的第一个字符d
表示taolin
是一个文件夹。然后接下来的9个字符rwxr-xrw-
,每三个为一组,即rwx r-x rw-
。 他们分别代表了文件所有者(也就是创建这个文件夹的用户),用户组(users),和其他所有人所具有的权限。其中r
表示读取
权限,w
表示修改
权限,x
表示可执行
权限(通常对某文件夹下文件的搜素权限也在其中),-
表示用户不具有相应的权限。对于文件来说,权限的含义与文件夹是一致的。 最后我们谈一探如何快速上手某个未知的程序,如果您想要知道关于程序参数、输入输出的信息,亦或是想要了解它们的工作方式,请试试man
这个程序。它会接受一个程序名作为参数,然后将它的文档(用户手册)展现给您。注意,使用 q
可以退出该程序。 就像这样,使用man
展示ls
命令的用户手册
taolin@tao-ubuntu:/home$ man ls
在程序中创建连接
在shell当中,程序有两个主要的”流”:输入流和输出流。当程序尝试读取信息时,它们会从输入流中进行读取;当程序打印信息时,它们会将信息输出到输出流中。通常,一个程序的输入输出流都是你的终端。也就是说通常将你的键盘作为输入,显示器作为输出。 但是,我们也可以重定向
这些流。 最简单地重定向是< file
和> file
。这两个命令将程序的输入输出流分别重定向到文件file
,举例如下:
taolin@tao-ubuntu:~/Documents/Draf$ echo hello > hello.txt
taolin@tao-ubuntu:~/Documents/Draf$ cat hello.txt
hello
taolin@tao-ubuntu:~/Documents/Draf$ cat < hello.txt
hello
taolin@tao-ubuntu:~/Documents/Draf$ cat < hello.txt > hello2.txt
taolin@tao-ubuntu:~/Documents/Draf$ cat hello2.txt
hello
我们使用echo
命令输出hello,使用>
将输出流重定义到文件hello.txt
,当然如果不存在这个文件,则会新建这个文件。cat
命令从指定的文件中获取输入,并打印到输出流中,在未重定向的情况下,会打印到默认的输出流也就是屏幕中。我们使用<
重定向cat
的输入流,>
重定向cat
的输出流为hello2.txt
,最后使用cat
查看hello2.txt
有没有写入hello.txt
同样的内容。 要值得注意的是>
会覆盖文件原先存在的内容:
taolin@tao-ubuntu:~/Documents/Draf$ echo hello > hello.txt
taolin@tao-ubuntu:~/Documents/Draf$ cat hello.txt
hello
taolin@tao-ubuntu:~/Documents/Draf$ echo world > hello.txt
taolin@tao-ubuntu:~/Documents/Draf$ cat hello.txt
world
为了防止原先文件的内容被覆改掉,我们可以使用>>
来向文件追加内容
taolin@tao-ubuntu:~/Documents/Draf$ echo hello > hello.txt
taolin@tao-ubuntu:~/Documents/Draf$ echo world >> hello.txt
taolin@tao-ubuntu:~/Documents/Draf$ cat hello.txt
hello
world
使用管道(pipes)
,我们能够更好的利用文件重定向。 |
操作符允许我们将一个程序的输出和另外一个程序的输入连接起来。
Shell的工具和脚本
到目前未知,我们已经知道了如何在shell中执行命令,并使用各种方式将命令组合起来使用。但是,很多情况下我们需要执行一系列的操作并使用条件或循环这样的控制流。
赋值语法,变量定义
首先我们要讨论的是赋值
操作,在bash中赋值的语法是foo=bar
,访问变量中存储的数值,其语法为$foo
。
需要注意的是,由于bash基于空格分割,因此
foo = bar
是无法正常工作的,因为解释器会调用foo
程序,并将=
和bar
都作为foo
的参数。 Bash中的字符串通过'
和"
分隔符来定义,他们的含义并不相同,以'
定义的字符串被称为原义字符串,其中的变量不会被转义,而"
定义的字符串会将变量值进行替换
taolin@tao-ubuntu:~/Documents/Draf$ foo=bar
taolin@tao-ubuntu:~/Documents/Draf$ foo = bar
Command 'foo' not found, did you mean:
command 'roo' from snap roo (2.0.3)
……
See 'snap info <snapname>' for additional versions.
taolin@tao-ubuntu:~/Documents/Draf$ echo '$foo'
$foo
taolin@tao-ubuntu:~/Documents/Draf$ echo "$foo"
bar
控制流
和大多数的编程语言一样,bash
也支持if
,case,
while和
for这些控制流关键字。同样的
bash也支持函数,它可以接受参数并基于参数进行操作。 书写bash的函数有两种方法,一种是直接在终端中书写,适合体积小,复杂程度低,复用度少的函数;另一种是将函数写入到
.sh文件中,
.sh文件也被称做脚本文件。 直接在终端中写函数,首先在第一行输入
函数名()然后终端会进入函数的编辑模式,在输入最后一个
}`后终端会退出编辑模式,这时候就可以调用函数观察结果了,比如:
taolin@tao-ubuntu:~$ hello() {
> echo hello
> }
taolin@tao-ubuntu:~$ hello
hello
另一种方式,将函数写入到.sh
文件中:
#!/bin/bash
hello(){
echo hello
}
这种方式需要确保脚本文件可执行,通常会使用ls -l
来确定文件是否具有可执行权限x
,如果没有的话,可以选择执行下面的代码
sudo chmod +x filename
# 或者是
sudo chmod +777 filename
后一种会给文件所有人可读可写可执行的权限
我们在这里并没有提到更多的函数用法,比如函数使用的特殊变量,命令码与错误码,退出码,以及更多的内容…… 如果你对它感兴趣,可以点击Shell工具与脚本-讲义
编辑器Vim
程序员们通常对于自己的文本编辑器有着非常强的执念
,比如浏览B战,知乎,掘金,csdn等各大社区,你会看到各种编辑器的对比,现如今可以列举的编辑器数不胜数,知名的如Visual Studio Code
, Visual Studio
, Atom
,Pycharm
…… 根据2019年Stack Overflow 的调查,Visual Studio Code 是目前最流行的代码编辑器。而 Vim 则是最流行的基于命令行的编辑器。
遗憾的是Vim的作者在2023年的8月5日去世了
bashvim Bram Moolenaar i Rest in peace :wq
为什么要使用Vim
坦白的来说,如果想要让Vim达到像Vscode一样开箱即用的效果,是非常难,需要花费大量的时间,并却会让同学们感到痛苦的一环。我们使用Vim主要是在这几个环节当中:
- 在机器人端侧,快速的修改某些配置文件(vim的启动时间要远远小于vscode,因为vscode是基于GUI图形界面的,而vim是基于命令行的)
- 在云端服务器侧,修改某些配置文件。一般我们使用的云服务器是不会配置GUI图形界面的,当然你也可以使用ssh的方式用vscode开发,不过那就是后话了
- 在我这几年的经历中,没了。
Vim快速上手
Vim的设计以大多数时间都花费在阅读,浏览和进行少量的代码修改为基础,因此Vim具有多种的操作模式:
- 正常模式:也是你打开Vim后进入的模式,在文件中四处移动光标,浏览代码
- 插入模式:插入文本
- 替换模式:替换文本
- 可视化模式:像鼠标长按左键选择大段的文本一样,选择文本块
- 命令模式:用于执行命令
在不同的模式下,键盘敲击的含义也不同,比如x
在插入模式下会插入字母x
,但是在正常模式下会删除光标所在的字母,可视化模式下则会删除选中的文本块。
插入文本
在正常模式下(也就是Vim启动后进入的模式),键入i
进入插入模式。现在Vim就跟很多的其他编辑器一样了,正常的键入字符,删除,移动光标。直到你键入esc
返回正常模式。
缓存,标签页,窗口
Vim 会维护一系列打开的文件,称为“缓存”。一个 Vim 会话包含一系列标签页,每个标签页包含 一系列窗口(分隔面板)。每个窗口显示一个缓存。跟网页浏览器等其他你熟悉的程序不一样的是, 缓存和窗口不是一一对应的关系;窗口只是视角。一个缓存可以在 多个 窗口打开,甚至在同一 个标签页内的多个窗口打开。这个功能其实很好用,比如在查看同一个文件的不同部分的时候。
Vim 默认打开一个标签页,这个标签也包含一个窗口。
命令行
在正常模式下键入:
进入命令模式,在键入:
后你的光标会立即的跳到屏幕下发的命令行,这个模式有很多的功能能,包括打开,保存,关闭,以及退出Vim
- :q 退出(关闭窗口)
- :w 保存
- :wq 保存然后退出
- :e{} edit fil 打开要编辑的文件
- :ls 显示打开的缓存
- :help {} 打开帮助文档
- :help :w 打开
:w
命令的文档 - :help w 打开
w
命令的文档
- :help :w 打开
选择
选择需要在可视化模式下才能进行,在正常模式下,键入v
进入可视化模式
- v 可视化
- V 可视化行
- ctrl+v 可视化块
复制与粘贴
在可视化模式下,键入y
进行复制,p
进行粘贴
版本控制Git
版本控制系统,是一类用于追踪源代码(或者其他文件,文件夹)改动的工具。顾名思义,这些工具可以帮助我们管理代码的修改历史;不仅如此,它还可以让协作编码变得更方便。(其实我们更在意的是后者)。 现代的版本控制系统可以帮助您轻松地(甚至自动地)回答以下问题:
- 当前模块是谁编写的?
- 这个文件的这一行是什么时候被编辑的?是谁作出的修改?修改原因是什么呢?
- 最近的 1000 个版本中,何时/为什么导致了单元测试失败?
尽管版本控制系统有很多, 其事实上的标准则是 Git
当然Git的接口是有一些丑陋了,但是他的底层设计思想是非常优雅的。丑陋的接口只能通过死记硬背(打熟练就好了),而优雅的底层却能非常容易的被人理解。 关于Git的资料和教程有很多,介绍Git底层的资料也有很多,我不愿在这里多写了,一是写不动了,二是因为了解Git的底层,对于后面的工作,意义不大。
Git快速上手
Git是直接记录快照的
Git 和其它版本控制系统(包括 Subversion 和近似工具)的主要差别在于 Git 对待数据的方式。 从概念上来说,其它大部分系统以文件变更列表的方式存储信息,这类系统(CVS、Subversion、Perforce 等等) 将它们存储的信息看作是一组基本文件和每个文件随时间逐步累积的差异 (它们通常称作 基于差异(delta-based)的版本控制)。 Git 不按照以上方式对待或保存数据。反之,Git 更像是把数据看作是对小型文件系统的一系列快照。 在 Git 中,每当你提交更新或保存项目状态时,它基本上就会对当时的全部文件创建一个快照并保存这个快照的索引。 为了效率,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 快照流。
Git的三种状态
Git有三种状态(当然这个状态是针对被Git记录的文件而言的),你的文件可能处于其中之一:已提交(committed)、已修改(modified) 和 已暂存(staged)。
- 已提交:数据已经安全的保存在了本地的数据库中(你也可以推到云端数据库,如gitee或者是github)
- 已修改:修改了文件,但是还没有保存到数据库中
- 已暂存:对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
这会让我们的 Git 项目拥有三个阶段:工作区、暂存区以及 Git 目录。
- 工作区是对项目的某个版本独立提取出来的内容。 这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。
- 暂存区是一个文件,保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。 按照 Git 的术语叫做“索引”,不过一般说法还是叫“暂存区”。
- Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,复制的就是这里的数据。
Git的基本工作流程
- 你在工作区修改文件
- 将你想要提交的更改选择性的暂存,这样只会将更改的部分添加到暂存区
- 提交更新,Git会找到暂存区的文件,将快照永久性存储到Git仓库目录
安装Git
以Ubuntu为例,安装命令行
sudo apt install git-all
Github Desktop
Github Desktio 文档 其实我个人更建议大家安装Github Desktio,GitHub Desktop 是由github推出的免费的开放源代码应用程序,可帮助处理托管在 GitHub 或其他 Git 托管服务(比如gitee)上的文件。
关于Github Desktop的优点
- 入门简单:不熟悉 Git 和 GitHub 的用户可能会发现,使用 GitHub Desktop 比在命令行上使用 Git 更轻松。 GitHub Desktop 提供了图形用户界面,可简化命令并帮助可视化显示更改
- 查找命令:由于 GitHub Desktop 提供了可视化界面,因此可以轻松访问不太常用的 Git 命令,例如选择要包含在提交中的更改行或将共同作者添加到提交中,而无需记住或查找语法。
- 与Github集成度高: 这一点在后面拉取飞行控制器PX4的代码仓库时会体现出来,使用Github Desktop将会减少2-3天的踩坑时间
Ubuntu安装Github Desktop
虽然Github Desktop官方只为Windows和MacOS提供了支持,但是有社区开源大佬提供了对Linux的支持:
# UPDATE (2024-01-24)
## Direct copy-paste from official instrubtions
## Github Desktop for Ubuntu
## Get the @shiftkey package feed
wget -qO - https://apt.packages.shiftkey.dev/gpg.key | gpg --dearmor | sudo tee /usr/share/keyrings/shiftkey-packages.gpg > /dev/null
sudo sh -c 'echo "deb [arch=amd64 signed-by=/usr/share/keyrings/shiftkey-packages.gpg] https://apt.packages.shiftkey.dev/ubuntu/ any main" > /etc/apt/sources.list.d/shiftkey-packages.list'
## Install Github Desktop for Ubuntu
sudo apt update && sudo apt install github-desktop
配置Git
假如你安装的是Github Desktop,你需要注册一个github帐号,并在浏览器中登录,然后点击启动Github Desktop,接着到浏览器中授权登录即可,本小节剩下的都不需要再看。
用户信息
假如你安装的是Git,要做的第一件事就是设置你的用户名和邮件地址。这一点很重要,因为每一个 Git 提交都会使用这些信息,它们会写入到你的每一次提交中,不可更改:
$ git config --global user.name "username"
$ git config --global user.email user@example.com
再次强调,如果使用了 —global 选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git 都会使用那些信息。 当你想针对特定项目使用不同的用户名称与邮件地址时,可以在那个项目目录下运行没有 —global 选项的命令来配置。
文本编辑器
既然用户信息已经设置完毕,你可以配置默认文本编辑器了,当 Git 需要你输入信息时会调用它。 如果未配置,Git 会使用操作系统默认的文本编辑器(在Ubuntu上或许是vi ?)。
拉取一个仓库
使用git最常见的操作是拉取一个github(或者是gitee)上的仓库,下面的命令(应该执行不了)会拉取cloudhub.com(我随便打得)代码托管网站上的一个repositories仓库,拉取到当前目录下。
大多数同学,拉取以后就不会进行本地提交了,也就是不做版本管理,需要某一版特定的了,就直接全部打包。恩,很难说这有什么坏处,毕竟版本管理这个流程,本身就适合于多人大项目开发,对于个人来说,直接对代码仓库打个压缩包重命名以下,也是很有效率的操作。
git clone http://cloudhub.com/repositories.git
完结 撒花
计算机教育中缺失的一课-极简版 到此结束! OVO 完结,撒花 。:.゚ヽ(*´∀`)ノ゚.:。