0%

关于终端、Shell、Bash以及环境变量那点事

关于终端、Shell、Bash以及环境变量那点事

在实际工作中,终端(terminal)、Shell、bash等概念都模糊化了,其实也并没有引起太大的歧义,本文对上述概念做一个梳理归纳

同时对环境变量的一些概念再进一步总结

1 终端Terminal

先举个栗子!

我通过我个人笔记本的Xshell这款终端软件,利用SSH协议远程登录了阿里云一台ECS主机

注意,Xshell名字里虽然有Shell,但它就是是一个Terminal,而不是第一个Shell

image-20240429131745167

终端软件是一款具有图形界面的管理软件,是一种文本的输入和输出环境,我们输入的ls命令具体是怎么工作的?

先回答第一个问题,终端、Shell、Bash到底有什么区别?

1.1 电传打印机teletypewriter

先从计算机的历史演变开始,最早的计算机非常昂贵且提及庞大

有一段时间,输出输出是靠一种电传打印机输入以及打印在纸上的,例如teletypewriter 电传打印机,如下图

image-20240429135210535

可以看出来,电传打印机设备由一个键盘和一个打印机组成,用户通过键盘输入命令,计算机将结果打印在打印机上

这里就是一个很重要的一个概念,TTY(teletypewriter )设备,显而易见通过电传打印机这种tty设备,输入输出的时间和成本代价是极其高昂的

注意,tty设备特指的是硬件,我们在Linux系统还会看到类似tty和pts等概念,后续再说

1.2 终端设备

重要分割线,此时终端设备上场

正如前述所说,最早的计算机非常昂贵且提及庞大,都会被安置在单独的房间,而用户会在其他房间通过其他类型的设备进行连接,这里的连接是指专用的电线,这些其他类型设备就是终端设备

DEC VT100就是当时著名的终端设备型号

image-20240429134541317

原先TTY设备被不断取代,一个很明显的变化就是:电子显示单元取代了传统电子打印机

另外这里的硬件终端设备都是专用设备,并没有采用CPU,而是采用了逻辑门等芯片

注意这里我特指了硬件终端设备,随着个人电脑的普及,软件终端即将步入历史舞台

1.3 终端模拟器

现代Linux的内核都保留了终端模拟器tty的驱动

1、硬件键盘设备通过内核的键盘驱动,再通过终端模拟器tty驱动模拟发出输入信号

2、终端模拟器同时从tty驱动接收到输出信号,再通过显示器驱动,显示在最终的物理显示器上

3、这里的终端模拟器侦听来自键盘的事件并将其发送给tty驱动,不同之处在于没有物理设备或电缆连接到 TTY 驱动程序上它负责向内核tty驱动模拟tty设备

4、终端模拟器存在内核态和用户态之分,内核也提供了终端模拟器叫做虚拟终端,而用户态提供的终端模拟器,就是桌面环境提供的各种终端软件,例如Ubuntu GNOMEE Terminal,这点很重要,可以详细看《虚拟控制台和伪终端》章节

这里中间再插播一下有关于终端和控制台的区别,在实际工作中不必区分这么明显

终端和控制台的区别在于

1、控制台严格意义来说就是物理设备,它不需要通过串口线等物理连接方式连接到计算机上的,它本身就是计算机的一部分。一个终端可以有多个,例如多个用户通过终端软件同时连接到一台计算机上

2、严格意义来说,一台计算机只有一台控制台,但可以有多个终端

可以看出来这里的终端(Terminal)是一个物理设备的概念逐步向软件模拟的概念演进的过程image-20240429141518028

在没有shell之前,输入输出设备通过终端模拟器只能和应用进程进行交互,但是无法对内核进行交互式的处理,没办法去理解命令行的意义

了解shell之前,先记住一个很重要概念,shell是由终端进行调用,是运行在终端之内的


1.4 虚拟控制台和伪终端

接下来我们重新看看最开始的栗子!

通过Xshell这款终端软件通过SSH协议远程登录阿里云一台ECS主机

此时我们在本机的Xshell终端输入ttywho命令,返回如下内容

1
2
3
4
[root@localhost ~]# tty
/dev/pts/0
[root@localhost ~]# who
root pts/0 2024-04-29 16:10 (114.84.238.103)

