Shell教程0--从terminal说起

介绍计算机中Shell的发展历史,介绍terminal、console、tty。介绍linux上常见的shell如bash/zsh,介绍shell的集中登陆方式。 Modified: 2023-08-14 23:23:04 Created: 2021-08-22 09:50:08 Tags: #linux #shell

学习Shell编程,需要对Shell有一些基本了解。本文从计算机发展历史介绍各种Shell之间的变迁。到了开始学习Linux Shell编程时候,则要知道常见的Shell如bash/zsh的差异,以及Shell各种登陆方式。

1 从终端机(Terminal)说起

Shell 命令运行在终端中,谈论 Shell 时一定离不开终端机。终端机的英文是 Terminal,也有翻译为命令行的,这个翻译很好地反映了工具的作用:接受命令输入,展示命令执行过程,给出命令输出。

常见操作系统中都有终端:OSX 系统有终端机,linux 系统有 gnome-terminal,Windows 系统有 Windows-Terminal。

mac Terminal

1.1 终端机--从硬件到软件

要了终端机,需要了解一些计算机的发展历史。冯诺伊曼计算机架构下,计算机分为输入、出入、存储、控制、计算部分。早期计算机都是庞然大物,这几部分都是分开的。输入输出靠纸带,读取纸带的打孔获得输入,计算机执行完成,往纸带上打孔用以输出。使用起来相当麻烦:当时优秀的程序员都是直接写01代码的。

世界上第一台计算机

世界上第一台计算机

写一个程序,就要消耗一堆纸带,新修改程序重新运行,得重新打纸带。就像用签字笔在纸上写字一样,写错了就只能划掉。(当然我猜测可能有修正带这样的东西。)这显然不是正常人类干出来的事,既不好读也不好写,更不用说调试了。

随着计算机的的发展,键盘成了计算机的输入,显示器成为计算机的输出,这两玩意组合到一起,通过电缆和主机连接到一起:键盘输入,主机执行,显示器显示命令运行结果。这方式可以比纸带高效,修改起来也更加方便,人机交互就容易了许多,比纸带不知道高到哪里去了。由是观之,今有键盘侠,古无纸带客,此言诚不我欺。

键盘显示器这两个玩意组成的设备就是终端。它们其实并不算计算机的一部分(计算机的主要部分还是中间运算的部分),但有了它(指的是键盘和显示器一起)之后,人类和计算机交互起来效率是大大地高。那么是不是可以给计算机本身也配置一个这样的设备呢,计算机启动的时候还可以输出一些日志,显示状态、报错等。当然是可以的!这样的“终端”就被称为控制台(console)。通过这个特殊的终端,可以对整个计算机进行控制。

随着技术进步,计算机进入集成电路时代,各种电路都塞到芯片中去了,电脑越做越小,微机越来越流行。

MOSFET

一种晶体管 MOSFET 的结构,图片来自维基百科

以前的庞然大物现在都可以放到口袋里面带走了。键盘显示器成了电脑的标配。反而那种只能和大型主机交互的设备彻底走入历史,硬件终端完全消失,取而代之的是软件终端:在微机上用一套软件模拟终端和主机进行交互。

1.2 键盘和 tty

在 Linux 中,打开终端就会自动开启一个 Shell(如 Bash),还会显示tty

Last login: Mon Sep 20 10:34:38 on ttys006

tty 是什么意思?这要从键盘说起。最开始键盘都是机械结构,直接将墨水印在纸上。它只是解决了信息输入的问题,而并没有解决远程输出的问题,即把信息从一个设备传送到另外一个设备。

通信技术在同步发展,此时已经有了电传打字机(teletype/teletypewriter),能够实现如下功能:

  1. 在本地通过键盘输入一些信息
  2. 输入结果能够在本地纸张上显示
  3. 本地输入结果能够传递到别的设备中,被别的设备识别
  4. 能够接受别的设备的输出,并且翻译为人类可以读的东西

这玩意就被称为 tty,按照词缀来翻译就是远程输入或者远程打字。翻译为电传打字的意思是说它传递的是电信号。

二战中使用的teletype

二战中使用的 teletype图片来自维基百科

