文章目录
-
- [5. 理解shell](#5. 理解shell)
-
- [5.1 shell的类型](#5.1 shell的类型)
- [5.2 交互shell和系统默认shell](#5.2 交互shell和系统默认shell)
- [5.3 安装zsh shell程序](#5.3 安装zsh shell程序)
- [5.4 shell的父子关系](#5.4 shell的父子关系)
- [5.5 命令列表](#5.5 命令列表)
- [5.6 命令分组](#5.6 命令分组)
- [5.7 使用命令分组创建子shell](#5.7 使用命令分组创建子shell)
- [5.8 子shell用法](#5.8 子shell用法)
- [5.9 shell的非内建命令和内建命令](#5.9 shell的非内建命令和内建命令)
-
- [5.9.1 非内建命令](#5.9.1 非内建命令)
- [5.9.2 内建命令](#5.9.2 内建命令)
- [5.9.3 history和alias命令介绍](#5.9.3 history和alias命令介绍)
5. 理解shell
shell不单单是一种CLI,它是一个时刻都在运行的复杂交互式程序。
输入命令并利用shell来运行脚本会出现一些既有趣又令人困惑的问题。搞清楚shell进程以及它与系统之间的关系能够帮助你解决这些难题,或是完全避开它们。
在本文章中,你会了解到如何创建子shell以及父shell与子shell之间的关系。探究各种用于创建子进程的命令和内建命令。
5.1 shell的类型
系统启动什么样的shell程序取决于你个人的用户ID配置。在/etc/passwd文件中,在用户ID记录的第7个字段中列出了默认的shell程序。只要用户登录到某个虚拟控制台终端或是在GUI中启动终端仿真器,默认的shell程序就会开始运行。
shell
# 1. 在现代Linux系统中,bash shell程序(bash)通常位于/usr/bin目录或者/bin目录,which bash命令可以帮助我们找出bash shell的位置。
[root@Fedora-Desktop ~]# which bash
/usr/bin/bash
[root@Fedora-Desktop ~]#
# 2. 从/usr/bin目录中,可以看到bash shell是一个程序。
[root@Fedora-Desktop ~]# ls -lF /usr/bin/bash
-rwxr-xr-x. 1 root root 1406608 2月 9日 08:00 /usr/bin/bash*
[root@Fedora-Desktop ~]#
# 3. root用户使用bash shell作为默认shell程序。
[root@Fedora-Desktop ~]# head -n 1 /etc/passwd
root:x:0:0:Super User:/root:/bin/bash
[root@Fedora-Desktop ~]#
# 4. 用户可以将这些shell程序中的某一个作为自己的默认shell。不过由于bash shell的广为流行,很少有人使用其他的shell作为默认的交互式shell。
[root@Fedora-Desktop ~]# cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
[root@Fedora-Desktop ~]#
除了Bash(Bourne Again SHell)和sh(Bourne Shell)之外,还有多种其他的shell,它们各有特色,适用于不同的场景和需求。以下是一些常见的shell:
1. C Shell (csh) & Turbo C Shell (tcsh):
C Shell 的语法类似于C编程语言,提供了命令历史、别名等功能。tcsh 是 csh 的增强版本,增加了命令行编辑、自动完成等便利功能。
2. Korn Shell (ksh):
由David Korn设计,是Bourne Shell的一个扩展,支持更高级的编程结构,如函数、数组等,同时兼容sh脚本。
3. Z Shell (zsh):
功能强大,是bash的一个超集,拥有丰富的插件系统、高级自动补全功能、主题支持等,近年来因其高度可定制性和用户体验而在开发者中变得非常流行。
4.Fish (Friendly Interactive Shell):
设计用于提供更好的用户体验,有着出色的命令补全和颜色高亮特性,以及友好的交互界面,适合新手。
5. Debian Almquist Shell (dash):
一个轻量级的shell,专为速度优化,主要用于系统的初始化脚本,是Debian和Ubuntu等系统中的/bin/sh的默认实现。
6. BusyBox Ash:
通常在嵌入式Linux系统中使用,是一个小型化的Almquist Shell,旨在减少资源占用。
7. PowerShell:
虽然起源于Microsoft Windows平台,但现在也有Linux和macOS版本,它是一种面向对象的shell,专为系统管理和自动化任务设计,支持.NET框架和CMDLETs。
这些shell各有优劣,选择哪种shell往往取决于用户的特定需求、习惯以及系统环境的要求。
5.2 交互shell和系统默认shell
默认的交互shell (bash)会在用户登录某个虚拟控制台终端或在GUI中运行终端仿真器时启动。
默认的系统shell(sh)用于那些需要在启动时使用的系统shell脚本。
1.某些发行版使用软链接将默认的系统shell指向bash shell,比如Fedora发行版:
shell
[root@Fedora-Desktop ~]# which sh
/usr/bin/sh
[root@Fedora-Desktop ~]# ls -lF /usr/bin/sh
lrwxrwxrwx. 1 root root 4 7月 9日 08:00 /usr/bin/sh -> bash*
[root@Fedora-Desktop ~]#
- 在有些发行版中,默认的系统shell并不指向bash shell,比如Ubuntu发行版:
shell
[root@Ubuntu-Desktop ~]# which sh
/usr/bin/sh
[root@Ubuntu-Desktop ~]# ls -lF /usr/bin/sh
lrwxrwxrwx 1 root root 4 7月 9日 08:01 /usr/bin/sh -> dash*
[root@Ubuntu-Desktop ~]#
- 使用不同的shell。
shell
# 使用echo $0显示当前所用shell的做法仅限在shell命令行中使用。如果在shell脚本中使用,则显示的是该脚本的名称。 "-bash"中的 "-"
[root@Fedora-Desktop ~]# echo $0
-bash -bash中的 "-"表示它是交互式shell
[root@Fedora-Desktop]~#
# 输入命令exit就可以退出sh程序
[root@Fedora-Desktop]~# sh
sh-5.2# exit
exit
[root@Fedora-Desktop ~]# zsh
[root@Fedora-Desktop]~# echo $0
zsh
[root@Fedora-Desktop]~#
5.3 安装zsh shell程序
shell
# 1. 在Fedora 40-1.14系统上安装zsh
[root@Fedora-Desktop ~]# dnf install zsh -y
# 2. 将zsh设置为当前系统的默认shell
[root@Fedora-Desktop ~]# chsh -s $(which zsh)
Changing shell for root.
Shell changed.
[root@Fedora-Desktop ~]#
# 3. 查看root用户现在使用的默认shell。
[root@Fedora-Desktop ~]# head -n 1 /etc/passwd
root:x:0:0:Super User:/root:/usr/bin/zsh
[root@Fedora-Desktop ~]# echo $PATH
/root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
[root@Fedora-Desktop ~]#
# 4. 重新打开一个终端执行exec zsh 或者重启生效
[root@Fedora-Desktop]~# exec zsh
[root@Fedora-Desktop]~# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
[root@Fedora-Desktop]~# cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
/usr/bin/zsh
/bin/zsh
[root@Fedora-Desktop]~#
# 5. 改回原来的shell: chsh -s [原来shell的完整路径]
[root@Fedora-Desktop]~# chsh -s /bin/bash
Changing shell for root.
Shell changed.
[root@Fedora-Desktop]~# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
# 6. 重新打开一个终端执行exec bash 或者重启生效
[root@Fedora-Desktop]~# exec bash
[root@Fedora-Desktop ~]# echo $PATH
/root/.local/bin:/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
[root@Fedora-Desktop ~]#
其他shell的安装方法类似,不过多介绍了。
5.4 shell的父子关系
shell
[root@Fedora-Desktop ~]# echo $0
-bash
[root@Fedora-Desktop ~]# bash
[root@Fedora-Desktop ~]# echo $0
bash
[root@Fedora-Desktop ~]# exit
exit
[root@Fedora-Desktop ~]# echo $0
-bash
[root@Fedora-Desktop ~]#
# 好像什么并没有改变什么,但是bash程序已经退出了。
为了理解这个过程,我们接下来将探究登录shell程序与新启动的shell程序之间的关系。
shell
# 1. 用户登录某个虚拟控制台终端或在GUI中运行终端仿真器时所启动的默认的交互式shel(登录shell)是一个父shell。
[root@Fedora-Desktop ~]# ps -f
UID PID PPID C STIME TTY TIME CMD
root 2636 2621 0 17:07 pts/1 00:00:00 -bash
root 2713 2636 99 17:28 pts/1 00:00:00 ps -f
[root@Fedora-Desktop ~]#
# 2. 当你在CLI提示符处输入/usr/bin/bash或者bash命令(或是其他shell程序名)时,会创建新的shell程序。这是一个子shell。子shell也拥有CLI提示符,同样会等待命令输入。
[root@Fedora-Desktop ~]# bash
[root@Fedora-Desktop ~]# ps -f
UID PID PPID C STIME TTY TIME CMD
root 2636 2621 0 17:07 pts/1 00:00:00 -bash
root 2714 2636 0 17:29 pts/1 00:00:00 bash #它的父进程是2636
root 2735 2714 0 17:30 pts/1 00:00:00 ps -f
[root@Fedora-Desktop ~]#
第一个bash shell程序,也就是父shell进程,其进程PID是2636。
第二个bash shell程序,即子shell进程,其进程PID是2430。
它们之间的关系如下图:
注意:
子shell( child shell,也叫subshell)可以从父shell中创建,也可以从另一个子shell中创建。
在生成子shell进程时,只有部分父进程的环境被复制到了子shell环境中。这会对包括变量在内的一些东西造成影响。
shell
# bash命令被输入了两次,这实际上创建了两个子shell。
[root@Fedora-Desktop ~]# ps -f
UID PID PPID C STIME TTY TIME CMD
root 1860 1846 0 21:17 pts/0 00:00:00 -bash
root 1894 1860 25 21:17 pts/0 00:00:00 ps -f
[root@Fedora-Desktop ~]# bash
[root@Fedora-Desktop ~]# bash
[root@Fedora-Desktop ~]# ps --forest
PID TTY TIME CMD
1860 pts/0 00:00:00 bash
1909 pts/0 00:00:00 \_ bash
1943 pts/0 00:00:00 \_ bash
2016 pts/0 00:00:00 \_ ps
[root@Fedora-Desktop ~]#
# 使用exit可以退出子shell
[root@Fedora-Desktop ~]# exit
exit
[root@Fedora-Desktop ~]# exit
exit
[root@Fedora-Desktop ~]# ps --forest
PID TTY TIME CMD
1860 pts/0 00:00:00 bash
2055 pts/0 00:00:00 \_ ps
[root@Fedora-Desktop ~]#
bash 主页:http://www.gnu.org/software/bash
使用 GNU 软件的通用帮助:http://www.gnu.org/gethelp/
shell
# 获取更多帮助信息
[root@Fedora-Desktop ~]# bash --help
[root@Fedora-Desktop ~]#
[root@Fedora-Desktop ~]# man bash
5.5 命令列表
- 是一系列由分号(
;
)、换行符或空格分隔的命令集合,它们在shell中按顺序执行。 - 每个命令独立运行,它们共享当前shell的环境(包括变量、工作目录等)。
- 命令之间互不影响,前一个命令的输出、错误或状态不会直接影响后续命令的执行,除非通过重定向或命令替换等方式显式地传递数据或状态。
shell
# 命令列表 (用 ;)
[root@Fedora-Desktop ~]# pwd; ls; cd /var/log; pwd; echo $BASH_SUBSHELL
/root
anaconda-ks.cfg zy.txt
/var/log
0
[root@Fedora-Desktop log]#
# 命令列表 (用 \)
[root@Fedora-Desktop log]# virt-install \
--boot hd \
--name=mysql-EC3 \
--os-type=linux \
--os-variant=fedora18 \
--cpu host \
--vcpus 2,maxvcpus=2 \
--memory 2048 \
--disk=/var/lib/libvirt/images/LinuxImage.qcow2 \
--network network=ovs01_network,target=if01,mac=02:01:01:01:01:01 \
--graphics vnc,listen=0.0.0.0,port=60001
bash: virt-install: 未找到命令...
安装软件包"virt-install"以提供命令"virt-install"? [N/y]
如果 echo $BASH_SUBSHELL
该命令返回0,表明没有子shell;返回其他数字,则表明存在子shell。
5.6 命令分组
命令分组是指在执行复杂操作或需要将多个命令组合以实现特定逻辑时,对这些命令进行的一种逻辑上的组织。这通常涉及使用shell的结构化特性。
- 利用括号
()
、大括号{}
(需配合;
、&
或换行符)或特定语法(如$(command)
用于命令替换)来组织命令。 - 当使用
()
时,会创建一个新的子shell环境来执行括号内的命令组。这意味着在子shell中对环境的修改(如变量赋值、工作目录变更)不会影响到外部的父shell。 - 当时用
{}
时,不会创建新的子shell,不过,它们通常需要配合分号或换行以及命令执行符(如;
、&
)来正确执行。 - 使用复杂逻辑如
&&
(前一命令成功后执行后一命令)、||
(前一命令失败后执行后一命令)、;
(无论前一命令成功与否都执行后一命令)等来进行命令的串联或并行执行。
命令分组可以实现:
- 逻辑分组:使得一组命令作为单一单元处理,比如在条件语句或循环中。
- 流程控制:控制命令的执行顺序和条件,比如基于前一个命令的成功或失败来决定是否执行下一个命令。
- 环境隔离:在子shell中执行命令组,可以保持或隔离环境变量和状态,不影响外部shell环境。
- 资源共享 :在同一个子shell内的命令可以更容易地共享输出或状态信息,例如使用管道
|
连接的命令组,前一个命令的输出直接作为后一个命令的输入。
简而言之,命令列表关注的是Linux提供的所有独立命令及其功能,而命令分组则侧重于如何通过逻辑组织这些命令来实现更复杂的操作流程和任务自动化。
5.7 使用命令分组创建子shell
不使用bash shell命令和运行shell脚本,你也可以生成子shell。一种方法就是使用命令分组 。
shell
# 1.使用圆括号生成了一个子shell来执行这些命令。
[root@Fedora-Desktop ~]# (pwd; ls; cd /var/log; pwd; echo $BASH_SUBSHELL)
/root
anaconda-ks.cfg zy.txt
/var/log
1 # 存在子shell
[root@Fedora-Desktop ~]#
# 2.使用包围起来的一组命令,它能够创建出子shell来执行这些命令。甚至可以在命令列表中嵌套括号来创建子shell的子shell。
[root@Fedora-Desktop ~]# echo $BASH_SUBSHELL
0
[root@Fedora-Desktop ~]# (pwd; echo $BASH_SUBSHELL)
/root
1
[root@Fedora-Desktop ~]# (pwd; (echo $BASH_SUBSHELL))
/root
2
[root@Fedora-Desktop ~]#
# 3. 使用花括号进行命令分组并不会像进程列表那样创建子shell。
[root@Fedora-Desktop ~]# { pwd ; echo $BASH_SUBSHELL; }
/root
0
[root@Fedora-Desktop ~]#
子shell在shell脚本中经常用于多进程处理。但是,创建子shell要消耗更多的资源,比如内存和计算能力,会明显拖慢任务进度。在交互式CLI shell会话中,子shell同样存在问题,它并非真正的多进程处理,原因在于终端与子shell的I/O绑定在了一起。
5.8 子shell用法
在交互式shell中,一种高效的子shell用法是后台模式。
shell
# 1. 探究后台模式
在后台模式运行命令可以在处理命令的同时让出CLI,以供他用sleep命令会接受一个参数作为希望进程等待(睡眠)的秒数。该命令在shell脚本中常用于引入一段暂停时间。
[root@Fedora-Desktop ~]# sleep 10
[root@Fedora-Desktop ~]#
shell
# 2. 要想将命令置入后台模式,可以在命令末尾加上字符 &
第一条信息是方括号中的后台作业号(1)。第二条信息是后台作业的进程ID(2162)。
[root@Fedora-Desktop ~]# sleep 5000&
[1] 2162
[root@Fedora-Desktop ~]# ps -f
UID PID PPID C STIME TTY TIME CMD
root 1305 1298 0 08:58 pts/0 00:00:00 -bash
root 2162 1305 0 10:30 pts/0 00:00:00 sleep 5000
root 2163 1305 0 10:31 pts/0 00:00:00 ps -f
[root@Fedora-Desktop ~]#
# 3. jobs命令能够显示当前运行在后台模式中属于你的所有进程(作业)
[root@Fedora-Desktop ~]# jobs -l
[1]+ 2162 运行中 sleep 5000 &
[root@Fedora-Desktop ~]#
需要提醒的是:后台作业的结束状态可未必会一直等待到合适的时候才现身。当作业结束状态突然出现在屏幕上的时候,你可别吃惊啊。
后台模式非常方便,它可以让我们在CLI中创建出有实用价值的子shell。
将分组列表置入后台
通过将分组列表置入后台,可以在子shell中进行大量的多进程处理。由此带来的一个好处是终端不再和子shell的I/O绑定在一起。
shell
# 4. 分组列表前台运行
在分组列表中加入sleep命令并显示BASH_SUBSHELL变量,结果不出所料:
[root@Fedora-Desktop ~]# (sleep 2 ; echo $BASH_SUBSHELL ; sleep 2)
1
[root@Fedora-Desktop ~]#
# 5. 将同样的进程列表置入后台会产生些许不同的命令输出:
[root@Fedora-Desktop ~]# (sleep 2 ; echo $BASH_SUBSHELL ; sleep 2)&
[2] 2183
[root@Fedora-Desktop ~]# 1
^C
[2]+ 已完成 ( sleep 2; echo $BASH_SUBSHELL; sleep 2 )
[root@Fedora-Desktop ~]#
# 6. tar 创建备份文件有效利用后台进程列表的一个实用例子
[root@Fedora-Desktop ~]# ls
documents music
[root@Fedora-Desktop ~]# (tar -cf doc.tar documents/ ; tar -cf music.tar music/)&
[2] 2211
[root@Fedora-Desktop ~]# ls *.tar
doc.tar music.tar
[2]+ 已完成 ( tar -cf doc.tar documents/; tar -cf music.tar music/ )
[root@Fedora-Desktop ~]# ls
doc.tar documents music music.tar
[root@Fedora-Desktop ~]#
协程
进程、线程、协程是现代计算中用于实现并发和多任务处理的三种基本概念,它们之间存在显著的区别:
-
资源拥有与隔离:
- 进程:是操作系统资源分配的最小单位,每个进程都有自己独立的地址空间,包括代码段、数据段、堆和栈。这意味着进程之间不共享内存,通信通常需要通过IPC(进程间通信)机制。
- 线程:是进程内的执行单元,线程之间共享所属进程的地址空间,包括代码、数据和文件描述符等资源。线程减少了上下文切换的开销,但增加了资源共享的复杂度,需要同步机制(如锁、信号量)来防止数据竞争。
- 协程:是一种用户态轻量级线程,运行在单个线程内,共享同一地址空间。协程的调度由程序自身控制,而非操作系统,因此切换更快且无需操作系统介入。协程通过协作而非抢占的方式进行任务切换,降低了同步开销。
-
系统开销:
- 进程:创建和销毁进程的开销相对较大,因为需要分配独立的地址空间和其他资源。
- 线程:相比进程,创建线程的开销较小,但仍需分配栈空间等资源,且线程间的切换由操作系统管理,有一定的开销。
- 协程:创建和切换协程的开销极小,仅需保存和恢复少量上下文,通常只需几字节到几十字节的栈空间。
-
调度方式:
- 进程和线程:通常由操作系统内核调度,可以被中断并由操作系统决定何时恢复执行。
- 协程:调度责任在于程序本身,程序员通过明确的yield(让出控制权)操作来控制协程的执行流,这使得协程能够避免不必要的调度,提高效率。
-
应用场景:
- 进程:适用于需要隔离资源和独立执行环境的任务,如服务器上运行的不同服务。
- 线程:适用于需要共享资源但又希望保持相对独立执行路径的场景,如Web服务器处理多个请求。
- 协程:特别适合于I/O密集型应用,如网络请求处理、异步IO、游戏开发等,它们能高效地处理大量的并发请求而无需大量线程。
-
资源占用:
-
- 进程占用资源最多,包括独立的内存空间。
- 线程相对较少,但仍需分配栈空间。
- 协程占用资源最少,一般只需要少量栈空间。
-
执行顺序:
- 线程:线程的执行顺序由操作系统调度,通常是无序的。
- 协程:通过程序控制,可以实现按需、有序的执行。
这篇文章可以大致看一下:一文彻底搞懂协程(coroutine)是什么,值得收藏-CSDN博客
总的来说,进程提供了最高级别的隔离和独立性,但开销大;线程减少了这种开销,实现了更细粒度的并发,但增加了资源共享的复杂性;而协程以其轻量、高效的特性,成为处理高并发I/O密集型任务的理想选择,但需要手动管理调度逻辑。
将进程列表置入后台并不是子shell在CLI中仅有的创造性用法,还有一种方法是协程。
协程同时做两件事:一是在后台生成一个子shell,二是在该子shell中执行命令。
shell
# 1. 要进行协程处理,可以结合使用coproc命令以及要在子shell中执行的命令:
[root@Fedora-Desktop ~]# ps -f
UID PID PPID C STIME TTY TIME CMD
root 1305 1298 0 08:58 pts/0 00:00:00 -bash
root 2162 1305 0 10:30 pts/0 00:00:00 sleep 5000
root 2237 1305 99 11:05 pts/0 00:00:00 ps -f
[root@Fedora-Desktop ~]#
[root@Fedora-Desktop ~]# coproc sleep 10
[1] 2238
[root@Fedora-Desktop ~]# jobs -l
[1]+ 2238 运行中 coproc COPROC sleep 10 &
[root@Fedora-Desktop ~]#
[1]+ 已完成 coproc COPROC sleep 10
[root@Fedora-Desktop ~]#
# 2. COPROC是coproc命令给进程起的名字。你可以使用命令的扩展语法自己设置这个名字
[root@Fedora-Desktop ~]# coproc My_Job { sleep 10; }
[1] 2240
[root@Fedora-Desktop ~]# jobs -l
[1]+ 2240 运行中 coproc My_Job { sleep 10; } &
[root@Fedora-Desktop ~]#
必须确保在第一个花括号{
和命令名之间有一个空格。还必须保证命令以分号 ;
结尾。另外,分号和闭花括号 }
之间也得有一个空格。
协程能够让你尽情发挥想象力,发送或接收来自子shell中进程的信息。只有在拥有多个协程的时候才需要对协程进行命名,因为你得和它们进行通信。否则的话,让coproc命令将其设置成默认的名字COPROC就行了。
shell
# 可以发挥才智,将协程与进程列表结合起来创建嵌套子shell。只需将命令coproc放在进程列表之前即可:
[root@Fedora-Desktop ~]# coproc ( sleep 20; sleep 10)
[1] 2253
[root@Fedora-Desktop ~]# jobs
[1]+ 运行中 coproc COPROC ( sleep 20; sleep 10 ) &
[root@Fedora-Desktop ~]# ps --forest
PID TTY TIME CMD
1305 pts/0 00:00:00 bash
2253 pts/0 00:00:00 \_ bash
2254 pts/0 00:00:00 | \_ sleep
2255 pts/0 00:00:00 \_ ps
[root@Fedora-Desktop ~]#
记住,生成子shell的成本不低,而且速度还慢。创建嵌套子shell更是火上浇油!在命令行中使用子shell能够获得灵活性和便利。要想获得这些优势,重要的是理解子shell的行为方式。对于命令也是如此。
5.9 shell的非内建命令和内建命令
5.9.1 非内建命令
外部命令(有时也称为文件系统命令)是存在于bash shell之外的程序,它并不属于shell程序的一部分。外部命令程序通常位于 /bin
、/usr/bin
、/sbin
或 /usr/sbin
目录中。
shell
# ps命令就是一个外部命令。可以使用which命令和type命令找到其对应的文件名:
[root@Fedora-Desktop ~]# which ps
/usr/bin/ps
[root@Fedora-Desktop ~]# type -a ps
ps 是 /usr/bin/ps
[root@Fedora-Desktop ~]# ls -l /bin/ps
-rwxr-xr-x. 1 root root 120776 1月26日 08:00 /bin/ps
[root@Fedora-Desktop ~]#
# 每当执行外部命令时,就会创建一个子进程。这种操作称为衍生(forking)。外部命令ps会显示其父进程以及自己所对应的衍生子进程。
[root@Fedora-Desktop ~]# ps -f
UID PID PPID C STIME TTY TIME CMD
root 1305 1298 0 08:58 pts/0 00:00:00 -bash
root 2290 1305 99 11:39 pts/0 00:00:00 ps -f
[root@Fedora-Desktop ~]#
只要涉及进程衍生,就需要耗费时间和资源来设置新子进程的环境。因此,外部命令系统开销较高。
当进程必须执行衍生操作时,它需要花费时间和精力来设置新子进程的环境。所以说,外部命令多少还是有代价的。
就算衍生出子进程或是创建了子shell,你仍然可以通过发送信号与其沟通,这一点无论是在命令行还是在脚本编写中都是极其有用的。
5.9.2 内建命令
内建命令无须使用子进程来执行。内建命令已经和shell编译成一体,作为shell的组成部分存在,无须借助外部程序文件来执行。cd命令和exit命令都内建于bash shell。可以使用type命令来判断某个命令是否为内建。
shell
[root@Fedora-Desktop ~]# type cd
cd 是 shell 内建
[root@Fedora-Desktop ~]# type exit
exit 是 shell 内建
[root@Fedora-Desktop ~]#
# echo和pwd既有内建命令也有外部命令
[root@Fedora-Desktop ~]# type -a echo
echo 是 shell 内建
echo 是 /usr/bin/echo
[root@Fedora-Desktop ~]# which echo
/usr/bin/echo
[root@Fedora-Desktop ~]#
[root@Fedora-Desktop ~]# type -a pwd
pwd 是 shell 内建
pwd 是 /usr/bin/pwd
[root@Fedora-Desktop ~]# which pwd
/usr/bin/pwd
[root@Fedora-Desktop ~]#
# type -a命令显示出了每个命令的两种实现(内建和非内建)。注意,which命令只显示外部命令文件。
提示: 对于有多种实现的命令,如果想使用其外部命令实现,直接指明对应的文件即可。例如,要使用外部命令pwd,可以输入/usr/bin/pwd。
5.9.3 history和alias命令介绍
shell
1. history是一个有用的内建命令
当输入 !! 时,bash会先显示从shell的历史记录中唤回的命令,然后再执行该命令。命令历史记录被保存在位于用户主目录的隐藏文件.bash_history之中:
[root@Fedora-Desktop ~]# ls .bash_history
.bash_history
[root@Fedora-Desktop ~]#
在CLI会话期间,bash命令的历史记录被保存在内存中。当shell退出时才被写入历史文件:
以在不退出shell的情况下强制将命令历史记录写入.bash_history文件。为此,需要使用history命令的 -a选项:
[root@Fedora-Desktop ~]# history -a
[root@Fedora-Desktop ~]#
如 果 你 打 开 了 多 个 终 端 会 话 , 仍 然 可 以 使 用 history -a 命 令 在 打 开 的 会 话 中
向.bash_history文件中添加记录。但是对于其他打开的终端会话,历史记录并不会自动更
新。这是因为.bash_history文件只有在打开首个终端会话时才会被读取。要想强制重新读
取.bash_history文件,更新终端会话的历史记录,可以使用history -n命令。
2. 使用命令别名
alias命令是另一个实用的shell内建命令。命令别名允许为常用命令及其参数创建另一个名称,从而将输入量减少到最低。
[root@Fedora-Desktop ~]# alias -p
alias cp='cp -i'
alias egrep='grep -E --color=auto'
alias fgrep='grep -F --color=auto'
alias grep='grep --color=auto'
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'
alias ls='ls --color=auto'
alias mv='mv -i'
alias rm='rm -i'
alias which='(alias; declare -f) | /usr/bin/which --tty-only --read-alias --read-functions --show-tilde --show-dot'
alias xzegrep='xzegrep --color=auto'
alias xzfgrep='xzfgrep --color=auto'
alias xzgrep='xzgrep --color=auto'
alias zegrep='zegrep --color=auto'
alias zfgrep='zfgrep --color=auto'
alias zgrep='zgrep --color=auto'
[root@Fedora-Desktop ~]#
# 命令别名属于内建命令,所以别名仅在其被定义的shell进程中才有效。
在定义好别名之后,你随时都可以在shell中使用它,就算在shell脚本中也没问题。
但要注意,因为命令别名属于内建命令,一个别名仅在它所被定义的shell进程中才有效。
[root@Fedora-Desktop ~]# alias li='ls -li' --color=auto
[root@Fedora-Desktop ~]# li
总计 32
6034 -rw-------. 1 root root 480 5月 4日 12:02 anaconda-ks.cfg
406259 -rw-r--r-- 1 root root 10240 7月12日 10:56 doc.tar
406255 drwxr-xr-x 1 root root 0 7月12日 10:54 documents
406257 drwxr-xr-x 1 root root 0 7月12日 10:55 music
406260 -rw-r--r-- 1 root root 10240 7月12日 10:56 music.tar
406256 -rw-r--r-- 1 root root 0 7月12日 10:55 zy.mp4
402585 -rw-r--r-- 1 root root 62 7月10日 10:04 zy.txt
[root@Fedora-Desktop ~]# zsh
[root@Fedora-Desktop]~# echo $0
zsh
[root@Fedora-Desktop]~# li
bash: li: 未找到命令...
[root@Fedora-Desktop]~# exit
[root@Fedora-Desktop]~#
# 取消别名
[root@Fedora-Desktop ~]# unalias li
[root@Fedora-Desktop ~]# echo $0
-bash
[root@Fedora-Desktop ~]# li
bash: li: 未找到命令...
[root@Fedora-Desktop ~]#
unalias命令的作用通常是临时的,仅在当前shell会话中有效。如果你想永久删除别名,你需要编辑配置文件,如.bashrc
, .bash_profile
, 或者 .zshrc
(取决于你使用的shell),并从文件中删除或注释掉定义该别名的行。然后,你需要重新加载配置文件或新开一个shell会话以使改动生效。例如,如果你使用Bash,可以执行:
shell
[root@Fedora-Desktop ~]# source ~/.bashrc