通过tty命令可以知道当前具体是什么终端

who命令可以显示当前登录的用户名、终端、登录时间等信息

注意,时间2024-04-29 16:10 后面的IP地址就是我本地用于远程SSH登录电脑的公网地址

但是如果我们通过本地控制台或者类似vSphere Web控制台进去后,出现如下欢迎界面【实际是这就是一个虚拟终端tty1】

image-20240429162631265

此时我们再次输入ttywho命令,返回如下内容

1
2
3
4
[root@localhost ~]# tty
/dev/tty1
[root@localhost ~]# who
root tty1 2024-04-29 19:10

注意对比之前同样命令的返回结果的区别

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+F2ctrl+alt+F6代表了5个字符的虚拟终端,这些虚拟终端都是内核提供的,他们都位于内核态运行,例如下图

image-20240429162451959

内核提供的虚拟终端,需要使用键盘/鼠标直接连接,或者使用类似vSphere Web控制台这种模拟的本地控制台进行操作

image-20240429193733759

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模型

image-20240429201928410

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就可以将

image-20240429182919961

实际上,如果你感兴趣的话,你完全可以通过两个伪终端进行试验,pts/0pts/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,例如zshfish

可以这么认为,shell是一个统称,而bashzshfish才是具体shell的具体实现

在Windows环境下,操作系统也提供了shell的功能

1、Windows 95 / 98下的command.exe

2、Windows NT内核下的cmd以及PowerShell

3、而图形界面壳层即为explorer.exe

记住之前反复提及的一个概念:shell是由终端进行调用,是运行在终端之内的

image-20240429143015536

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
2
3
4
[root@localhost ~]# type echo
echo is a shell builtin
[root@localhost ~]# type ls
ls is aliased to `ls --color=auto'

2.3 查看当前shell

要查看当前使用的shell,你可以使用echo $0或者echo $SHELL命令。

在终端中执行以下任一命令,可以看出当前shell是由/bin/bash这个具体程序负责用户态和内核态在命令交互的翻译工作

1
2
[root@localhost ~]# echo $SHELL
/bin/bash

注意SHELL一定要是大写

查看当前用户可以使用的shell

1
2
3
4
5
[root@localhost ~]# cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash

或者使用chsh -l命令

1
2
3
4
5
6
7
8
9
[root@localhost ~]# chsh -l
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/usr/bin/zsh
/bin/zsh
/usr/bin/tmux
/bin/tmux

2.3 切换下一次使用的shell

切换shell通常指的是在同一个终端会话中更换使用的shell程序

在Linux系统中,这可以通过输入不同的shell命令来完成。例如,如果你当前使用的是bash shell,你可以切换到其他shell,如zsh或者fish

注意

1、切换shell必须使用-s参数

2、下次重启shell才能使用

1
2
3
[root@localhost ~]# chsh -s /bin/zsh
Changing shell for root.
Shell changed.

切换shell背后的原理其实很简单

chsh -s其实修改的就是/etc/passwd文件里和你的用户名相对应的那一行,现在来查看下

1
2
[root@ethan ~]# cat /etc/passwd | grep zsh
root:x:0:0:root:/root:/bin/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、用户环境变量:顾名思义,这种类型的环境变量只对特定的用户有效

image-20240429211156806

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
2
[root@localhost ~]# bash
打开一个bash

实际上我们发现~/.bashrc中就放alias别名设置

1
2
3
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'

交互式非登录 shell

指不与终端关联,一般是执行一个命令、一个脚本的 shell ,通常不需要人工干预,例如系统维护脚本、后台脚本

3.3 su命令是否带-的区别

使用su命令切换用户时,不会改变当前的环境变量,即仍然保持原用户的环境变量

而su -命令则会改变为切换到用户的环境变量,获得新用户的环境变量及执行权限

如图所示

su -命令会按优先级顺序执行

su命令则只会按照交互式nologin方式进行加载环境变量配置文件

3.4 修改环境变量的最佳实践

Linux 发行版更新的时候,会更新 /etc 里面的文件,比如 /etc/profile,因此建议不要直接修改这个文件

如果想修改所有用户的环境参数,建议在 /etc/profile.d目录里面新建.sh 脚本,如果想修改个人登录环境,一般是写在 ~/.bashrc 里面