在计算机出现之前,人们就已经建立起了以电传打字机为节点的“互联网”。这个“互联网”,可以交换股票数据。计算机开始发展之后,人们也发现有和计算机交互的需求,于是就直接把 tty 拿过来了,作为计算机的输入输出部分。tty 是可以脱离计算运行的,它并不是计算机的一部分;现在的服务器在没有键盘显示器的情况下也能运行。

某些情况下,又不得不依赖键盘显示器操作。如果服务器宕机了需要处理,就需要给服务器连接显示器和键盘进行诊断。此时这样的终端也被看成是控制台(console),它的作用是输出计算机的基本信息,它属于计算机的一部分。当然现在并不会明显区分终端和控制台,它们都是和计算机交互的工具。

现在系统中还有tty命令,用于显示当前终端:

tty
/dev/ttys009

在 Linux 中,可以运行Alt + Ctrl + F{2,6} 开启各个 tty。究竟有多少个 tty 呢,查看/dev/文件夹,看其中的tty文件。在 linux 中,一切皆文件。tty 不例外,也可以向 tty 写入信息:

echo "hello" >> /dev/tty1

通过who命令可以获取所有的登陆的终端,echo内容到/dev/tty{n} 可以在对应终端上显示。

通常 ssh 时候,执行tty获得的信息可能是pts,这是 pseudo terminal 的缩写,表示伪终端。在内核中有两部分,master side 和 tty 驱动中实现的 slave side。如果在 ssh 情况下输入 tty,则会是/dev/pts,也可以通过往/dev/pts 中写入信息往终端传输信息。

2 Shell 是什么

前面说到了终端,通过它可以和计算机进行交互。实际上终端也不是直接去控制硬件,而是它和操作系统(Operator System)内核(Kernel)交互,让操作系统内核去控制硬件。

内核

操作系统内核,图片来自鸟哥的 linux 私房菜

设计操作系统这一中介的原因,本质是计算机系统太复杂,如果每执行一个操作就直接操作计算机硬件,那将复杂到无法完成。电子计算机主要依靠晶体管中电子运动形成的高低电压,目前最新 CPU 已经能够集成 10 亿级别的晶体管,如果手工去操作,写代码绝对要到地老天荒。

解决办法是层层抽象,只要给操作系统发指令,剩下的事情就让操作系统帮我们搞定。Shell 就是我们给操作系统发送指令的一个通道。

广义上任何能够给操作系统发送信息实现一定功能的程序都是 Shell。如 Windows/MacOS 上各种软件,Windows 文件浏览器,Mac 上的 Finder,Linux/Unix 上的文字界面的 Shell 程序。狭义的 Shell 指的是 Linux/Unix 上的用 C 语言写成的程序,如 Bash/Zsh。本文以 Bash/Zsh 为代表,介绍狭义 Shell 的使用。

2.1 使用 Shell 的理由

  1. 大部分服务器运行 Linux/Unix操作系统,Shell 是原生的管理方式
  2. Shell 传递文字界面,对带宽要求比较低
  3. 灵活使用 Shell,可以提升效率

3 Shell 登陆方式

Shell 有多种使用方式:

  1. 文字界面登陆 Linux 系统
  2. 在图形界面直接打开终端机
  3. 通过 ssh 远程登陆服务器
  4. 运行一段 Shell Script

需要对上面的场景进行一些分类:按照是否需要输入密码,可以分为登录式(login)和非登陆式(non-login);按照运行一个文件还是运行一个命令,可以分为交互式(interactive)和非交互式(non-interactive)。

3.1 登陆与非登陆式

登陆式和非登陆式的区别在于启动退出时候读取的文件不一样。

3.1.1 登陆式

本地 Linux,进入系统后直接打开 terminal,这种方式被称为非登陆(non-login),因为不需要输入账户和密码。bash 有如下配置文件:

/ect/profile
~/.bash_profile
~/.bash_login
~/.profile
~/.bash_logout

对于 login 的 Shell,通常会读取两个文件:

  1. /etc/profile,系统配置,一般不要修改
  2. ~/.bash_profile~/.bash_login~/.profile中的一个,读到一个就停止

