Shell基础
history - 历史命令
history命令可以展示我们执行过的命令。执行过的命令,在正常退出终端的时候,都会报错在/root/.bash_history文件中。在退出终端前,执行过的命令在内存里面。
环境变量$HISTSIZE定义了.bash_history可以保存多少条历史命令。在/etc/profile文件里可以修改$HISTSIZE的值:
$ vim /etc/profile
HISTSIZE=1000 # 1000是默认值,修改数字即可
HISTTIMEFORMAT="%Y/%m/%d %H:%M:%S " # 添加此条可显示命令执行时间
$ source /etc/profile # 执行生效
$ chattr +a ~/.bash_history # 防止被意外删除
终端操作:
history -c:清除当前历史命令!!:执行上一条命令!n:执行.bash_history文件中的第n条命令!word:执行.bash_history文件中最近一次以word开头的命令
命令补全及别名
命令补全:终端输入命令或文件时,按tab键一下可以补全。按tab键两下可以列出当前关键字开头的所有命令或文件。
Centos7支持命令参数自动补全。需要安装bash-completion包:
$ yum install -y bash-completion
别名:
# 查看当前所有的别名
$ alias
# 新建别名。别名保存在/etc/profile.d/目录下和当前用户的.bashrc文件中。用户自定义的别名都保存在.bashrc文件中
$ alias lf='ls -a' # 临时别名
$ vim ~/.bashrc # 永久别名
alias lf='ls -a'
# 取消自定义别名
$ unalias lf
通配符
*:匹配任意字符?:匹配任意单个字符[]:匹配任意个括号中的字符或数字,可以在中括号加入0-9a-zA-Z,例如:ls [ao]*表示以a和o开头的所有文件;ls [1-3].txt表示1.txt 2.txt 3.txt。[!]:不匹配中括号中的字符或数字,例如:ls [!0-9]*表示不以数字开头的文件{}:匹配花括号中的任意个或范围的字符或数字,例如:
$ ls {a,b,c}.txt # 匹配a.txt b.txt c.txt $ ls {lwz,zc}.txt # 匹配lwz.txt zc.txt $ touch {a..f}.txt # 创建a到f这个范围的.txt文件
特殊符号
#:在终端输入命令时,命令行提示符#后面的符号是当前用户是root用户,例如:# ls;在文件中,#代表注释。\:转义符;将特殊符号,转义成普通符号。$:在终端输入命令时,命令行提示符$后面的符号就是当前用户,例如:$ ls;也是shell中变量的前缀。!$:在正则表达式中表示行尾;在终端输入命令时,表示把上一条命令的参数作为标准输出,例如:$ ls aa.txt aa.txt $ !ls ls aa.txt aa.txt 或者 $ ls !$ ls aa.txt aa.txt
~:在终端时表示用户的家目录;在正则表达式中表示匹配符。
输入、输出、重定向
0:标准输入,从键盘输入1:标准输出,输出到终端2:标准错误,输出到终端
>:输出重定向,将命令执行的结果输出到文件中,如果文件存在,则覆盖,如果不存在,则创建。>>:输出重定向,将命令执行的结果追加到文件中,如果文件存在,则追加,如果不存在,则创建。2>:标准错误,将命令执行过程中的错误信息覆盖到文件中,正确结果不输入。2>>:标准错误,将命令执行过程中的错误信息追加到文件中,正确结果不输入。&>:标准输出和标准错误都覆盖到文件中。&>>:标准输出和标准错误都追加到文件中。<:输入重定向,将文件中的内容作为命令的输入。
管道和任务控制
示例:cat 11.txt | wc -l;cat 11.txt|grep 'pass'
wc -l:统计文件内容的行数或者文件的数量。
管道:
|:管道,将一个命令的输出作为另一个命令的输入。;:任务分隔,多个任务依次执行。&&:逻辑与,当第一个命令执行成功后,才执行第二个命令。||:逻辑或,当第一个命令执行失败后,才执行第二个命令。
任务控制:
&:后台运行,将任务放到后台执行。在命令结尾加上&符号,如:ping 127.0.0.1 &,后台运行ping命令。ctrl + z:将任务放到后台,并暂停执行。(后台可以暂停多个任务)jobs:查看当前后台任务。fg [id]:将任务放到前台,并继续执行。bg [id]:将暂停的任务放到后台,并继续执行。sleep:延迟执行任务。默认以秒(s)为单位示例:
sleep 10s;ls# 10秒后执行ls命令s为秒,m为分,h为小时,d为天
kill [id]:结束任务,id为任务编号。
Shell变量
变量类型
局部变量:局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
环境变量:所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
shell变量:shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行。
环境变量配置文件
/etc/profile:系统级,定义系统全局环境变量,该文件中定义的环境变量会在用户登陆时被加载。/etc/bashrc:系统级,但它仅对bash shell生效。当用户启动一个新交互式bash shell时,该文件会被读取,并设置bash shell特定的环境变量和执行一些系统级的初始化操作。~/.bashrc:用户级,用户的个人配置文件,该文件中的环境变量会在用户启动新的终端窗口时被加载。~/.bash_profile:用户级,该文件是每个用户的个人配置文件,该文件中定义的环境变量仅在该用户登陆时有效。
定义变量
name="lwz" # 定义一个变量,变量名为name,变量值为lwz
注:变量名和等号之间不能有空格。同时,变量名的命名须遵循如下规则:
命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
中间不能有空格,可以使用下划线
_。不能使用标点符号。
不能使用bash里的关键字(可用
help命令查看保留关键字)。
用语句给变量赋值:
# 将/etc目录下的文件
for file in `ls /etc`
或者
for file in $(ls /etc)
单引号
$ myname='my name is lwz'
单引号字符串的限制:
单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的。
单引号字符串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可以成对出现,作为字符串拼接使用。
双引号
$ name="lwz"
$ myname="my name is \"$name\" !" # \为转义符,双引号中的双引号作用为拼接,转义后为普通双引号输出。
$ echo $myname
my name is "lwz" !
双引号里可以用变量。
双引号里可以出现转义字符。
反引号
反引号``,反引号中的内容会当做一个命令来执行,并且将执行结果作为变量的值。
# 将当前时间作为变量的值赋值给a。
$ a=`date`
$ echo $a
Wed Feb 26 16:37:15 CST 2019
拼接字符串
$ name="lwz"
# 双引号拼接
$ myname="myname: my name is "$name" !"
$ myname1="myname1: my name is ${name} !"
$ echo $myname $myname1
myname: my name is lwz ! myname1: my name is lwz !
# 单引号拼接
$ myname2='myname2: my name is '$name' !'
$ myname3='myname3: my name is ${name} !'
$ echo $myname2 $myname3
myname2: my name is lwz ! myname3: my name is ${name} !
使用变量
变量名前面加上美元符号$即可。
$ echo $name
或者
$ echo ${name}
变量名外的花括号
{}是可选的,建议加上,避免歧义,类似如下情况。$ echo ${name}123 lwz123
只读变量
使用readonly命令将变量定义为只读变量,只读变量的值不能被更改。
# 将变量"name"设置成只读变量
$ readonly name
$ name="qqq"
-bash: name: readonly variable
注意:设置为只读变量,有可能无法删除。
删除变量
# 使用unset命令删除变量
$ unset name
cut - 按列分割文本
cut命令将文本内容根据指定的规则分割成多列输出,默认分隔符为“TAB”。
语法
cut [选项参数] 文件
选项与参数:
-b:以字节为单位进行分割。-n:与-b一起使用,表示禁止将字节分隔开来操作。-c:以字符为单位进行分割。-d:自定义分割字符。如果不加-d会默认字段分隔符为“TAB”;因此只能与-f一起使用。-f:以字段为单位进行分割;根据-d的分隔字符将一段信息分割成为数段,再用-f取出第几段的意思(列号,提取第几列)。-s:避免打印不包含分隔符的行。--complement:补足被选择的字节、字符或字段(反向选择的意思,或者说是补集)。--output-delimiter:更改输出分隔符;默认为输入分隔符。
示例
创建一个文本用于测试
[root@lwz ~]# cat rr.sh
NO Name SubjectID Mark 备注
1 longshuai 001 56 不及格
2 gaoxiaofang 001 60 及格
3 zhangsan 001 50 不及格
4 lisi 001 80 及格
5 wangwu 001 90 及格
djakldj;lajd;sla
按字段进行分隔
rr.sh文本一共有5列,筛选出第二列和第四列,使用空格作为分隔符。
[root@lwz ~]# cut -d" " -f2,4 rr.sh
Name Mark
001
001
djakldj;lajd;sla
因为文本内容不规则,所以输出的内容是非预期的。
使用tr工具将重复的多个空格视为单个:
[root@lwz ~]# cat rr.sh | tr -s " " | cut -d" " -f2,4
Name Mark
longshuai 56
gaoxiaofang 60
zhangsan 50
lisi 80
wangwu 90
djakldj;lajd;sla
最后一行完全没有分隔符的行也输出了,
使用-s来过滤这样的输出:
[root@lwz ~]# cat rr.sh | tr -s " " | cut -d" " -f2,4 -s
Name Mark
longshuai 56
gaoxiaofang 60
zhangsan 50
lisi 80
wangwu 90
使用--complement输出除了第二列和第四列以外的所有列:
[root@lwz ~]# cat rr.sh | tr -s " " | cut -d" " -f2,4 -s --complement
NO SubjectID 备注
1 001 不及格
2 001 及格
3 001 不及格
4 001 及格
5 001 及格
按字节或字符分隔
英文和阿拉伯数字是单字节字符,中文是双字节或者是三字节。
使用-b来筛选字节,-c筛选字符。
注意:按字节或字符分隔时不能指定
-d,因为-d是分隔字段的。
筛选第1个到第3个字节的内容:
[root@lwz ~]# cut -b1-3 rr.sh
NO
1 l
2 g
3 z
4 l
5 w
dja
筛选第3个到第6个字符的内容:
[root@lwz ~]# echo "今晚去哪喝!"|cut -c3-6
去哪喝!
--output-delimiter自定义分隔符
可以在多段分隔需要拼接时使用--output-delimiter来指定分隔符:
[root@lwz ~]# cut -b3-5,6-8 rr.sh # 没有指定分隔符的拼接
Name
longsh
gaoxia
zhangs
lisi
wangwu
akldj;
[root@lwz ~]# cut -b3-5,6-8 rr.sh --output-delimiter ":" # 指定":"分隔拼接内容
Na:me
lon:gsh
gao:xia
zha:ngs
lis:i
wan:gwu
akl:dj;
cut中的范围筛选
输出第3个字段往后的所有内容:
[root@lwz ~]# cut -d" " -f3- rr.sh -s
SubjectID Mark 备注
001 56 不及格
001 60 及格
001 50 不及格
001 80 及格
001 90 及格
如果筛选的多段内容中有重复的,不会重复输出:
[root@lwz ~]# cut -d" " -f3-5,4-6 rr.sh -s # 同`-f3-6`输出的内容一样。
SubjectID Mark 备注
001 56 不及格
001 60 及格
001 50 不及格
001
001
在终端输入的分段顺序不会影响输出的顺序,cut只会按照文本内容的顺序输出:
[root@lwz ~]# cut -d" " -f4-6,2 rr.sh -s # 两条命令输入的字段顺序不一样,但是输出内容还是一样的。
Name Mark 备注
longshuai 56 不及格
gaoxiaofang 001 60 及格
zhangsan 50 不及格
lisi 001
wangwu 001
[root@lwz ~]# cut -d" " -f2,4-6 rr.sh -s
Name Mark 备注
longshuai 56 不及格
gaoxiaofang 001 60 及格
zhangsan 50 不及格
lisi 001
wangwu 001
sort - 内容排序
sort命令将文件内容的每一行作为比较对象,通过将不同行进行互相比较,按照指定的排序规则进行排序输出,默认正序输出。
语法
sort [选项] 文件
常用选项:
-u:不输出重复行。-r:倒叙输出。-n:按照数字排序。(特殊符号为0,所以输出在数字前面)-o:将排序后的结果输出到指定文件。-t:指定分隔符。(通常和-k一起使用)-k:指定排序的列。(通常和-t一起使用)
其他选项:
-f:将小写字母转换为大写字母进行比较,即忽略大小写。-b:忽略每行前面开始处的空格字符。-m:合并已排序文件,而不是输出。-M:将每一行的月份用月份全称代替,例如JAN小于FEB,等等。-c:检查文件是否已经按照顺序排序,如果乱序,则输出第一个乱序的行的相关信息,最后返回1。-C:与-c选项类似,如果乱序,不输出内容,仅返回1。
示例
# 不加参数输出正序
[root@lwz ~]# sort seq.txt
!!@#
../
123
apple
apple:10:2.5
banana
banana:30:5.5
orange
orange:20:3.4
pear
pear
pear:90:2.3
# -r倒叙
[root@lwz ~]# sort -r seq.txt
pear:90:2.3
pear
pear
orange:20:3.4
orange
banana:30:5.5
banana
apple:10:2.5
apple
123
../
!!@#
# -u去重,-r倒叙,并将输出-o写入文件
[root@lwz ~]# sort -ur seq.txt -o seq2.txt
[root@lwz ~]# cat seq2.txt
pear:90:2.3
pear
orange:20:3.4
orange
banana:30:5.5
banana
apple:10:2.5
apple
123
../
!!@#
# -t指定分隔符为 : ,-k指定以:分隔后的第2列排序
[root@lwz ~]# sort -t: -k2 seq.txt
!!@#
../
123
apple
banana
orange
pear
pear
apple:10:2.5
orange:20:3.4
banana:30:5.5
pear:90:2.3
wc - 内容统计
wc命令用于统计指定文件中的字节数、字数、行数,并将统计结果显示输出。
语法
wc [选项] [文件]
如果不加选项,默认统计文件中的总行数、总字数、总字节数,如果命令行中有文件名,则后面加上文件名依次输出,没有文件名则不输出文件名。
如果不加文件名,会从标准输入读取内容(一般是结合其他命令一起使用)。
选项:
-c:统计字节数。-l:统计行数。-m:统计字符数。-w:统计字数。
示例
# 统计文件中的行数、字数、字节数,依次输出。
[root@lwz ~]# wc seq.txt
12 12 98 seq.txt
# 统计多个文件
[root@lwz ~]# wc -clw seq.txt seq2.txt # 输出结果与不加选项相同
12 12 98 seq.txt
12 12 98 seq2.txt
24 24 196 总用量
# -l统计行数(比较常用)
[root@lwz ~]# wc -l seq.txt
12 seq.txt
# 结合其他命令使用,统计ps -ef中的进程数
[root@lwz ~]# ps -ef | wc -l
92
uniq - 内容去重
uniq命令用于输出或忽略文件中重复的行,一般与sort命令结合使用。
语法
uniq [选项] [文件]
选项:
-c:在每列旁边显示该行重复出现的次数。-d:仅显示重复出现的行列,只显示一行。-D:显示所有重复的行,重复多少行就显示多少行。-u:仅显示出一次的行,不显示重复的行。-i:忽略大小写字符的不同。-f [N]:忽略第N列。-s [N]:忽略前面N个字符。-w [N]:忽略第N个字符后的内容。
示例
# 示例文本uniq.txt
[root@lwz ~]# cat uniq.txt
My name is Delav
My name is Delav
MY name is Delav
I'm learning Java
I'm learning Java
I'm learning Java
who am i
WHO am i
Python is so simple
My name is Delav
That's good
That's good
And studying Golang
# 不加选项默认输出去重后的内容
[root@lwz ~]# uniq uniq.txt
My name is Delav
MY name is Delav
I'm learning Java
who am i
WHO am i
Python is so simple
My name is Delav
That's good
And studying Golang
# 只显示重复的行-d,并显示重复次数-c
[root@lwz ~]# uniq -dc uniq.txt
2 My name is Delav
3 I'm learning Java
2 That's good
# 忽略第2列(注意My和MY,忽略后就算重复的)
[root@lwz ~]# uniq -f2 uniq.txt
My name is Delav
I'm learning Java
who am i
Python is so simple
My name is Delav
That's good
And studying Golang
# 忽略前面3个字符(注意who和WHO会算作重复的,从第4个字符后就不算重复了)
[root@lwz ~]# uniq -s3 uniq.txt
My name is Delav
I'm learning Java
who am i
Python is so simple
My name is Delav
That's good
And studying Golang
# 结合sort命令一起使用(uniq只会把相邻的行算作重复,使用sort就能把不相邻的重复行排序到一起了)
[root@lwz ~]# sort uniq.txt | uniq -c
1 And studying Golang
3 I'm learning Java
3 My name is Delav
1 MY name is Delav
1 Python is so simple
2 That's good
1 who am i
1 WHO am i
tee - 重定向到标准输出和文件
tee命令将一份标准输入分别重定向到标准输出/dev/stdout和指定的文件中,文件可以有多个。tee命令与>、>>的区别是,tee命令会将输出重定向到文件的同时也输出在终端上。标准错误不会被
tee读取到。
例:
[root@centos03 ~]# who | tee -a who.out
root pts/3 2023-10-26 10:20 (192.168.1.110)
[root@centos03 ~]# cat who.out
root pts/3 2023-10-26 10:20 (192.168.1.110)
tee tee.txt:覆盖到文件,与>同理。tee -a tee.txt:追加到文件,与>>同理。
tr - 替换或删除字符
语法:tr [参数] "被替换" "替换后"
参数:
-d:删除字符,即删除字符串中指定的字符。-s:把连续重复的字符以单独一个字符表示。-c:反选,即除了被指定的字符外其它的字符都处理。-t:删除所有属于第一字符集且不属于第二字符集的字符。
示例:
# 将结果小写转换为大写
[root@lwz ~]# who | tr "a-z" "A-Z" | tee test.txt
ROOT TTY1 2023-10-24 16:02
ROOT PTS/0 2023-10-24 16:07 (192.168.1.110)
# 将结果中的数字依次替换成字母,0=a、1=b、2=c,以此类推
[root@lwz ~]# echo '01222345' | tr '0-9' 'abcdefghijk' | tee test.txt
abcccdef
# 删除结果中数字以外的内容
[root@lwz ~]# echo '542asd99@#$' | tr -dc '0-9' | tee test.txt
54299
# 删除echo结果中数字以外的内容,并且去除结果中的重复数字
[root@lwz ~]# echo '542asd999999@#$' | tr -dc '0-9' | tr -s '0-9' | tee test.txt
5429
替换的范围表示:
[:digit:] # 所有数字
[:lower:] # 所有小写字符
[:upper:] # 所有大写字符
[:graph:] # 所有可打印字符,不包括空格
[:print:] # 所有可打印字符,包括空格
[:punct:] # 所有的标点字符
[:space:] # 所有横向或者纵向的空白
[:xdigit:] # 所有十六进制数字,包括0-9,a-f,A-F
split - 分割字符串
split命令用于将一个文件分隔成数个。
该指定将大文件分割成较小的文件,在默认情况下将按照每1000行分割为一个小文件。
语法
split [选项] [被切割文件] [切割后的文件名]
选项:
-<行数>:指定按多少行切割。-b<字节>:指定按多少字节切割。-l<行数>:也是指定按多少行切割。-C<>:与-b相似,但切割时会维持每行的完整性。--version:显示版本信息。--help:在线帮助。
示例:
按每50行分割为一个文件:
[root@lwz 111]# split -50 11.txt # 当不指定分割后的文件名时,文件名默认以 xa 开头
[root@lwz 111]# ls
11.txt xaa xab xac xad xae xaf xag xah xai
按每1kb大小分割为一个文件,并且分割后的文件名以a1开头:
[root@lwz 111]# split -b1k 11.txt a1
[root@lwz 111]# ls
11.txt a1aa a1ab a1ac a1ad a1ae a1af a1ag a1ah a1ai a1aj a1ak a1al a1am
将上一条命令执行的结果,按每5行存储到文件:
[root@lwz 111]# ps -ef | split -5
[root@lwz 111]# ls
11.txt xab xad xaf xah xaj xal xan xap xar
xaa xac xae xag xai xak xam xao xaq xas