shell的简单发展
基本上我们常用的是bash,它的全称是Bourne-Again SHell,而sh的全称则是Bourne shell,但是在系统中的/bin/sh其实是一个软连接,指向不同的shell(在ubuntu18上指向了dash,centos上指向了bash),在脚本中还是比较推荐写#!/bin/sh来指定shell的。
Bash的功能
- 可以查看历史
- 利用tab键进行补全命令或者文件,如果有插件的支持,还可以补全命令的选项
- 可以设置别名alias
- 控制jobs,这个比较推荐使用tmux
- wildcard的使用
ll /etc/a*
如果要查看一个命令的执行顺序,可以用type -a ls来查看,不仅可以告诉系统查询命令的顺序,还能告诉你指令是shell内置的。
变量
由于bash是非常古老的,所以它对变量的定义也是非常老的。如果我要看一个变量的值,推荐使用如下的命令:
1 | echo ${PATH} |
而定义的话则更加简单了,直接一个a=test就定义好了,这里的test默认是字符串类型的。有一个重要的规则,就是如果用双引号引起来,则$PATH代表的是这个变量的值,如果是单引号引起来,则代表了这个$PATH字符串。还有一个用法是$(ls)来执行命令,然后用该命令的结果来代替。同样如果要对变量增加一些值,直接加在后面就好了,PATH=${PATH}:/home/test/bin←这里的冒号是PATH里面的分隔符,并没有特殊含义。
如果不想要变量了,可以直接unset a来取消,如果希望变量成为环境变量,则用export a即可。
之前说了默认是字符串类型的,那如果希望是一个数字或者是一个数组呢?可以用declare来宣告某个变量的类型,比如declare -i a=100+200 这样a就会是300,而不是字符串的100+200。
环境变量
刚刚说了用export可以让一个普通的变量成为环境变量,那么环境变量的作用是什么呢。
举个例子,直接输入cd会回到用户的家目录,那是什么决定的呢?是由HOME这个环境变量来决定的,所以你只需要修改HOME=/etc,这样你直接一个cd就回到/etc目录下面了。比较重要的环境变量有
- HOME
- SHELL
- HISTSIZE
- PATH
- LANG
- RANDOM
这几个就是比较重要的环境变量,所有的环境变量可以通过env来进行查看,而所有的变量(包括用户自定义的和环境变量)则可以用set来查看。set显示的比较重要的变量应该就是PS1和PS2这两个提示符了吧:
- PS1:就是用户平时的提示符。
- PS2:第一行没输完,等待第二行输入的提示符。
语系问题
一般来说,出现乱码无非就是编码解码的问题,这里大部分都可以通过设置系统的语系来解决。可以通过直接file命令来查看某个文件的编码:

可以看到一个是utf-8的编码,另外一个是ISO-8859编码,而且还说了是换行符CRLF,说明是在windows下编辑的。
如果要设置语系,只需要设置LANG这个变量的值,然后让它成为环境变量即可生效。
变量内容修改
可以直接给变量进行一次新的赋值,当然也可以为变量进行新增,修改和覆盖,但是我看了语法,比较难记,所以感觉还是算了,毕竟可以通过最简单的、最暴力的方法:直接覆盖进行操作嘛~
这里真的用到过的是以前在装zsh插件的时候使用的一个
1 | git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions |
一开始我是没理解ZSH_CUSTOM后面的那个:-是什么意思的,为什么不是直接下载到目录里呢?其实这一步的目的是,如果ZHS_CUSTOM这个变量已经设置且不为空,那么就下载到这个变量所指示的目录里面去,否则就下载到后面那个目录里面去并且设置ZSH_CUSTOM为后面的那个路径。
| 变量配置方式 | str 没有配置 | str 为空字符串 | str 已配置非为空字符串 |
|---|---|---|---|
| var=${str-expr} | var=expr | var= | var=$str |
| var=${str:-expr} | var=expr | var=expr | var=$str |
| var=${str+expr} | var= | var=expr | var=expr |
| var=${str:+expr} | var= | var= | var=expr |
| var=${str=expr} | str=expr var=expr | str 不变 var= | str 不变 var=$str |
| var=${str:=expr} | str=expr var=expr | str=expr var=expr | str 不变 var=$str |
| var=${str?expr} | expr 输出至 stderr | var= | var=$str |
| var=${str:?expr} | expr 输出至 stderr | expr 输出至 stderr | var=$str |
上表摘录自鸟哥的网站。
同一个用户同时多次登录
不少时候会使用root同时登录一台服务器,那么当所有这些root退出之后,这个历史记录看上去似乎怪怪的,会丢失一部分,这是为什么呢?
每当root登录的时候,会读取之前的~/.bash_history的内容,写入到内存之后,然后他开始自己的工作,每次敲一个命令就会在在内存中增加一条新的记录(如果记录达到上限值还会删掉最老的一条),当root退出的时候才会把这个内存里面的数据写回到~/.bash_history中,所以理论上只有最后退出的那个root的命令才会写入到文件,才会被之后的history看到,因为它把之前的都覆盖掉了。
bash配置文件
这部分是最重要的,理解了这部分就理解了为什么用户刚进入bash就能有一些环境变量,就能理解我应该在哪里设置我的命令别名。
首先简单的把shell是否需要登录分成login shell与nologin shell,只需要查看一下/etc/passwd就能知道哪个用户对应的哪个shell。
login
如果是login的用户,则会去读取以下的两个配置文件:
- /etc/profile
- ~/.bash_profile ~/.bash_login ~/.profile 这三个文件依顺序读取,只要读取到了一个,就不读取之后的了。
但是你可以在这两个文件中写入一些操作,让shell再去读取别的文件,当然也是OK的。
nologin
这个仅仅会去读取~/.bashrc这个文件而已(rc的意思是run command)。
stty(setting tty)
其实一直好奇,为什么ctrl+d就代表了eof,为什么ctrl+c就能终止程序呢?一切都在stty -a这个命令里。

- intr=interrupt,会发送一个信号给程序来终止。这是为什么ctrl+c能够终止程序。
- kill在这里是清除tty内容的意思,所以ctrl+u能够清除所有的内容。
- eof=end of file,所以在文本输入的时候按下ctrl+d就能够结束。
要设置的话,就可以直接stty eof ^h,这样你就可以用ctrl+h来输入文件的结束符了。
管道符
管道符的作用是,把前一个命令的标准输出,作为标准输入给后面的一个命令。所以要求就是后面的那条命令需要接受标准输入,否则就不能使用。但是有个命令是xargs,可以将这个不可能化为可能。