shell是一个程序,程序加载后启动一个shell进程,以命令行提示符的形式跟用户交互, shell不单单是一种CLI。它是一个时刻都在运行的复杂交互式程序
shell的类型
系统启动什么样的shell程序取决于你个人的用户ID配置 , 在/etc/passwd文件中,在用户ID记 录的第7个字段中列出了默认的shell程序 ,例:
root:x:0:0:root:/root:/bin/bash
7 个字段的对应关系如下:
- 登录名:登录系统时,用户所必须输入的唯一名称
- 经过加密的密码:该字段包含的是经过加密处理的密码,长度为 13 个字符 ,这里显示字母“x”, 而经过加密处理的密码实际上却存储到 shadow 密码文件中
- 用户 ID(UID) : 用户的数值型 ID
- 组 ID(GID):用户属组中首选属组的数值型 ID。
- 注释:该字段存放关于用户的描述性文字
- 主目录:用户登录后所处的初始路径。
- 登录 shell:一旦用户登录,便交由该程序控制。
在Linux系统上,通常有好几种Linux shell可用 ,不同的shell有不同的特性,有些更利于创建脚本,有些则更利于管理进程。如下:
- ash 一种运行在内存受限环境中简单的轻量级shell,但与bash shell完全兼容
- korn 一种与Bourne shell兼容的编程shell,但支持如关联数组和浮点运算等一些高级的编程特性
- tcsh 一种将C语言中的一些元素引入到shell脚本中的shell
- zsh 一种结合了bash、tcsh和korn的特性,同时提供高级编程特性、共享历史文件和主题化提示符的高级 shell
所有Linux发行版默认的shell都是bash shell , 你经常会看到某些发行版使用软链接将默认的系统shell设置成bash shell ,比如我电脑的CentOS发行版:
[root@help ~]# ls -l /bin/sh lrwxrwxrwx 1 root root 4 Dec 14 23:41 /bin/sh -> bash
最左侧的l代表软连接
外部命令&内建命令
[root@help ~]# type cd
cd is a shell builtin
[root@help ~]# type ps
ps is hashed (/usr/bin/ps)
注:cd就是内建命令,而ps是外部命令,那有什么区别呢
外建命令:有时候也被称为文件系统命令,是存在于bash shell之外的程序 , 当外部命令执行时,会创建出一个子进程。这种操作被称为衍生(forking)。
内建命令:它们已经和shell编译成了一 体,作为shell工具的组成部分存在, 执行不会新建shell子进程。
shell的父子关系
用于登录某个虚拟控制器终端或在GUI中运行终端仿真器时所启动的默认的交互shell,是一 个父shell , 父shell提供CLI提示符,然后等待命令输入 。
在生成子shell进程时,只有部分父进程的环境被复制到子shell环境中。这会对包括变量在内的一些东西造成影响
[root@help /]# bash [root@help /]# bash [root@help /]# bash [root@help /]# ps --forest PID TTY TIME CMD 14128 pts/6 00:00:00 bash 3031 pts/6 00:00:00 \_ bash 3046 pts/6 00:00:00 \_ bash 3060 pts/6 00:00:00 \_ bash 3108 pts/6 00:00:00 \_ ps
在上面的例子中,bash命令被输入了三次。这实际上创建了三个子shell。ps -forest命令 展示了这些子shell间的嵌套结构
进程列表
你可以在一行中指定要依次运行的一系列命令。这可以通过命令列表来实现,只需要在命令
之间加入分号(;)即可,要想知道是否生成了子shell,得借助一个使用了环境变量的命令echo $BASH_SUBSHELL。例:
[root@help /]# pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls ; echo $BASH_SUBSHELL / bin dev get-pip.py lib lost+found mnt opt root sbin sys usr boot etc home lib64 media myredis proc run srv tmp var /etc /root : format_schemas metadata_dropped status yarn.lock 555.txt FreebuyApplication mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz store data iZwz9hegfy8iwwrwssss7kZ.pid nacos tmp dictionaries_lib logs node_modules user_files flags metadata preprocessed_configs work 0
在命令输出的最后,显示的是数字0。这就表明这些命令不是在子shell中运行的。
[root@help ~]# (pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls ; echo $BASH_SUBSHELL) /root : format_schemas metadata_dropped status yarn.lock 555.txt FreebuyApplication mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz store data iZwz9hegfy8iwwrwssss7kZ.pid nacos tmp dictionaries_lib logs node_modules user_files flags metadata preprocessed_configs work /etc /root : format_schemas metadata_dropped status yarn.lock 555.txt FreebuyApplication mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz store data iZwz9hegfy8iwwrwssss7kZ.pid nacos tmp dictionaries_lib logs node_modules user_files flags metadata preprocessed_configs work 1
这次在命令输入的最后显示出了数字1。这表明的确创建了子shell,并用于执行这些命令
别出心裁的子 shell 用法
在交互式的shell CLI中,还有很多更富有成效的子shell用法。进程列表、协程和管道都利用了子shell。
在交互式shell中,一个高效的子shell用法就是使用后台模式,
探索后台模式
什么是后台模式?
在后台模式中运行命令可以在处理命令的同时让出CLI,以供他用,演示后台模式的一个经
典命令就是sleep。
而要想将命令置入后台模式,可以在命令末尾加上字符&,把sleep命令置入后台模式可以让我
们利用ps命令来小窥一番。
[root@help ~]# sleep 20& [1] 12995 [root@help ~]# ps -f UID PID PPID C STIME TTY TIME CMD root 12920 12915 0 22:39 pts/9 00:00:00 -bash root 12995 12920 0 22:39 pts/9 00:00:00 sleep 20 root 12998 12920 0 22:39 pts/9 00:00:00 ps -f
sleep命令会在后台(&)睡眠20秒,当它被置入后台,在shell CLI提示符返回
之前,会出现两条信息。第一条信息是显示在方括号中的后台作业(background job)号(1)。
第二条是后台作业的进程ID(12995)。除了ps命令,你也可以使用jobs命令来显示后台作业信息。
[root@help ~]# sleep 20& [1] 13432 [root@help ~]# jobs -l [1]+ 13432 Running sleep 20 &
一旦后台作业完成,就会显示出结束状态
[root@help ~]# jobs -l [1]+ 13432 Done sleep 20
将进程列表置入后台
[root@help ~]# (sleep 2 ; echo $BASH_SUBSHELL ; sleep 2)
1
[root@help ~]#
在上面的例子中,有一个2秒钟的暂停,显示出的数字1表明只有一个子shell,在返回提示符
之前又经历了另一个2秒钟的暂停
将相同的进程列表置入后台模式会在命令输出上表现出些许不同
[root@help ~]# (sleep 2 ; echo $BASH_SUBSHELL ; sleep 2)& [1] 14349 [root@help ~]# 1 [1]+ Done ( sleep 2; echo $BASH_SUBSHELL; sleep 2 ) [root@help ~]#
把进程列表置入后台会产生一个作业号和进程ID,然后返回到提示符。不过奇怪的是表明单
一级子shell的数字1显示在了提示符的旁边!不要不知所措,只需要按一下回车键,就会得到另
一个提示符
在CLI中运用子shell的创造性方法之一就是将进程列表置入后台模式。你既可以在子shell中
进行繁重的处理工作,同时也不会让子shell的I/O受制于终端
当然了,sleep和echo命令的进程列表只是作为一个示例而已。使用tar(参见第4章)创
建备份文件是有效利用后台进程列表的一个更实用的例子。
[root@help ~]# (tar -cf Rich.tar /home/rich ; tar -cf My.tar /home/christine)& [3] 2423
将进程列表置入后台模式并不是子shell在CLI中仅有的创造性用法。协程就是另一种方法。
协程
协程可以同时做两件事。它在后台生成一个子shell,并在这个子shell中执行命令。
要进行协程处理,得使用coproc命令,还有要在子shell中执行的命令
[root@help ~]# coproc sleep 10 [1] 15041
除了会创建子shell之外,协程基本上就是将命令置入后台模式。当输入coproc命令及其参
数之后,你会发现启用了一个后台作业。屏幕上会显示出后台作业号(1)以及进程ID(15041)。
jobs命令能够显示出协程的处理状态
[root@help ~]# jobs [1]+ Done coproc COPROC sleep 10
在上面的例子中可以看到在子shell中执行的后台命令是coproc COPROC sleep 10
[root@help ~]# coproc My_Job { sleep 10; } [1] 15387 [root@help ~]# jobs [1]+ Running coproc My_Job { sleep 10; } &
通过使用扩展语法,协程的名字被设置成My_Job。这里要注意的是,扩展语法写起来有点
麻烦。必须确保在第一个花括号({)和命令名之间有一个空格。还必须保证命令以分号(
;)结 尾。另外,分号和闭花括号(})之间也得有一个空格
你可以发挥才智,将协程与进程列表结合起来产生嵌套的子shel
[root@help ~]# coproc ( sleep 20; sleep 20 ) [1] 16547 [root@help ~]# jobs [1]+ Running coproc COPROC ( sleep 20; sleep 20 ) & [root@help ~]# ps --forest PID TTY TIME CMD 12920 pts/9 00:00:00 bash 16547 pts/9 00:00:00 \_ bash 16599 pts/9 00:00:00 | \_ sleep 16627 pts/9 00:00:00 \_ ps
生成子shell的成本不低,而且速度还慢。创建嵌套子shell更是火上浇油!
在命令行中使用子shell能够获得灵活性和便利。要想获得这些优势,重要的是理解子shell的
行为方式