通常配置的却是~/.bashrc文件。其中rc是 run common 的意思。但~/.bashrc并不是上面 2 中任何文件。奥秘就在~/.bash_profile中:

cat ~/.bashrc
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
    . ~/.bashrc
fi

# User specific environment and startup programs

它直接就读取~/.bashrc文件了。

3.1.2 非登陆式

对于 non-login 的 Shell,它只读取~/.bashrc这一个配置文件。

3.1.3 su切换时候的环境变量

在 Linux 中,通过su命令切换用户。su 是 switch user 的意思。通常su后,只是切换用户,并不会切换其他的。而运行su -之后,就会直接切换到 root 账户,连环境变量都会切换的。

3.2 交互式和非交互式

3.2.1 交互式运行

交互式(interactive)运行就是输入一条命令,执行,输出运行结果或者报错。直接打开命令行工具或 SSH 时候默认状态。好处就是可以马上运行,获得结果或者报错。后面第五部分会详细介绍交互式 Shell 的配置。

3.2.2 非交互式

简单来说就是运行 Shell Script。把要运行的命令按照顺序写到一个文件里,这个文件就是 Shell Script,然后让 Shell 调用这个脚本,里面的命令从上到下运行。Shell Script 的好处在于可以快速地复用命令,比如有如下场景:

  1. 同样的命令需要运行 100 次,但是命令本身并不提供循环运行的参数,比如对 100 个文件按照规则改名
  2. 运行的命令比较长,需要在前几条修改一下参数,然后运行后面的命令获取结果
  3. 在电脑 A 上运行的命令需要在电脑 B(或者更多电脑)上原样运行一番

此时若采用交互式运行,则需要不停的敲键盘运行,和敲击鼠标无异,效率提升有限。运行文件的方式就时非交互式(non-interactive),此时运行就简化了许多。非交互式运行会开启一个新的线程来执行 Shell 命令,当执行遇到EOF之后,整个线程就会退出。

4 Shell Script

在非交互式运行中,需要特别学习 Shell Script 的语法:借助 Shell Script 的语法,还可以对命令进行扩展,如循环运行一条命令,如按照条件运行命令。Shell Script 编程的学习曲线有些陡峭:

  1. 空格十分敏感 在 Shell Script 中,等号(=)左右有无空格,表示完全不同的意思,但这样的错误一开始是很难发现的
  2. 报错过于简单 如果遇到了空格问题,报错不会提示空格缺失,而会说缺少命令
  3. 遇到错误后继续执行 一般的程序语言是遇到错误后停止运行,shell script 跳过错误,继续执行,前面不对的结果会影响后面的执行,如果不好好查 log,就不知道错误在哪里了
  4. 缺乏方便调试工具 调试起来不是很方便,常常是在运行中调试。运行一次,再调试,环境就不一样的。

5 zsh 配置

当然,交互式的 Shell 也是被广泛使用的工具。

sh 是最古老的 Shell,目前已经不怎么使用了。目前最流行版本是 Bash(orne Again Shell),但是 Bash 在没有配置的情况下很难用。常见的 Shell 还有 Zsh、Fish、Ksh、Tcsh。一般比较推荐 Zsh,它相对于 Bash 而言,有更好的补全和跳转功能。搭配上oh-my-zsh插件,可以极大提升效率。zsh 安装方式如下:

sudo dnf install zsh

安装好 zsh 之后需要切换系统默认登陆 Shell 为 zsh:

sudo chsh -s /usr/bin/zsh user_name

配置oh-my-zsh

sh -c "$(curl -fsSL https://raw.github.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

配置oh-my-zsh插件,找到~/.zshrc文件,在plugins中添加:

plugins=(
   git
   extract
   zsh-syntax-highlighting
   zsh-autosuggestions
 )

总结

本文主要介绍 Shell 的基本概念,主要解决 Shell 使用之前的问题。先介绍了终端机(Terminal)、tty 等概念,然后介绍了 Shell 交互和非交互的运行方式,最后介绍了 Shell 交互运行时候 zsh 的使用方式,配置oh-my-zsh相关的配置。