Appearance
理解shell
shell不只是一种简单的CLI, 他是一个时刻在运行的复杂的交互式程序
shell的类型
启动什么样的shell取决于个人用户ID的设置, 在/etc/passwd
文件中
bash
6jiao:x:1000:1000:jiao,,,:/home/jiao:/bin/bash
-rwxr-xr-x 1 root root 1392424 10月 7 2021 /bin/bash*
是一个可执行文件, 一般还包含有其他的shell, 但是由于bash shell广为流传, 所以很少使用别的shell作为默认的shell
默认的shell会在用户登录的时候启动, 不过还有一个默认的shell是/bin/sh, 作为默认的系统shell, 用于那些需要启动时候使用的系统shell脚本
有的发行版会把默认的系统shell使用软连接设置为bash shell,
在有的发行版上默认的系统shell和交互shell并不相同
bash
lrwxrwxrwx 1 root root 4 3月 11 21:38 /bin/sh -> dash //Ubuntu使用dash
对于shell脚本来说, 由于shell的不同, 会导致问题
可以更改默认的交互shell, 直接输入文件名就可以了, 使用exit退出
bash
jiao@jiao-virtual-machine:~/桌面/linux-shell/3$ /bin/dash
$ ls
3 abb acc baa num.txt.gz test.tar.gz tset
$ cd ..
$ ls
1 3 test.tar
$ exit
shell的父子关系
用于登录某个虚拟控制器终端或在GUI中运行终端仿真器时所启动的默认的交互shell,是一个父shell。父shell提供CLI提示符,然后等待命令输入。
在CLI提示符后输入/bin/bash命令或其他等效的bash命令时,会创建一个新的shell程序。这个shell程序被称为子shell(child shell)。子shell也拥有CLI提示符,同样会等待命令输入
bash
$ ps --forest
PID TTY TIME CMD
2753 pts/0 00:00:00 bash
6135 pts/0 00:00:00 \_ dash
6139 pts/0 00:00:00 \_ ps
bash shell程序可以使用命令行参数修改shell的启动方式
参数 | 描述 |
---|---|
-c string | 从string中读取命令并进行处理 |
-i | 启动一个能够接收用户输入的交互shell |
-l(小写L) | 以登录shell的形式启动 |
-r | 启动一个受限shell,用户会被限制在默认目录中 |
-s | 从标准输入中读取命令 |
进程列表
你可以在一行中指定要依次运行的一系列命令。这可以通过命令列表来实现,只需要在命令之间加入分号(;)即可
进程列表是一种命令分组(command grouping)。另一种命令分组是将命令放入花括号中,并在命令列表尾部加上分号(;)。语法为{ command; }。使用花括号进行命令分组并不会像进程列表那样创建出子shell。
bash
pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls
这不是一个进程列表
bash
(pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls )
这是一个进程列表, 会产生一个子shell来运行
bash
(pwd ; ls ; cd /etc ; pwd ; cd ; pwd ; ls ; echo $BASH_SUBSHELL)
最后一个命令可以显示运行的shell数量
可以使用多个括来创建子shell的子shell, 在脚本中经常使用子shell来进行多进程处理, 但是采用子shell的成本不菲,会明显拖慢处理速度。在交互式的CLI shell会话中,子shell同样存在问题。它并非真正的多进程处理,因为终端控制着子shell的I/O
别出心裁的shell用法
在交互式的shell CLI中还有很多用法, 进程列表, 协程, 管道等
后台模式
在后台模式运行命令可以在处理命令的同时让出CLI, 想要进入后台模式, 可以在末尾加上&
bash
jiao@jiao-virtual-machine:~$ sleep 3000 &
[1] 2778
显示为后台作业一号, 任务标号为2778
bash
$ jobs
[1]+ 运行中 sleep 3000 &
显示所有的后台任务
-l(小写L): 显示详细的信息, 显示任务号
把进程列表置入后台
进程列表是运行在子shell中的一条或多条命令
bash
jiao@jiao-virtual-machine:~$ (sleep 2 ; echo $BASH_SUBSHELL ; sleep 2)
1
jiao@jiao-virtual-machine:~$ (sleep 2 ; echo $BASH_SUBSHELL ; sleep 2)&
[2] 2818
jiao@jiao-virtual-machine:~$ 1
[2]+ 已完成 ( sleep 2; echo $BASH_SUBSHELL; sleep 2 )
jiao@jiao-virtual-machine:~$
把进程;列表放入后台就可以在子shell中处理繁重的任务, 也可以让子shell的I/O受到终端控制
协程
同时做两件事, 后台生成一个子shell, 并在子shell中执行任务
bash
$ coproc sleep 10 [1] 2544
[2] 2850
$ jobs
[1]- 运行中 sleep 3000 &
[2]+ 运行中 coproc COPROC sleep 10 &
coproc COPROC sleep
是coproc
进程起的名字, 可以使用扩展语法来对其命名
bash
jiao@jiao-virtual-machine:~$ coproc My_Job { sleep 10; }
[2] 2874
jiao@jiao-virtual-machine:~$ jobs
[1]- 运行中 sleep 3000 &
[2]+ 运行中 coproc Myjob { sleep 10; } &
jiao@jiao-virtual-machine:~$
[2]+ 已完成 coproc Myjob { sleep 10; }
必须确保在第一个花括号({)和命令名之间有一个空格。还必须保证命令以分号(;)结尾。另外,分号和闭花括号(})之间也得有一个空格。
生成子shell的成本不低,而且速度还慢。创建嵌套子shell更是火上浇油
理解shell的内建命令
type命令
用来查看命令是内部命令还是外部
-t: 监测种类
- 别名: alias
- 内部命令: builtin
- 外部命令: file
-p: 是外部命令才会显示文件名
-a: 会在环境变量中把文件找出来, 包括别名
外部命令
也称为文件系统命令, 存在于bash shell之外的程序, 不是bash shell的一部分, 外部命令通常位于/bin, /usr/bin, /sbin, /usr/sbin中
可以使用which和type命令查看
bash
jiao@jiao-virtual-machine:~/桌面$ which ps
/usr/bin/ps
jiao@jiao-virtual-machine:~/桌面$ type -a ps
ps 是 /usr/bin/ps
ps 是 /bin/ps
当外部的命令执行的时候会创建一个新的子进程, 需要花费精力来配置新的子进程的环境
创建的进程之间可以使用信号来通讯
内建命令
不需要子进程来进行, 已经和shell编译为一个整体, 作为shell的工具
bash
jiao@jiao-virtual-machine:~/桌面$ type exit
exit 是 shell 内建
jiao@jiao-virtual-machine:~/桌面$ type cd
cd 是 shell 内建
内建命令的效率更高
有的命令会有多种实现
bash
jiao@jiao-virtual-machine:~/桌面$ type -a echo
echo 是 shell 内建
echo 是 /usr/bin/echo
echo 是 /bin/echo
history
显示使用过的命令列表
可以修改HISTSIZE环境变量来确定保存的记录数量
使用!!可以再次使用上一条命令
使用过的命令保存在.bash_history
, 位于用户的主目录之中, 历史记录先存放在内存之中shell退出的时候才会写入
可以使用
history -a
命令强制写入
如果打开了多个终端, 使用-a添加之后其他的终端不会更新, .bash_history只有在第一次被打开的时候才会被读取, 可以使用
history -n
更新
使用!n, 可以再次使用记录中的第n个命令
命名别名
alias是shell的内建命令, 允许你为常用的命令起别名
bash
alias -p
查看已经存在的别名
bash
alias li='ls -l'
由于是内部命令, 所以可以在脚本中使用, 但是只能在定义他的shell中, 命令是暂时有效的, 保存在~/.bashrc或者/etc/bashrc之后使用source ~/.bashrc就可以