认识bash这个shell
硬件、内核与shell
我们知道管理计算节硬件的其实就是操作下统的内核(kernel),一般用户只能通过shell来和内核沟通,以便让内核完成我们想完成的任务,那么系统有多少shell可用呢?为什么要使用bash?
举个例子,当用户需要计算机播放音乐时,这里的相关组件和操作有
硬件:具有发声能力的硬件设备,如有声卡的设备
内核管理:操作系统的内核可以支持这个芯片组,当然还需要提供芯片的驱动程序
应用程序:支持用户输入发生的命令
实际场景是,我们总需要用户使用操作系统,所以就有了在操作系统上面发展的应用程序,用户可以通过应用程序来指挥内核,从操作系统的角色来看,应用程序在最外层,因此也被称为壳程序(shell),我们可以理解shell是连接用户和内核的桥梁,将用户的输入传递给内核。
其实壳程序的功能只是提供用户操作系统的一个界面,因此壳程序还要能调用其他软件才可以,像之前提到的man、chmod、chown等命令都是独立的应用程序。但我们可以通过壳程序(命令行模式)来操作这些应用程序。
广义的壳程序:只要能够提供用户和内核沟通的软件都叫壳程序
狭义的壳程序:指命令行相关的软件,包括bash(后面会介绍)
为什么要学命令行模式的shell?
- 命令行模式的shell几乎所有的操作系统都一样
- 远程管理:命令行模式传输速度更快,不容易掉线会信息外流
系统的合法shell与/etc/shells功能
早年的UNIX年代发展着众多,shell依据发展者的不同就有许多的版本,简单说一下shell的历史吧。第一个流行的shell是Steven Bourne发展出来的,为了纪念他称为Bourne shell简称sh,后来另一个广为流传的shell由伯克利大学的Bill joy设计依附于BSD版的UNIX系统的shell,这个shell的语法类似C语言,因此得名C shell简称csh。Linux使用的这个版本称为【Bourne Again Shell】简称bash,这个是Bourne shell的加强版,也是具有GNU的架构发展出来的。
检查linux下可用的shell
[root@cnsz02vl29079 ~]# cat /etc/shells
/bin/sh
/bin/bash # Linux默认的shell
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh # 整合C Shell,提供更多功能
/bin/csh # 已经被 /bin/tcsh所替换
为什么系统合法/可用的shell要写在/etc/shells文件中?
因为系统某些服务在运行过程中,会检查用户能使用的shells,就是通过查询/etc/shells文件
那么用户什么时候可以取得shell来工作,默认取到的是哪个shell呢?
1.在终端登录linux时,就会取得一个shell
2.登录取得的shell就记录在/etc/passwd这个文件内
[root@cnsz02vl29079 ~]# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
。。。
centos:x:1000:1000:Cloud User:/home/centos:/bin/bash
这里记录很多,我们需要知道的是,每一行最后一个数据,就是登录后能获取的默认shell,可以看到root是/bin/bash,其他账号可能不同。
Bash shell的功能
目前Linux发行版的标准shell是bash,同时bash兼容于sh,并依据用户需求而加强的shell版本。bash主要的优点有:
- 历史命令(history)
- 命令与文件补全功能(Tab键)
- 命令别名设置功能(alias)
- 任务管理
- 程序化脚本:(shell scripts)
- 通配符
查询命令是否为Bash shell的内置命令:type
如何知道命令是来自于外部命令,或是内置在bash中?答案是,利用type命令
type [-tpa] name
选项与参数:
基本使用:不加任何参数时,type会显示name是外部命令还是bash内置命令
-t:加入该参数,type将name以下面字眼显示出它的意义:
file : 表示为外部命令
alias : 表示为命令别名
builtin: 表示为bash内置命令
-p:后面接外部的name为外部命令时,才会显示完整文件名
-a:会由PATH变量定义的路径中,将所有含name的命令都列出来,包含alias
[root@cnsz02vl29079 ~]# type cat
cat is hashed (/bin/cat)
[root@cnsz02vl29079 ~]# type ls
ls is aliased to `ls --color=auto'
[root@cnsz02vl29079 ~]# type cd
cd is a shell builtin
[root@cnsz02vl29079 ~]# type -t cat
file
命令执行与快速编辑按钮
换行输入长命令
命令行组合快捷键# 通过反斜杠\加Enter,注意:斜杠\是紧接Enter,中间无任何字符串
[root@cnsz02vl29079 ~]# cp /var/spool/mail/ \
>
命令 | 说明 |
---|---|
[ctrl] + u/[ctrl] + k | [ctrl] + u:从光标处向前删除命令串;[ctrl] + k:从光标处向后删除命令串 |
[ctrl] + a/[ctrl] + e | [ctrl] + a:光标移动到命令串最前面;[ctrl] + e:光标移动到命令串最后面 |
shell的变量功能
什么是变量?
通过特定字符代表不固定的内容
使用变量的好处
- 变量的可变性与方便性
- 影响bash环境操作的变量
-
变量的使用
读取变量 echo $变量名
[root@cnsz02vl29079 app]# echo $PATH
/app/jdk1.8.0_121/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
[root@cnsz02vl29079 app]# echo ${PATH}
/app/jdk1.8.0_121/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
[root@cnsz02vl29079 app]# echo $HOME
/root
设置变量 变量名=变量值
变量的设置规则 ```shell当变量名未被设置时,输出为空
[root@cnsz02vl29079 app]# echo $myname
[root@cnsz02vl29079 app]# myname=Shigl [root@cnsz02vl29079 app]# echo $myname Shigl
变量命名
[root@cnsz02vl29079 app]# 1myname=shigl bash: 1myname=shigl: command not found [root@cnsz02vl29079 app]# myname=shigl [root@cnsz02vl29079 app]# echo $myname shigl
1. 设置变量名时左右不能有空格
1. 变量名只能是英文字母与数字,但开头不能是数字
1. 变量内容可结合引号使用<br />单引号内的特殊字符如$,仅为一般字符(纯文本)<br />双引号内的特殊字符如$,可以保留原本含义(转义)
```shell
[root@cnsz02vl29079 app]# echo $LANG
en_US.UTF-8
[root@cnsz02vl29079 app]# myvar="echo lang $LANG"
[root@cnsz02vl29079 app]# echo $myvar
echo lang en_US.UTF-8
[root@cnsz02vl29079 app]# myvar='echo lang $LANG'
[root@cnsz02vl29079 app]# echo $myvar
echo lang $LANG
变量内容配合其他额外命令,通过单引号【
`命令
】或【$(命令)】,注意单引号指的是键盘1左边的那个逗号[root@cnsz02vl29079 app]# version=$(uname -r)
[root@cnsz02vl29079 app]# echo $version
3.10.0-693.17.1.el7.x86_64
变量扩增(在原变量值的基础上增加内容),可使用【”{变量}】在后面累加内容
如:PATH=”$PATH”:/home/bin或PATH={PATH}:/home/bin[root@cnsz02vl29079 app]# echo $version
3.10.0-693.17.1.el7.x86_64
[root@cnsz02vl29079 app]# version=${version}:sss
[root@cnsz02vl29079 app]# echo $version
3.10.0-693.17.1.el7.x86_64:sss
变量需要其他其他子程序执行,则需要以export来使变量成为环境变量
export PATH
export命令
用法 export [-fpm] [变量名]=[变量值]
参数说明:[root@cnsz02vl29079 app]# echo $myname
Shigl
[root@cnsz02vl29079 app]# export myname
[root@cnsz02vl29079 app]# export -p
declare -x CLASSPATH=".:/app/jdk1.8.0_121/lib/dt.jar:/app/jdk1.8.0_121/lib/tools.jar"
declare -x HISTCONTROL="ignoredups"
...
declare -x myname="Shigl"
[root@cnsz02vl29079 app]# export -p | grep myname
declare -x myname="Shigl"
[root@cnsz02vl29079 app]# export -n myname
[root@cnsz02vl29079 app]# export -p | grep myname
[root@cnsz02vl29079 app]# echo $myname
Shigl
- -f 代表[变量名称]中为函数名称。
- -n 删除指定的变量。变量实际上并未删除,只是不会输出到后续指令的执行环境中。
- -p 列出所有的shell赋予程序的环境变量。
说明:这里的子程序可以理解为子进程,通常主进程里的变量是无法在子进程中使用的,export将变量转成环境变量后是可以在子进程中使用的
[root@cnsz02vl29079 app]# echo $myname
Shigl
[root@cnsz02vl29079 app]# bash <== 进入所谓的子进程
[root@cnsz02vl29079 app]# echo $myname
[root@cnsz02vl29079 app]# exit <== 退出子进程
exit
[root@cnsz02vl29079 app]# echo $myname
Shigl
# export转为环境变量后
[root@cnsz02vl29079 app]# echo $myname
Shigl
[root@cnsz02vl29079 app]# export myname
[root@cnsz02vl29079 app]# bash
[root@cnsz02vl29079 app]# echo $myname
Shigl
- 通常大写字符为系统默认变量,自行设置的变量使用小写字符(仅为规范,并非强制)
- 取消变量的命令为unset: 【unset 变量】
通过变量名进入目录 ```shell [root@cnsz02vl29079 kernel]# cd /app/ [root@cnsz02vl29079 app]# cd /lib/modules/$(uname -r)/kernel/ [root@cnsz02vl29079 kernel]# pwd /lib/modules/3.10.0-693.17.1.el7.x86_64/kernel[root@cnsz02vl29079 app]# echo $myname
shigl
[root@cnsz02vl29079 app]# unset myname
[root@cnsz02vl29079 app]# echo $myname
实际相当于进行了两步操作
1.获取uname -r的路径 2.将1的结果带入到原命令
<a name="92586e80"></a>
### 环境变量的功能
查看环境变量
- env<br />列出所有环境变量
- export<br />同env作用相同,只是export还有其他额外的功能
```shell
[root@cnsz02vl29079 ~]# env
XDG_SESSION_ID=77716
HOSTNAME=cnsz02vl29079
SHELL=/bin/bash
HISTSIZE=300
...
SUDO_GID=1001
_=/bin/env
[root@cnsz02vl29079 ~]# export
declare -x HISTSIZE="300"
...
declare -x XDG_SESSION_ID="77716"
常见的环境变量
- HOME
代表用户的根目录,cd ~ 或cd进入的就是。 - SHELL
当前使用的shell是哪种程序。Linux默认使用/bin/bash - MAIL
使用mail命令收信时,系统会读取的邮箱文件 - LANG
语系数据,启动某些perl的程序语言文件时,会主动的分析语系数据文件 RANDOM
随机数变量,一般Linux发行版都会有的随机数生成器,在/dev/random,在bash的环境下,RANDOM变量的内容介于0~32767# 直接输出结果会在0~32767之间 [root@cnsz02vl29079 ~]# echo ${RANDOM} 5472 # 输出0~10之前的随机数 declare -i 后续会[declare]部分会介绍 [root@cnsz02vl29079 ~]# declare -i number=$RANDOM*10/32767;echo $number 4
查看所有变量
set(含环境变量和自定义变量)
提示符设置(PS1)
当我们每次按[Enter]按键去执行某个命令后,最后再次出现提示字符时,就会主动去读取这个变量值。
[root@cnsz02vl29079 ~]# echo $PS1
[\u@\h \W]\$
# 可以看到不同的用户提示符也不同(root用户为#,其他用户为$)
[root@cnsz02vl29079 ~]# exit
exit
[shigl@cnsz02vl29079 app]$
特殊含义的变量
$:(本shell的PID)
$代表当前sehll的进程号[root@cnsz02vl29079 app]# echo $$ 13809
?:(上一个执行命命令的返回值)
?代表上一个(仅代表$?的上一个)命令的返回值,一般来说如果成功执行该命令,返回值为0,非0表示执行异常[root@cnsz02vl29079 app]# $? bash: 0: command not found [root@cnsz02vl29079 app]# echo $? 127 # 返回值非0,表示上一个命令执行异常 [root@cnsz02vl29079 app]# echo $? 0 # 返回0,上一个命令为echo $?
export:自定义变量转成环境变量
通过前面的学习,我们知道环境变量和自定义的本质区别在于【该变量能否被子进程继续引用】
父进程:当登录linux并取得一个bash,这个bash就是一个独立的进程(可用PID识别)
子进程:输入bash进入的新bash环境就叫做子进程
[root@cnsz02vl29079 app]# echo $$
13809
# 进入新的bash,pid是不同的
[root@cnsz02vl29079 app]# bash
[root@cnsz02vl29079 app]# echo $$
17049
# 退回到首次进入的bash环境
[root@cnsz02vl29079 app]# exit
exit
[root@cnsz02vl29079 app]# echo $$
13809
进程与变量的关系?
子进程会继承父进程的环境变量,而不会继承父进程的自定义变量,因此在新的bash中自定义变量会失效,那么export命令正好就可以将自定义变量变成环境变量供子进程使用
# 用法
export 变量名称
[root@cnsz02vl29079 app]# name=SHIGL
[root@cnsz02vl29079 app]# export name
[root@cnsz02vl29079 app]# export
declare -x HISTSIZE="300"
...
declare -x XDG_SESSION_ID="77716"
declare -x name="SHIGL"
影响显示结果的语系变量(local)
语系问题常见的场景比如 man command的方式查询命令说明文件时,文件内容和语系不同而产生乱码。如何查看自己的linux支持哪些语系呢,就要用到locale命令
[root@cnsz02vl29079 app]# locale -a
# 以下是我们可以通过更改变量值自定义设置的语系变量
[root@cnsz02vl29079 app]# locale
LANG=en_US.UTF-8 # 主语言的环境
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL= # 整体语系的环境
说明:
通常情况其他语系变量都未设置,只要有设置LANG或LC_ALL时,则其他语系变量就会被这两个变量所替换,所以一般情况我们主要关注LANG/Lc_ALL变量
系统默认的语系定义在/etc/locale.conf文件中
[root@cnsz02vl29079 app]# cat /etc/locale.conf
LANG=en_US.UTF-8
变量的有效范围
变量的有效范围,根据export命令的功能,我们可以这么理解
变量读取、类型与声明
之前提到的变量都是直接设置的,那么有些需要用户自己输入的场景该怎么处理呢
read(读取来自键盘输入的变量)
read [-pt] variable
选项与参数-p:后面可以接提示符 -t:后面接等待时间[单位:秒]
基本使用
[root@cnsz02vl29079 app]# read test 123 [root@cnsz02vl29079 app]# echo $test 123 # 带提示符和等待时间 [root@cnsz02vl29079 app]# read -p "test command" -t 10 test # 10s未输入,则关闭等待用户输入 test command[root@cnsz02vl29079 app]#
declare(申明变量的类型)
declare后面没有接任何参数,那么bash就会主动的将所有的变量名称与内容统统显示出来,就如同set命令
declare [-aixr] variable
选项与参数-a:将变量variable定义成数组(array)类型 -i:将变量variable定义成数组(int)类型 -x:同export,将variable变成环境变量 -r:将变量设置为readonly类型,变量内容不能被更改,也不能unset
基本使用
```shell [root@cnsz02vl29079 ~]# declare -i num=12*12 [root@cnsz02vl29079 ~]# echo ${num} 144 [root@cnsz02vl29079 ~]# export | grep num [root@cnsz02vl29079 ~]# declare -x num [root@cnsz02vl29079 ~]# export | grep num declare -ix num=”144” [root@cnsz02vl29079 ~]# declare -r num [root@cnsz02vl29079 ~]# num=100 bash: num: readonly variable [root@cnsz02vl29079 ~]# export | grep num declare -irx num=”144”将-变成+可以取消环境变量为自定义变量
[root@cnsz02vl29079 ~]# declare +x num [root@cnsz02vl29079 ~]# export | grep num [root@cnsz02vl29079 ~]#
说明: 1.变量类型默认为字符串,不指定类型是无法起到运算的效果 2.bash环境中数值运算,默认最多仅能达到整数形态,1/3得到的结果为0
数组变量类型
```shell
[root@cnsz02vl29079 ~]# arr[1]="lili"
[root@cnsz02vl29079 ~]# arr[2]="xixi"
[root@cnsz02vl29079 ~]# echo "${arr[1]}, ${arr[2]}"
lili, xixi
与文件系统及程序的限制关系:ulimit
Linux主机是允许多人同时登录,意味着多人都在共用这主机的内存等配置,如果不限制用户对资源的操作限制,那么极容易使操作系统挂掉。bash是可以限制用户的某些系统资源的,包括可以开启的文件数据,可使用的cpu时间,内存总量等,都是通过ulimt来设置
ulimit [-Shacdfltu] [配额]
选项与参数
-H: hard limit 严格限制,不允许超过设置值
-S: soft limit 警告限制,可以超过,超过则有警告信息
-a:后面不接任何参数,列出所有限制配额参数
-c:程序发生错误,将内存中的信息写成文件,称为内核文件(core file)
-f:此shell可以建立的最大文件容量
-d:程序可使用的最大段内存(segment)容量
-l:可用于锁定(lock)的内存量
-t:可使用最大cpu时间
-u:单一使用者可使用的最大进程(process)数量
基本使用
[root@cnsz02vl29079 ~]# ulimit -a
core file size (blocks, -c) unlimited #
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 3886
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 655360
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) unlimited
cpu time (seconds, -t) unlimited
max user processes (-u) 655360
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
文件内容删除、取代与替换
命令别名与历史命令
命令别名设置:alias&unalias
使用场景:当命令比较长时,为了简化操作命令
列出命令别名
[root@cnsz02vl29079 app]# alias
alias cp='cp -i'
alias egrep='egrep --color=auto'
alias fgrep='fgrep --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 sd='sudo su'
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
设置命令别名
临时设置(alias 别名=’原命令’)
[root@cnsz02vl29079 ~]# alias ca='cd /app/' [root@cnsz02vl29079 ~]# pwd /root [root@cnsz02vl29079 ~]# ca [root@cnsz02vl29079 app]# pwd /app
永久设置
[root@cnsz02vl29079 app]# vim ~/.bashrc # 写入要增加的别名命令 [root@cnsz02vl29079 app]# source ~/.bashrc
取消命令别名
取消临时设置(unalias)
[root@cnsz02vl29079 app]# unalias ca [root@cnsz02vl29079 app]# cd /root/ [root@cnsz02vl29079 ~]# ca bash: ca: command not found
取消永久设置
从~/.bashrc文件中删除命令配置并source下就可以了历史命令history
选项与参数
n:数值,意思是列出最近的n条命令 -c:将目前shell中所有的history内容全部清除 -a:将新增的history命令新增到histfiles,不加默认写入~/.bash_history -r:将histfiles的内容读到目前shell的history记录中 -w:将history记录内容写入到histfiles中
基本使用
history 常用环境变量:
HISTSIZE: 执行history命令输出的行数,默认1000行
HISTFILESIZE: 保存history历史命令文件~/.bash_history最多保存的行数
HISTTIMEFORMAT: 指定执行history命令时显示的时间格式
HISTFILE: 指定存放历史命令的文件,默认是~/.bash_history[root@cnsz02vl29079 app]# env | grep HISTSIZE HISTSIZE=300 [root@cnsz02vl29079 app]# history 38 version 39 name ... 336 env | grep HISTSIZE 337 history
history命令返回结果操作
!number:执行第几条命令
- !command:由最近命令向前查找command命令
!!:上一条命令,相当于【上键 + 回车】
[root@cnsz02vl29079 app]# history 10 332 env | grep HISTSIZE 333 history 334 vim ~/.bashrc 335 vim ~/.bash_history 336 env | grep HISTSIZE 337 history 338 history 10 339 history 340 history 341 history 10 [root@cnsz02vl29079 app]# !332 env | grep HISTSIZE HISTSIZE=300 [root@cnsz02vl29079 app]# !! env | grep HISTSIZE HISTSIZE=300 [root@cnsz02vl29079 app]# !env env | grep HISTSIZE HISTSIZE=300
history命令常见问题
同账号同时多次登录history写入问题
linux开好几个bash界面时,bash身份都是root,因此所有的bash都会有自己的1000条记录在内存中,到注销时才会更新记录文件,因此最后注销的那个bash才会最后写入,其他bash的命令操作其实有记录,只是会被最后一个bash覆盖。
怎样保证history命令记录不被覆盖?
使用单一bash登录,再用任务管理来切换不同任务,保证所有曾今执行过的命令都记录,方便未来进行命令debug- 无法记录时间
history命令或命令文件中都没有记录使用命令的时间,考虑方向可以通过~/.bash_logout来进行history的记录,并增加date来增加时间参数