关于终端、Shell、Bash以及环境变量那点事
在实际工作中,终端(terminal)、Shell、bash等概念都模糊化了,其实也并没有引起太大的歧义,本文对上述概念做一个梳理归纳
同时对环境变量的一些概念再进一步总结
1 终端Terminal
先举个栗子!
我通过我个人笔记本的Xshell
这款终端软件,利用SSH协议远程登录了阿里云一台ECS主机
注意,Xshell名字里虽然有Shell,但它就是是一个Terminal,而不是第一个Shell
终端软件是一款具有图形界面的管理软件,是一种文本的输入和输出环境,我们输入的ls
命令具体是怎么工作的?
先回答第一个问题,终端、Shell、Bash到底有什么区别?
1.1 电传打印机teletypewriter
先从计算机的历史演变开始,最早的计算机非常昂贵且提及庞大
有一段时间,输出输出是靠一种电传打印机输入以及打印在纸上的,例如teletypewriter
电传打印机,如下图
可以看出来,电传打印机设备由一个键盘和一个打印机组成,用户通过键盘输入命令,计算机将结果打印在打印机上
这里就是一个很重要的一个概念,TTY(teletypewriter )设备
,显而易见通过电传打印机这种tty设备,输入输出的时间和成本代价是极其高昂的
注意,tty设备特指的是硬件,我们在Linux系统还会看到类似tty和pts等概念,后续再说
1.2 终端设备
重要分割线,此时终端设备
上场
正如前述所说,最早的计算机非常昂贵且提及庞大,都会被安置在单独的房间,而用户会在其他房间通过其他类型的设备进行连接,这里的连接是指专用的电线,这些其他类型设备就是终端设备
DEC VT100
就是当时著名的终端设备型号
原先TTY设备
被不断取代,一个很明显的变化就是:电子显示单元取代了传统电子打印机
另外这里的硬件终端设备
都是专用设备,并没有采用CPU,而是采用了逻辑门等芯片
注意这里我特指了
硬件终端设备
,随着个人电脑的普及,软件终端即将步入历史舞台
1.3 终端模拟器
现代Linux的内核都保留了终端模拟器
和tty的驱动
1、硬件键盘设备通过内核的键盘驱动,再通过终端模拟器
向tty驱动
模拟发出输入信号
2、终端模拟器
同时从tty驱动
接收到输出信号,再通过显示器驱动,显示在最终的物理显示器上
3、这里的终端模拟器
侦听来自键盘的事件并将其发送给tty驱动
,不同之处在于没有物理设备或电缆连接到 TTY 驱动
程序上它负责向内核tty驱动
模拟tty设备
4、终端模拟器存在内核态和用户态之分,内核也提供了终端模拟器叫做虚拟终端,而用户态提供的终端模拟器,就是桌面环境提供的各种终端软件,例如Ubuntu GNOMEE Terminal
,这点很重要,可以详细看《虚拟控制台和伪终端》章节
这里中间再插播一下有关于终端和控制台的区别,在实际工作中不必区分这么明显
终端和控制台的区别在于
1、控制台严格意义来说就是物理设备,它不需要通过串口线等物理连接方式连接到计算机上的,它本身就是计算机的一部分。一个终端可以有多个,例如多个用户通过终端软件同时连接到一台计算机上
2、严格意义来说,一台计算机只有一台控制台,但可以有多个终端
可以看出来这里的终端(Terminal)
是一个物理设备的概念逐步向软件模拟的概念演进的过程
在没有shell之前,输入输出设备通过终端模拟器
只能和应用进程进行交互,但是无法对内核进行交互式的处理,没办法去理解命令行的意义
了解shell之前,先记住一个很重要概念,shell是由终端进行调用,是运行在终端之内的
1.4 虚拟控制台和伪终端
接下来我们重新看看最开始的栗子!
通过Xshell
这款终端软件通过SSH协议远程登录阿里云一台ECS主机
此时我们在本机的Xshell终端输入tty
和who
命令,返回如下内容
1 | [root@localhost ~]# tty |
通过
tty命令
可以知道当前具体是什么终端
who
命令可以显示当前登录的用户名、终端、登录时间等信息
注意,时间2024-04-29 16:10
后面的IP地址就是我本地用于远程SSH登录电脑的公网地址
但是如果我们通过本地控制台或者类似vSphere Web控制台进去后,出现如下欢迎界面【实际是这就是一个虚拟终端tty1】
此时我们再次输入tty
和who
命令,返回如下内容
1 | [root@localhost ~]# tty |
注意对比之前同样命令的返回结果的区别
1、前者``tty命令返回了
/dev/pts/0后者返回了
/dev/tty1`
2、前者who
命令返回了pts/0
,后者返回了tty1
3、后者who
命令没有远程登录的IP地址信息了,说明这是一个本地的登录结果
总结
1、tty0-6
代表的就是虚拟终端,是由Linux kernel提供的虚拟控制台或者叫做虚拟终端
拿最容易理解的场景解释下:
我们都知道Linux6个用户界面,包括1个图形界面,5个字符界面 ,可以靠ctrl+alt+fn功能键
进行切换
其中ctrl+alt+F2
到ctrl+alt+F6
代表了5个字符的虚拟终端,这些虚拟终端都是内核提供的,他们都位于内核态运行,例如下图
内核提供的虚拟终端,需要使用键盘/鼠标直接连接,或者使用类似vSphere Web控制台这种模拟的本地控制台进行操作
2、pty终端
全称就是pseudoterminal
即伪终端的意思,它与tty终端本质区别,在于pty终端
是一个虚拟的,是运行在用户态的,tty终端
运行在内核态
而我们远程连接到主机上时,当用户通过SSH等远程登录方式登录到Linux系统时,会在服务器上创建一个伪终端设备,用于与用户进行交互,才会显示root pts/0 2024-04-29 16:10 (114.84.238.103)
这样的信息
在启动图形化界面的Linux桌面环境下,使用ctrl+alt+F1
从字符界面切换到代表图形界面,这个图形界面下GUI终端就是是位于用户态
可以简单理解,是在GUI桌面环境下产生的伪终端
概念
1.4.1 pty master-slave模型
伪终端有一个很重要的概念就是master-slave模型
1、当我们启动GNOME Terminal软件,进程就会打开/dev/ptmx获得一个字符描述符
2、然后在/dev/pts目录下创建序列文件
3、GNOME Terminal进程fork一个shell进程,例如Bash
4、我们使用键盘输入ls两个字符,由内核态的键盘驱动输出至GUI系统,由GUI系统输出至GNOME Terminal
5、GNOME Terminal会侦听键盘事件,然后将ls字符到获得的PTY master
6、PTY master输入字符发送到缓冲区,同时缓冲区也会发送回PTY master,交由终端模拟器通过显示驱动,显示在物理显示器上
7、Line Discilpine会进行字符检测,例如发现回车键时,就会直接将字符发送到PTY slave
8、Bash的标准输入是PTY slave
9、shell负责解析命令,当解析到ls命令时,是一个内置的命令,就会fork一个ls进程
10、ls进程执行完后输出值PTY slave
11、PTY slave输出值缓冲区
12、缓冲区再输出至pty master,由后者再输出至终端模拟器,由后者通过显示驱动显示你在物理显示器上
1.4.2 虚拟终端和伪终端互相通讯的试验
为了进一步验证pty master-slave模型
,我们做一个虚拟终端和伪终端的通讯试验
在linux里,一切皆文件,在这个试验里/dev/pts/0
就是对应通过who
命令显示的第二个远程客户pts/0
我们在本地tty1
终端里,输入ehco 'hello' > /dev/pts/0
就可以将
实际上,如果你感兴趣的话,你完全可以通过两个伪终端进行试验,
pts/0
和pts/1
2 Shell
终端是一个可以调用shell的程序,它可以接受字符流的输入和输出用于人类可读取的方式显示出来
2.1 基本概念
操作系统包括内核(kernel)态和用户态,内核态和用户态的设计确保了操作系统的稳定性和安全性
内核不提供和用户的交互功能,可以使用shell这一层翻译官
shell为用户提供用户界面的软件,通常指的是命令行界面的解析器,负责用户层和内核之间的交互工作
shell的核心点就是命令行界面的解析器
在Linux环境下,可以记住两个经典的shell实现
1、Sh就是最经典的Unix shell,全称是Bourne Shell
2、Bash是shell的最常用的一种,Bash全称是Bourne-Again Shell
除了Bash以外,还有更多的其他shell,例如zsh
、fish
等
可以这么认为,
shell
是一个统称,而bash
、zsh
、fish
才是具体shell的具体实现
在Windows环境下,操作系统也提供了shell的功能
1、Windows 95 / 98下的command.exe
2、Windows NT内核下的cmd
以及PowerShell
3、而图形界面壳层即为explorer.exe
记住之前反复提及的一个概念:shell是由终端进行调用,是运行在终端之内的
2.2 内部命令和外部命令
再次强调,shell负责命令的解析
内置命令(builtin command) 是shell解释程序内建的,有shell直接执行,不需要派生新的进程。有一些内部命令可以用来改变当前的shell环境
对于外部命令,shell会创建一个新的进程来执行命令,当命令的进程运行时,默认shell将等待直到该进程结束
常见的外部命令有:grep、more、cat、mkdir、rmdir、ls、sort、ftp、telnet、ssh、ps等
例如我们在《1.4.1 pty master-slave模型 》看到当bash检测到ls字符时,就会form一个ls进程
我们可以使用type
命令去查看是否为内部或者外部命令
1 | [root@localhost ~]# type echo |
2.3 查看当前shell
要查看当前使用的shell,你可以使用echo $0
或者echo $SHELL
命令。
在终端中执行以下任一命令,可以看出当前shell是由/bin/bash
这个具体程序负责用户态和内核态在命令交互的翻译工作
1 | [root@localhost ~]# echo $SHELL |
注意SHELL一定要是大写
查看当前用户可以使用的shell
1 | [root@localhost ~]# cat /etc/shells |
或者使用chsh -l
命令
1 | [root@localhost ~]# chsh -l |
2.3 切换下一次使用的shell
切换shell通常指的是在同一个终端会话中更换使用的shell程序
在Linux系统中,这可以通过输入不同的shell命令来完成。例如,如果你当前使用的是bash shell,你可以切换到其他shell,如zsh或者fish
注意
1、切换shell必须使用
-s
参数2、下次重启shell才能使用
1 | [root@localhost ~]# chsh -s /bin/zsh |
切换shell背后的原理其实很简单
chsh -s
其实修改的就是/etc/passwd
文件里和你的用户名相对应的那一行,现在来查看下
1 | [root@ethan ~]# cat /etc/passwd | grep zsh |
使用chsh加选项-s
就可以修改登录的shell了!你会发现你现在执行echo $SHELL
后仍然输出为/bin/bash
,这是因为你需要重启你的shell才完全投入到zsh怀抱中去
3 环境变量
Linux shell在执行命令过程中,有一个绕不开的概念就是Linux环境变量
而且Linux shell还存在交互式和非交互式的概念
Linux环境变量是一个存储在系统中的特殊变量,它为用户在Shell中执行命令时提供了一些有用的信息
例如,PATH
变量包含了一系列目录,Shell会在这些目录中查找可执行文件
3.1 环境变量的分类
按照生命周期来分:
1、永久的:需要用户修改相关的配置文件,变量永久生效
2、临时的:用户利用export
命令,在当前终端下声明环境变量,关闭Shell终端失效
按照作用域来分:
1、系统环境变量:系统环境变量对该系统中所有用户都有效
2、用户环境变量:顾名思义,这种类型的环境变量只对特定的用户有效
3.2 交互式和非交互式shell
交互式shell包括登录和非登录两种shell
交互式登录 Shell
指需要用户名、密码登录后才能进入的 shell,例如:通过 ssh 或本地远程登录到终端,还有就是使用 --login
选项启动 Bash
交互式非登录 shell
指不需要用户名和密码即可打开的 shell,例如:直接命令 “bash” 就是打开一个新的非登录 shell,或者在 Linux Gnome 桌面环境打开一个“终端(terminal)”窗口也是一个非登录 shell
例如我们通过vim,在~/.bashrc
末尾一句添加echo 打开一个bash
然后直接执行一个bash命令,打开一个子shell进程
1 | [root@localhost ~]# bash |
实际上我们发现~/.bashrc
中就放alias别名设置
1 | alias rm='rm -i' |
交互式非登录 shell
指不与终端关联,一般是执行一个命令、一个脚本的 shell ,通常不需要人工干预,例如系统维护脚本、后台脚本
3.3 su命令是否带-的区别
使用su命令切换用户时,不会改变当前的环境变量,即仍然保持原用户的环境变量
而su -命令则会改变为切换到用户的环境变量,获得新用户的环境变量及执行权限
如图所示
su -
命令会按优先级顺序执行
su
命令则只会按照交互式nologin
方式进行加载环境变量配置文件
3.4 修改环境变量的最佳实践
Linux 发行版更新的时候,会更新 /etc 里面的文件,比如 /etc/profile,因此建议不要直接修改这个文件
如果想修改所有用户的环境参数,建议在 /etc/profile.d
目录里面新建.sh
脚本,如果想修改个人登录环境,一般是写在 ~/.bashrc
里面