Linux Bash Shell
Shell是什么
操作系统管理着整个计算机的运行,协调着各部分的工作。操作系统的核心是被保护着的,如果我们想要和操作系统核心交互,就需要通过Shell(壳——就像核心外的一层外壳啦)来沟通(提供了使用操作系统的接口)。另外,我们还可以通过Shell调用其他的程序,让这些程序调用核心来完成我们的工作。Linux中的图形界面不属于系统的一部分,只算是一组应用程序,所以并不是万金油,很多时候我们依旧得使用Shell来控制系统。
BASH(Bourne Again SHell——sh(Bourne SHell)的增强版)就属于一种Shell程序,不同的SHell语法有区别,不过当前Linux默认使用的是sh/bash,系统中还有tcsh(cshell的强化版),不过我们还是先从bash学起吧。
用户登录后取得的shell记录在/etc/passwd中
|
|
Bash shell的功能
bash提供的功能
bash注要相容于sh,并且有一些增强的功能。
历史命令
通过上下键可以调出历史用过的命令。记录在~/.bash_history中。
- history n 列出最近的n条历史指令
- -c 清空当前shell的历史指令
- -a将目前新增的history指令记入histfiles文件中,如果没加文件名,默认使用 ~/.bash_history
- -r从histfiles的内容读取到当前shell的history中
- -w 将目前历史指令写入histfiles 覆写
- !number 执行历史记录中的第number条指令
- !comand 由最近的指令向前搜寻,找到以command开头的指令并执行
- !! 执行上一条指令
如果一个账户同时打开了多个bash,那么最后一次登出的时候写入的history文件会覆盖之前的。
命令与文件名补全
使用tab可以补全命令与文件名,一串指令的第一个词里面使用tab为命令补全,第二个词后面则为文件名补全。通过bash-completion可以在某些指令后面使用tab补全选项/参数。
alias 命令别名设置
使用个alias可以为我们的命令设置别名。
alias la='ls -al'
,还可以修改现有的指令alias rm='rm -i'
这样写也是可以的。以bash登录系统时,系统会从*~/.bash_history*中读取之前的历史指令。变量HISTFILESIZE描述了会记录最近的多少条记录。工作控制
可以让程序在后台执行。
脚本
shell scripts可以将我们需要使用的连续指令写到一个文件里面,批量执行,可以与用户交互式的进行,也可以依据参数和环境变量自动执行。
万用字符与特殊符号
bash提供的万用字符与特殊符号:
万用符号:
* 代表0到无穷个任意字符
? 代表一定有一个任意字符 ??? 名字为三个字符的文件
[] 代表一定有一个括号内的字符,[abcd]代表一定有一个字符是a,b,c,d内的任何一个。
[-],- 代表编码顺序中连续的所有字符 如 0-9,括号的作用和上面一样。
ls /etc/*[0-9]*
[^],若[]中的第一个字符为^,则代表排除括号中的任意一个字符。(除a,b,c,d外都接受)
ls /etc/[^a-z]*
排除小写字母开头的文件。
特殊符号:
- # 注解
- \ 转义
- | 管线
- ; 连续指令分隔
- ~ 主文件夹
- $ 取变量
- & 后台运行程序
- ! 非
- / 目录符号
- > ,>>,<,<< 数据重定向
- ’’ 单引号,内部忽略取变量
- "" 双引号,内部取变量$有效
- () 中间包含子shell
- {} 命令区块
bash的内置指令
bash内置了很多的指令,如cd umask等指令起始都是bash内置的指令,而不是外部的程序。
|
|
对比一下可以外部指令和内建指令。
下达指令
- 多行指令 使用
\Enter
来输入多行指令,回车一定要接着反斜杠。 - ctrl+u 删除光标之前的指令 ctrl+k删除光标之后的指令
- ctrl+a光标移动到指令串的最前面 ctrl+e 移动到最后后面
限制资源使用
之前不知道为什么linux会限制打开的文件数量,还会出现打开文件过多的错误。今天了解到还是有一定的必要加以限制的。考虑到linux是多任务多用户的系统,要是用户一多,并且同时打开了很多的比较大的文件,可能会消耗掉大量的内存,甚至多余主机的内存,这样会对系统造成很大的影响,所以bash可以通过ulimit
限制使用者对于某些系统资源的使用。 包括打开的文件数量,可以使用的CPU时间,可以使用的内存总量。
|
|
另外可以通过pam来管理使用者的ulimit限制。
Bash变量
linux作为多用户多任务系统,每个人登陆时都能取得一个shell,而每个人都有自己的环境变量。变量即用一个简单的字符串代表一个复杂或者经常变动的数据(当然你要是弄一个复杂的字符串也是可以的。),变量是可变的,所以程序可以不用把参数写死,可以接受系统中的变量,增加了灵活性。
某些环境变量还能影响到bash本身。如PATH变量,bash会在PATH变量中的路径中寻找可执行的程序指令。*bash的变量提供了与系统进行交互时需要的部分参数。*为了区分环境变量与普通变量,环境变量
一般使用大写。另外在shell脚本中也可以使用变量,放在脚本开始处,这样之后需要修改一项参数的时候就不用一一替换了。
变量的查看与修改
查看变量内容
使用
echo $var
可以查看变量var,$或${}的作用是取变量的内容。变量赋值
设用
=
连接变量与想要设置的内容就可以设置变量的内容了,等号两边不能有空格。变量名不能以数字开头,如果内容有空格需要使用引号括起来。双引号内的像是$这样的特殊字符还会保持它们原有的功能(可以使用\对特殊符号进行转义 im= very\ hungry),单引号中的内容则仅当作纯文本处理。如果一串指令执行中还需要获得其他指令执行提供的信息,可以使用反引号将指令围起来或者使用$(指令),如 version=$(uname -r)
扩充变量
如果想要在一个变量后面追加内容,可以在等号右边使用$取出变量并补充。
PATH=${PATH}:/home/bin
如果变量需要在其他子程序中使用,需要用export使其变为环境变量。 运行脚本程序时,系统会创建一个子shell(新的进程,新的PID) ,普通变量只在被定义的shell内有效,而在子shell中无效,而export添加的环境变量则会在创建子shell时,在子shell中定义该变量的一个拷贝。 环境变量会被子程序继续引用。
取消变量
unset 变量名称 可以清除一个变量。
另外在一串指令中反引号中的命令会优先执行,执行结果会作为外部输入的信息,类似于$()的作用。
变量持久化
可以把变量写入bash配置文件 ~/.bashrc中,之后每次登录,这个变量都会被设置了。
环境变量的功能
使用env
或者export
可以查看当前shell环境的环境变量。
其中有一些环境变量是比较重要的,可以单独拿出来记录一下。
HOME
代表使用者的主文件夹——
cd ~
进入的文件夹。SHELL
表明当前SHELL环境使用的是哪个SHELL程序,默认使用/bin/bash
MAIL
表示使用mail收信时,系统回去读取哪个邮箱的文件。
PATH
代表可执行文件的搜寻路径
LANG
语系数据,某些程序会根据它来确定编码方式,遇见某些语系可能会出现程序无法解析当前语系编码的错误。
RANDOM
代表着一个随机数的变量,通过
/dev/random
取得一个随机数,介于0~32767。declare -i number=$RANDOM*10/32768; echo $number
可以得到一个-~9之间的数。
另外还可以使用set
查看当前shell中的所有变量。除了环境变量,与shell操作接口相关/系统内设置的变量一般也设置成大写。下面这些是系统中比较重要的变量:
PS1
命令提示符的格式变量,命令提示符即
root@raspbian:~#
这样的一串字符。$
$本身也是一个变量,表示当前Shell的PID(进程ID)
?
上一个执行的指令的返回值(return的值),如果正常执行,一般返回的是0,如果出错,得到的就是错误代码。
语系变量locale
locale -a
可以查看我们的系统当前支持多少种语系。
|
|
如果没有单独设置,只要设置了LANG或者LC_ALL,那么未设置的部分就会被这两个变量取代,这两个是最主要的。Linux的终端接口tty只不支持中文显示,所以如果在tty里面修改成了中文,那中文部分全部都会变成乱码。
系统的默认语系设置位于/etc/locale.conf
中(不过debian中好像不是这样)。
变量作用范围
环境变量基本可以看作全局变量,而自订的变量可以当作局部变量来看待。不过并不是只有环境变量才能传递到子shell中,“环境变量”不等于“bash的环境”,PS1并非环境变量,但是也会影响到bash的接口。
变量的键盘读取
可以让用户通过键盘输入来设置变量,还可以宣告这个变量的属性。
read
使用read指令读取来自键盘输入的变量,一般用于脚本的编写。read -p 提示字符 -t 等待时间 变量名
declare/typeset
用于宣告变量类型
-a 定义为数组
array类型,var[index]=content 用于声明一串相关的变量
-i 定义为整数数字
-x 将后面的变量变成环境变量 +x 设置成非环境变量。
-r 变量设置成只读类型(需要重新登录才能恢复)
Bash操作环境
指令执行顺序
指令的搜寻与执行是有顺序的,从上到下优先级依次降低:
- 相对/绝对路径执行的指令
- 由alias找到的指令
- bash内置的指令
- 从$PATH变量指定的路径中,按顺序找到的第一个指令。
登录时的欢迎信息
我们在登录某些系统的时候可以看到一串欢迎信息,他们是在/etc/issue
以及/etc/motd
里面设置的,issue里面可以使用反斜杠取用一些包含了系统信息的变量。文件夹下还有一个issue.net,那是在通过telnet登录时显示的信息。 而如果想在用户登录后告诉用户一些信息,可以编辑/etc/motd
。
环境配置文件
当我们登录bash后,默认就有了很多的环境变量,这些变量是在环境配置文件中设置的。bash在启动时会读取这些文件。配置文件分为全体系统配置文件和个人偏好配置文件,上面的alias,设置的变量,在登出bash后久会失效,只有把他们写入到配置文件中才能保留下来。
login与nologin shell
通过完整的流程取得的shell,称为login shell,而nologin shell则是不需要重复登录而取得的shell。 如:在bash中执行bash,会打开一个新的bash shell,或者在图形界面中打开一个bash shell,都属于nologin shell。
这两种shell读取的配置文件是不一样的。
login shell读取的配置文件
不通发行版的配置文件,配置文件中读取的配置文件可能会有一些区别,但是总体上的作用和意义是相同的。
login shell一般只读取下面的配置文件:
/etc/profile ,系统的整体设置,除非要修改全局设置,不然不建议修改
1 2 3 4 5 6
if [ "`id -u`" -eq 0 ]; then PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" else PATH="/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games" fi export PATH
节选一部分,可以看到,该文件也是一个脚本,通过用户的UID设置不同的PATH。
另外,
/etc/profile
里面还会调用外部的文件,比如观察可以看到,脚本中还依次执行了/etc/profile.d/
下的文件,进行颜色,语系,命令别名,bash命令补全等等参数的设置以及初始化。总的来说,虽然整体环境配置文件看上去只读取了/etc/profile,但是profile又会调用更多的配置文件。~/.bash_profile 或者 ~/bash_login 或者 ~/.profile
为什么用的是或者?因为bash login shell设置只会按照上面的顺序读取三个文件中的一个,读到了一个就不会读剩下的文件。
这几个文件是属于使用者个人的配置文件,这个文件有时候可能也会调用外部的配置文件如*.bashrc*.在我的系统(debian10)中,/etc/profile与~/.profile中的内容都比较简单,大部分的设置都是在.bashrc中执行的。
bash是使用source
指令来读取这些环境配置文件的,在修改配置文件后我们也可以使用source使这些配置文件生效,而无需重新登录。另外使用. ~/.bashrc
也可以读入配置文件。我们也可以设置一些自定义的环境配置文件,在不同的工作需求下使用不同的环境配置。
nologin shell读取的配置文件
nologin shell在取得shell时仅会读取~/.bashrc
配置文件。
|
|
看得出bashrc中也会调用其他的配置文件,root和一般用户的.bashrc也有着一定的区别。
其他配置文件
/etc/man_db.conf 或 /etc/manpath.config (debian中)
规定了使用man的时候前往哪里寻找man page。
~/.bash_history
记录了历史输入指令
~/.bash_logout (debian下没发现这个文件)
记录了用户登出bash之后,系统需要执行的操作
终端机环境配置
我们登录终端的时候,会取得一些关于终端的输入环境的设置(如按键对应的功能),某些类unix系统中,可能会把删除字符设置为del而不是退格键,不过这个设置我们也是可以自己选修改的。这需要用到stty
设置终端命令来进行设置。另外bash也有一些关于终端机的设置,使用到set
进行设置。
数据流重定向
标准输出与标准错误输出
标准输出指的是指令执行回传的正确信息,标准错误输出则为指令执行失败后回传的错误信息。这两个输出默认都是输出到屏幕上的,不过我们可以将stdout与stderr重定向到其他的地方(设备或者文件)。
- 标准输入 stdin 代码为0, 使用<或者«
- 标准输出 stdout 代码为1, 使用>或者» (表示追加而不是直接覆盖)
- 标准错误输出 stderr 代码为2 使用 2>或者2»
代码和符号之间不要加空格!。经常能看到的2>&1 表示错误输出等同于标准输出的重定向,不要使用1>file 2>file的写法,这样写的话,两种数据是同时写入文件的,可能会出现交叉写入/次序混乱的情况,文本中的顺序与实际屏幕上显示的顺序会不一样。 如果两种输出写到一个文件还可以写作 &>file
。
标准输入
原本的标准输入来自于键盘,使用该符号可以将其重定向到文件。
« 用来表示结束输入的字符,遇到这个字符的时候,输入就会结束了。
重定向通常用途
重定向通常用于
- 需要保存输出的信息
- 后台执行程序,不希望输出干扰到屏幕
- 想要丢弃一些不需要的信息 > /dev/null
- 错误信息与正确信息分别输出
管道命令
管道命令|
,将一个指令的标准输出(不包含stderr)作为下一个命令(需要能接受stdin)的输入。如ls /etc | less
。如果想让管道能够处理stderr,可以使用2>&1
,让stderr的输出等同于stdout。
管道命令通常配合下面这些命令使用:
配合使用的命令
减号 - 的用途
有时在用到了管道指令的地方我们还能看见减号的出现。
某些指令需要用到文件名称进行处理,如tar -cvf filename /home
将/home打包成filename,但是如果我们想要和管道一起用,那么这条指令我们就不需要它输出文件,此时该指令的stdout与管道后面的一条指令的stdin可以用 - 来替代。tar -cvf - /home | tar -xvf - -C /tmp
。还有就是我们执行网络上的脚本时也很常见到减号的使用 .
数据撷取
cut
cut可以将信息的某一段给切出来,以行为单位进行处理
-d 后面接分隔符
-f -d指定的分隔符把一段文字切为数段,-f指定取出第几段(从1开始) 用于处理使用了分隔符的数据。
-c 以字符为单位取出固定的字符区间 -c 20- 20个字符后的数据 用于处理长度规整的数据。
如”切开“ PATH变量
echo $PATH | cut -d ':' -f 3,5
grep
用于从文本中取出想要的信息
grep [-acinv] [--color=auto] '搜索字符串' filename
。- -a 二进制文件以文本方式搜索数据
- -c 计算找到要搜索的串的次数 (加了这个,输出的就是次数)
- -i 忽略大小写
- -n 输出行号
- -v 反向选择,显示没有”搜索字符串“的行
- –color=auto 搜索的字符串加上色彩标明
- 还有很多支持的语法,比如正则表达式等等…
last | grep -n "root"
查看root登录的记录。
排序
sort [-fbMnrtuk] file
-f 忽略大小写
-b 忽略前面的空白字符
-M 以月份的名字(JAN,DEC)来进行排序
-n 以纯数字进行排序
-r 反向排序
-u unique,相同的数据仅出现一次
-t 分隔符号 默认使用tab分隔
-k 以指定区间进行排序 (与t结合,如很多行以:分隔的文本排序,若要依据第三部分来进行排序,
sort -t ':' -k 3
)uniq
用于去除重复内容
-i 忽略大小写
-c 进行计数
如获取每个账户的登录次数
last | cut -d ' ' -f 1| sort|uniq -c
(需要先排序)1 2 3 4 5 6
root@raspbian:~# last | cut -d ' ' -f 1| sort | uniq -c 1 27 pi 24 reboot 95 root 1 wtmp
wc
计算一个文本中有多少个字,多少行(默认全部输出)。
-l 仅列出行
-w 仅列出字符
-m 多少字符
查看登录总人数的命令,去除了无关信息,空白,注意使用反向选择排除
last | grep [a-zA-Z] | grep -v 'wtmp' | grep -v 'reboot' | grep -v 'unknown' | wc -l
查看系统中有多少个账号
cat /etc/passwd | wc -l
双向重定向
>
会将数据流整个传送给文件或设备,所以我们重定向后就没办法继续利用这个数据流了,而使用tee
可以让数据流保存一份到文件中之后继续利用数据流。
tee [-a] file
,-a累加方式,将数据加入文件中。
last | tee last.list | cut -d " " -f1
将last的输出存到文件中的同时,还能继续利用数据流进行撷取操作。
字符转换指令
tr
用来删除一段文本中的特定文字,或者替换。
-d str 删除str -s 取代重复的字符。
tr ‘[a-z]’ ‘[A-Z]’ 小写转大写(替换)
col
很多时候可以用来将tab转为空格(-x参数)
join
将两个文件中有相同数据的一行连接在一起。
paste
不经过比对直接将两个文件中的,同一行合并在一起,以tab分开。
expand
将tab转为空格
分区命令
split [-bl] file PREFIX
该命令可以将一个大文件分为多个小文件(依据行数或者大小)
-b 分区成的文件大小 可用单位b k m等
-l 以行数来分区
之后想要合并的话就使用 cat PREFIX* >> siglefile
即可
参数替换指令
xargs
可以读入stdin并以空格或者分隔符作为依据,将stdin的数据分隔成一个个参数。并作为后面的命令的参数(有些命令不支持管道命令,可以使用xargs间接的将管道传输过来的指令放到作为命令的参数)
find /usr/sbin -perm /7000 | xargs ls -l
命令执行
一行执行多个命令
使用;分隔
&& 与 || 前后指令相关
如果两个指令彼此之间是相关的,如后者指令的执行取决于前者的执行结果,就要用到这两个符号了。 后面的指令会受到前一个指令执行的返回值($?)影响。如
cmd1 && cmd2
如果cmd1正确执行,接着执行cmd2,cmd1执行返回错误值($?不等于1),cmd2不执行。 比如在判断目录存在时创建文件
cmd1 || cmd2
cmd1正确执行,cmd2不执行;cmd1执行失败才会继续执行cmd2 比如用于判断目录不存在时创建目录。
混合起来,
ls /tmp/files && echo "exist" || echo "not exist"
,注意两个符号的顺序是不能交换的。