再学习Linux记录

参考鸟哥Linux私房菜
学习的时候,要记得翻译:繁体->简体

命令未找到

Linux 命令概述:命令未找到

exit:退出

1
2
3
4
5
6
7
8
9
10
1、命令行执行:退出 bash

2、shell script:也是退出 bash。因为 shell script 是让当前 bash 睡眠,在子 bash 运行的。

3、在 shell script 中,可使用“exit n”,其中 n 取[0,255]。
调用脚本之后,echo $? 返回的就是 exit n 中的那个 n,shell script 尾部隐藏一个“exit 0”。
命令执行之后,$? 0 表示正确,其他均为错误。
通常 shell script 也遵循这个原则,而进行不同的返回。

4、“exit 0”可以简写为“exit

seq:数字序列

2020-11-27 16:57:30
打印一个数字序列(sequence)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
用法:seq  尾数
 或:seq 首数 尾数
 或:seq 首数 增量 尾数

首数:默认 1
增量:默认 1

------------seq 3
1
2
3
------------seq 4 6
4
5
6
------------seq 0 2 6
0
2
4
6

locale:本地语言

2020-11-03 11:41:33

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@localhost ~]# locale
LANG=zh_CN.UTF-8
LC_CTYPE="zh_CN.UTF-8"
LC_NUMERIC="zh_CN.UTF-8"
LC_TIME="zh_CN.UTF-8"
LC_COLLATE="zh_CN.UTF-8"
LC_MONETARY="zh_CN.UTF-8"
LC_MESSAGES="zh_CN.UTF-8"
LC_PAPER="zh_CN.UTF-8"
LC_NAME="zh_CN.UTF-8"
LC_ADDRESS="zh_CN.UTF-8"
LC_TELEPHONE="zh_CN.UTF-8"
LC_MEASUREMENT="zh_CN.UTF-8"
LC_IDENTIFICATION="zh_CN.UTF-8"
LC_ALL=

永久生效(如果想设置英文:LANG=”en_US.UTF-8”):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
1. 改文件
[root@localhost ~]# echo 'LANG="zh_CN.UTF-8"' >> /etc/locale.conf

2. 配置生效
[root@localhost ~]# source /etc/profile.d/lang.sh

3. 退出
[root@localhost ~]# exit

4. 重新连接(日期显示中文)
[root@localhost ~]# date
2020年 11月 02日 星期一 19:41:49 PST

5. 命令帮助文档也显示中文
[root@localhost ~]# date --help | head -n10
用法:date [选项]... [+格式]
 或:date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]
Display the current time in the given FORMAT, or set the system date.

Mandatory arguments to long options are mandatory for short options too.
-d, --date=STRING display time described by STRING, not 'now'
-f, --file=DATEFILE like --date once for each line of DATEFILE
-I[TIMESPEC], --iso-8601[=TIMESPEC] output date/time in ISO 8601 format.
TIMESPEC='date' for date only (the default),
'hours', 'minutes', 'seconds', or 'ns' for date
[root@localhost ~]# date --help | head -n20
用法:date [选项]... [+格式]
 或:date [-u|--utc|--universal] [MMDDhhmm[[CC]YY][.ss]]
Display the current time in the given FORMAT, or set the system date.

Mandatory arguments to long options are mandatory for short options too.
-d, --date=STRING display time described by STRING, not 'now'
-f, --file=DATEFILE like --date once for each line of DATEFILE
-I[TIMESPEC], --iso-8601[=TIMESPEC] output date/time in ISO 8601 format.
TIMESPEC='date' for date only (the default),
'hours', 'minutes', 'seconds', or 'ns' for date
and time to the indicated precision.
-r, --reference=文件 显示文件指定文件的最后修改时间
-R, --rfc-2822 以RFC 2822格式输出日期和时间
例如:2006年8月7日,星期一 12:34:56 -0600
--rfc-3339=TIMESPEC output date and time in RFC 3339 format.
TIMESPEC='date', 'seconds', or 'ns' for
date and time to the indicated precision.
Date and time components are separated by
a single space: 2006-08-07 12:34:56-06:00
-s, --set=STRING set time described by STRING

date:日期时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-----------------------date
2020年 11月 26日 星期四 10:50:25 CST #===> 当前时间

-----------------------date "+%Y-%M-%d %H:%m:%S"
2020-04-26 17:11:22 #===> 日期格式化显示

-----------------------date +%s
1606381478 #===> 时间戳(秒)

-----------------------date --date="2020-11-27" +%d
27 #===> 指定一个日期,获取天数

-----------------------date --date="2020-11-27" +%s
1606406400 #===> 指定一个日期,获取时间戳(秒)

cal:日历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1. 查看当前
[root@localhost test]# cal
十一月 2020
日 一 二 三 四 五 六
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30

2. 查看 2010-05
[root@localhost test]# cal 5 2010
五月 2010
日 一 二 三 四 五 六
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31

tab:补全

单击:自动补全
双击:列出候选补全

man:命令手册

manual

命令数字

1
2
3
4
5
6
7
8
9
10
11
代号  代表内容

1 使用者在shell环境中可以操作的指令或可执行档
2 系统核心可呼叫的函数与工具等
3 一些常用的函数(function)与函式库(library),大部分为C的函式库(libc)
4 装置档案的说明,通常在/dev下的档案
5 设定档或者是某些档案的格式
6 游戏(games)
7 惯例与协定等,例如Linux档案系统、网路协定、ASCII code等等的说明
8 系统管理员可用的管理指令
9 跟kernel有关的文件

man 页面操作

1
2
3
4
5
6
7
8
9
10
11
12
13
按键        进行工作

空白键 向下翻一页
[PageDown] 向下翻一页
[PageUp] 向上翻一页
[Home] 去到第一页
[End] 去到最后一页
/string 向『下』搜寻string 这个字串,如果要搜寻vbird 的话,就输入/vbird
?string 向『上』搜寻string 这个字串
n, N 利用/ 或? 来搜寻字串时,可以用n 来继续下一个搜寻(不论是/ 或?) ,可以利用N 来进行『反向』搜寻。
举例来说,我以/vbird 搜寻vbird 字串, 那么可以n 继续往下查询,用N 往上查询。
若以?vbird 向上查询vbird 字串, 那我可以用n 继续『向上』查询,用N 反向查询。
q 结束这次的man page

man:中文手册

》》》记得要先设置上面的locale:本地语言

1
yum -y install man-pages-zh-CN

who:谁在线上

1
2
3
4
[root@localhost test]# who
root tty1 2020-11-03 18:58
root pts/0 2020-11-03 19:41 (192.168.1.8)
root pts/1 2020-11-03 20:01 (192.168.1.8)

showdown:关机

reboot:重启

reboot -f
强制重启

chgrp:改变所属群组

1
2
3
4
5
6
7
8
9
10
11
12
[root@study ~]# chgrp [-R] dirname/filename ...
选项与参数:
-R : 进行递回(recursive)的持续变更,亦即连同次目录下的所有档案、目录
都更新成为这个群组之意。常常用在变更某一目录内所有的档案之情况。
范例:
[root@study ~]# chgrp users initial-setup-ks.cfg
[root@study ~]# ls -l
-rw-r--r--. 1 root users 1864 May 4 18:01 initial-setup -ks.cfg
[root@study ~]# chgrp testing initial-setup-ks.cfg
chgrp: invalid group: `testing' <==发生错误讯息啰~找不到这个群组名~

[root@study ~]# chgrp myquotagrp /home/myquota #===> 把 /home/myquota 目录的群组改为 myquotagrp

chown:改变档案拥有者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

[root@study ~]# chown [-R]帐号名称 档案或目录
[root@study ~]# chown [-R]帐号名称:群组名称 档案或目录
选项与参数:
-R : 进行递回(recursive)的持续变更,亦即连同次目录下的所有档案都变更

范例:将initial-setup-ks.cfg的拥有者改为bin这个帐号:
[root@study ~]# chown bin initial-setup-ks.cfg
[root@study ~]# ls -l
-rw-r- -r--. 1 bin users 1864 May 4 18:01 initial-setup-ks.cfg

范例:将initial-setup-ks.cfg的拥有者与群组改回为root:
[root@study ~]# chown root:root initial-setup-ks.cfg
[root@study ~]# ls -l
- rw-r--r--. 1 root root 1864 May 4 18:01 initial-setup-ks.cfg

chmod:改变权限

》》》数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
r:4 w:2 x:1

每种身份(owner/group/others)各自的三个权限(r/w/x)分数是需要累加的,
例如当权限为: [-rwxrwx---] 分数则是:
owner = rwx = 4+2+1 = 7
group = rwx = 4+2+1 = 7
others= --- = 0+0+0 = 0


[root@study ~]# chmod [-R] xyz档案或目录
选项与参数:
xyz : 就是刚刚提到的数字类型的权限属性,为rwx 属性数值的相加。
-R : 进行递回(recursive)的持续变更,亦即连同次目录下的所有档案都会变更

[root@study ~]# ls -al .bashrc
-rw-r--r-- . 1 root root 176 Dec 29 2013 .bashrc
[root@study ~]# chmod 777 .bashrc
[root@study ~]# ls -al .bashrc
-rwxrwxrwx . 1 root root 176 Dec 29 2013 .bashrc

》》》符号

1
2
3
4
5
user:u group:g others:o
a则代表all亦即全部的身份
那么读写的权限就可以写成r, w, x

[root@study ~]# chmod a+w .bashrc # 给所有人加 w 写权限
1
2
3
4
5
6
7
8
9
例题:
我想在/tmp 底下建立一个目录,这个目录名称为chapter6_1 ,并且这个目录拥有者为dmtsai, 群组为dmtsai,
此外,任何人都可以进入该目录浏览档案,不过除了dmtsai 之外,其他人都不能修改该目录下的档案。

答:
因为除了dmtsai 之外,其他人不能修改该目录下的档案,所以整个目录的权限应该是drwxr-xr-x 才对!
建立目录: mkdir /tmp/chapter6_1
修改属性: chown -R dmtsai:dmtsai /tmp/chapter6_1
修改权限: chmod -R 755 /tmp/chapter6_1

cd:改变目录

Change Directory

1
2
3
4
5
6
7
.   代表此层目录
.. 代表上一层目录
- 代表前一个工作目录(减号)
~ 代表『目前使用者身份』所在的家目录

家目录:root:/root
其他:/home/用户名

pwd:显示目前所在的目录

Print Working Directory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@study ~]# pwd [-P]
选项与参数:
-P :显示出确实的路径,而非使用连结(link) 路径。



[root@localhost /]# ll
总用量 32
...
lrwxrwxrwx. 1 root root 7 11月 3 18:51 lib -> usr/lib
...
[root@localhost /]# cd lib
[root@localhost lib]# pwd
/lib
[root@localhost lib]# pwd -P
/usr/lib

mkdir:建立新目录

make directory

1
2
3
4
5
6
7
8
9
10
[root@study ~]# mkdir [-mp] 目录名称
选项与参数:
-m :设定档案的权限喔!直接设定,不需要看预设权限(umask) 的脸色~
-p :帮助你直接将所需要的目录(包含上层目录)递回建立起来!

[root@study tmp]# mkdir -p test1/test2/test3/test4
#原来是要建test4上层没先建test3之故!加了这个-p的选项,可以自行帮你建立多层目录!

范例:建立权限为rwx--x--x的目录
[root@study tmp]# mkdir -m 711 test2

rmdir:删除『空』的目录

1
2
3
[root@study ~]# rmdir [-p] 目录名称
选项与参数:
-p :连同『上层』『空的』目录也一起删除

$PATH:路径变量

1
2
3
4
5
6
7
8
9
10
11
12
13
# 追加变量
PATH=${PATH}:/root/diy-script/

# 绝对/相对路径 指定名称执行
[root@study ~]# /root/ls <==直接用绝对路径指定该档名
[root@study ~]# ./ls <==因为在/root目录下,就用./ls来指定

# 注意事项
+ 不同身份使用者预设的PATH不同,预设能够随意执行的指令也不同(如root与dmtsai);
+ PATH是可以修改的;
+ 使用绝对路径或相对路径直接指定某个指令的档名来执行,会比搜寻PATH来的正确;
+ 指令应该要放置到正确的目录下,执行才会比较方便;
+ 本目录(.)最好不要放到PATH当中。

ls:列出目录内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[root@study ~]# ls [-aAdfFhilnrRSt]档名或目录名称.. 
[root@study ~]# ls [--color={never,auto,always}]档名或目录名称..
[root@ study ~]# ls [--full-time]档名或目录名称..
选项与参数:
-a :全部的档案,连同隐藏档(开头为.的档案)一起列出来(常用)
-A :全部的档案,连同隐藏档,但不包括. 与.. 这两个目录
-d :仅列出目录本身,而不是列出目录内的档案资料(常用)
-f :直接列出结果,而不进行排序(ls 预设会以档名排序!)
-F :根据档案、目录等资讯,给予附加资料结构,例如:
*:代表可执行档; /:代表目录; =:代表socket 档案; |:代表FIFO 档案;
-h :将档案容量以人类较易读的方式(例如GB, KB 等等)列出来;
-i :列出inode 号码,inode 的意义下一章将会介绍;
-l :长资料串列出,包含档案的属性与权限等等资料;(常用)
-n :列出UID 与GID 而非使用者与群组的名称(UID与GID会在帐号管理提到!)
-r :将排序结果反向输出,例如:原本档名由小到大,反向则为由大到小;
-R :连同子目录内容一起列出来,等于该目录下的所有档案都会显示出来;
-S :以档案容量大小排序,而不是用档名排序;
-t :依时间排序,而不是用档名。
--color=never :不要依据档案特性给予颜色显示;
--color=always :显示颜色
--color=auto :让系统自行依据设定来判断是否给予颜色
--full-time :以完整时间模式(包含年、月、日、时、分) 输出
--time={atime,ctime} :输出access 时间或改变权限属性时间(ctime)
而非内容变更时间(modification time)


modification time (mtime):
(默认显示)当该档案的『内容资料』变更时,就会更新这个时间!内容资料指的是档案的内容,而不是档案的属性或权限喔!
status time (ctime):
当该档案的『状态(status)』改变时,就会更新这个时间,举例来说,像是权限与属性被更改了,都会更新这个时间啊。
access time (atime):
当『该档案的内容被取用』时,就会更新这个读取时间(access)。举例来说,我们使用cat去读取/etc/man_db.conf ,就会更新该档案的atime了。

[root@study ~]# date; ls -l /etc/man_db.conf ; ls -l --time=atime /etc/man_db.conf ; \
> ls -l --time=ctime /etc/man_db.conf #这两行其实是同一行喔!用分号隔开
Tue Jun 16 00:43:17 CST 2015 #目前的时间啊!
-rw-r--r--. 1 root root 5171 Jun 10 2014 /etc/man_db.conf #在2014/06/10建立的内容(mtime)
-rw-r--r--. 1 root root 5171 Jun 15 23:46 /etc/man_db.conf #在2015/06/15读取过内容(atime)
-rw-r--r--. 1 root root 5171 May 4 17:54 /etc/man_db.conf #在2015/05/04更新过状态(ctime)
#为了要让资料输出比较好看,所以鸟哥将三个指令同时依序执行,三个指令中间用分号(;)隔开即可。

ll:查看

无论如何, ls最常被使用到的功能还是那个-l的选项。
为此,很多distribution在预设的情况中,已经将ll (L的小写)设定成为ls -l的意思了!
其实,那个功能是Bash shell的 alias 功能呢~也就是说,我们直接输入ll就等于是输入ls -l是一样的

1
2
3
4
5
6
7
8
9
10
11
[root@localhost /]# 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 which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'

cp:拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

[root@study ~]# cp [-adfilprsu] 来源档(source) 目标档(destination)
[root@study ~]# cp [options] source1 source2 source3 .... directory
选项与参数:
-a :将档案的所有特性都一起复制过来(相当于-dr)(常用)
-d :src为软链接,dest则也是软链接。二者指向同一个文件;
-f :为强制(force)的意思,若目标档案已经存在且无法开启,则移除后再尝试一次;
-i :若目标档(destination)已经存在时,在覆盖时会先询问动作的进行(常用)
-l :建立硬链接dest。
-p :连同档案的属性(权限、用户、时间)一起复制过去,而非使用预设属性(备份常用);
-r :递回持续复制,用于目录的复制行为;(常用)
-s :建立软链接dest,指向src
-u :destination 比source 旧才更新destination,或destination 不存在的情况下才复制。
--preserve=all :除了-p 的权限相关参数外,还加入SELinux 的属性, links, xattr 等也复制了。
最后需要注意的,如果来源档有两个以上,则最后一个目的档一定要是『目录』才行!


# 原本dmtsai 身份无法进行的动作,即使加上-a 选项,也是无法达成完整复制权限的!
[dmtsai@study ~]$ cp -a /var/log/wtmp /tmp/dmtsai_wtmp
[dmtsai@study ~]$ ls -l /var/log/wtmp /tmp/dmtsai_wtmp
-rw-rw-r--. 1 dmtsai dmtsai 28416 6月11 18:56 /tmp/dmtsai_wtmp
-rw-rw-r--. 1 root utmp 28416 6月11 18:56 /var/log/wtmp

rm:移除档案或目录

remove

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@study ~]# rm [-fir]档案或目录
选项与参数:
-f :就是force 的意思,忽略不存在的档案,不会出现警告讯息;
-i :互动模式,在删除前会询问使用者是否动作
-r :递回删除啊!最常用在目录的删除了!这是非常危险的选项!!!


范例四:删除一个带有-开头的档案
[root@study tmp]# touch ./-aaa- <== touch这个指令可以建立空档案!
[root@study tmp]# ls -l
-rw-r--r--. 1 root root 0 Jun 11 19:22 -aaa- <==档案大小为0,所以是空档案
[root@study tmp] # rm -aaa-
rm: invalid option -- 'a' <==因为"-"是选项嘛!所以系统误判了!
Try 'rm ./-aaa-' to remove the file `-aaa-'. <==新的bash有给建议的
Try 'rm --help' for more information.
[root@study tmp]# rm ./-aaa-


[root@localhost test]# touch -- -aaa
[root@localhost test]# ll
-rw-r--r--. 1 root root 0 11月 3 23:47 -aaa

只能用避过首位字元是"-" 的方法啦!
1. 就是加上本目录『 ./ 』即可!
2. 如果man rm 的话,其实还有一种方法,那就是『 rm -- -aaa- 』也可以啊!

mv:移动档案与目录,或更名

move

1
2
3
4
5
6
7

[root@study ~]# mv [-fiu] source destination
[root@study ~]# mv [options] source1 source2 source3 .... directory
选项与参数:
-f :force 强制的意思,如果目标档案已经存在,不会询问而直接覆盖;
-i :若目标档案(destination) 已经存在时,就会询问是否覆盖!
-u :(update )若目标档案已经存在,且source 比较新,才会更新(update)

basename dirname:取得路径的档案名称与目录名称

那么你怎么知道那个是档名?那个是目录名?嘿嘿!就是利用斜线(/) 来分辨啊!
所以,这部分的指令可以用在 shell scripts 里头喔!

1
2
3
4
[root@localhost test]# basename /app/test/a
a
[root@localhost test]# dirname /app/test/a
/app/test

cat:显示

concatenate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

[root@study ~]# cat [-AbEnTv]
选项与参数:
-A :相当于-vET 的整合选项,可列出一些特殊字符而不是空白而已;
-b :列出行号,仅针对非空白行做行号显示,空白行不标行号!
-E :将结尾的断行字元$ 显示出来;
-n :列印出行号,连同空白行也会有行号,与-b 的选项不同;
-T :将[tab] 按键以^I 显示出来;
-v :列出一些看不出来的特殊字符


[root@localhost test]# cat -b a
1 a
2 b
3 c
4 d

5 e
[root@localhost test]# cat -n a
1 a
2 b
3 c
4 d
5
6 e

1. 写入内容
[root@localhost test]# cat > catfile << "eof"
> 123 ===> content
> 456 ===> content
> eof
[root@localhost test]#
[root@localhost test]#
[root@localhost test]# cat catfile
123
456
[root@localhost test]#

2. 写入空
[root@localhost test]# cat > catfile << "eof"
> eof
[root@localhost test]# cat catfile
[root@localhost test]#

tac:反向显示

(tac刚好是将cat反写过来,所以他的功能就跟cat相反啦)

1
2
3
4
5
6
7
8
[root@localhost test]# tac a
e

d
c
b
a
[root@localhost test]#

nl:添加行号列印

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
[root@study ~]# nl [-bnw]档案
选项与参数:
-b :指定行号指定的方式,主要有两种:
-ba :表示不论是否为空行,也同样列出行号(类似cat -n);
-bt :如果有空行,空的那一行不要列出行号(默认带有 -bt);
-n :列出行号表示的方法,主要有三种:
-n ln :行号在萤幕的最左方显示;
-n rn :行号在自己栏位的最右方显示,且不加0 ;
-n rz :行号在自己栏位的最右方显示,且加0 ;
-w :行号栏位的占用的字元数。


[root@localhost test]# nl -ba a
1 a
2 b
3 c
4 d
5
6 e

[root@localhost test]# nl a
1 a
2 b
3 c
4 d

5 e
[root@localhost test]# nl -n ln a
1 a
2 b
3 c
4 d

5 e
[root@localhost test]# nl -n rn a
1 a
2 b
3 c
4 d

5 e
[root@localhost test]# nl -n rz a
000001 a
000002 b
000003 c
000004 d

000005 e
[root@localhost test]# nl -w 1 a
1 a
2 b
3 c
4 d

5 e
[root@localhost test]# nl -w 2 a
1 a
2 b
3 c
4 d

5 e
[root@localhost test]# nl -w 10 a
1 a
2 b
3 c
4 d

5 e
[root@localhost test]#

more:只能往下翻

1
2
3
4
5
6
空白键(space): 代表向下翻一页;
Enter : 代表向下翻『一行』;
/字串 : 代表在这个显示的内容当中,向下搜寻『字串』这个关键字;
:f : 立刻显示出档名以及目前显示的行数;
q : 代表立刻离开more ,不再显示该档案内容。
b : 代表往回翻页,不过这动作只对档案有用,对管线无用。

less:上下翻动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
空白键 :向下翻动一页;
b :向上翻动一页;

[pagedown]:向下翻动一页;
[pageup] :向上翻动一页;

d(down):向下翻动半页;
u(up):向上翻动半页;

/字串 :向下搜寻『字串』的功能;
?字串 :向上搜寻『字串』的功能;

n :重复前一个搜寻(与/ 或? 有关!)
N :反向的重复前一个搜寻(与/ 或? 有关!)

g :前进到这个资料的第一行去;
G :前进到这个资料的最后一行去(注意大小写);

q :离开less 这个程式;

head:取出前面几行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

[root@study ~]# head [-n number] 档案
选项与参数:
-n :后面接数字,代表显示几行的意思
number:默认10,(默认正数)正数表示只显示前n行,负数表示最后n行不显示
head -n 3 a 等同于 head -n +3 a


1. 显示前 10 行
[root@localhost test]# head a
a
b
c
d
2. 显示前 3 行
[root@localhost test]# head -n 3 a
a
b
c
3. 最后 3 行不显示
[root@localhost test]# head -n -3 a
a
4. 显示命令执行结果的前三行
[root@localhost test]# ps aux | head -n 3
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.3 44500 7172 ? Ss 11月03 0:03 /usr/lib/systemd/systemd --switched-root --system --deserialize 21
root 2 0.0 0.0 0 0 ? S 11月03 0:00 [kthreadd]
[root@localhost test]#

tail:取出后面几行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46

[root@study ~]# tail [-n number] 档案
选项与参数:
-n :后面接数字,代表显示几行的意思
number:默认10,(默认负数)正数表示不显示前n-1行,负数表示显示最后n行
tail -n 3 a 等同于 tail -n -3 a


-f :表示持续侦测后面所接的档名,要等到按下 ctrl + c 才会结束tail的侦测

[root@localhost test]# tail a
a
b
c
d
[root@localhost test]# tail -n 2 a
c
d
[root@localhost test]# tail -n -2 a
c
d
[root@localhost test]# tail -n +2 a
b
c
d



问:怎么显示一个文件的 3~7 行数据?
[root@localhost test]# cat -n a
1 11
2 22
3 33
4 44
5 55
6 66
7 77
8 88
9 99
[root@localhost test]# cat -n a | head -n 7 | tail -n 5
3 33
4 44
5 55
6 66
7 77
[root@localhost test]#

touch:修改档案时间或建置新档

1
2
3
4
5
6
7
8
9
10
[root@study ~]# touch [-acdmt]档案
选项与参数:
-a :仅修订access time;
-c :仅修改档案的时间,若该档案不存在则不建立新档案;
-d :后面可以接欲修订的日期而不用目前的日期,也可以使用--date="日期或时间"
-m :仅修改mtime ;
-t :后面可以接欲修订的时间而不用目前的时间,格式为[YYYYMMDDhhmm]


在 ls --time 参数我们讲到 mtime(modify 修改时间,默认),atime(access 状态改变),ctime(create 创建)

1、touch 文件存在,只修改 mtime,atime,ctime 为当前时间。文件不存在,会新建,mtime,atime,ctime 为当前时间
2、即使使用 cp -a src dest,只会复制 mtime,atime,并不会复制 ctime,ctime 是命令执行的当前时间
3、注意看看下面,日期在atime 与mtime 都改变了,但是ctime 则也是记录目前的时间!

1
2
3
4
5
6
[dmtsai@study tmp]# touch -t 201406150202 bashrc 
[dmtsai@study tmp]# date; ll bashrc; ll --time=atime bashrc; ll --time=ctime bashrc
Tue Jun 16 00:54:07 CST 2015
-rw-r--r--. 1 dmtsai dmtsai 231 Jun 15 2014 bashrc
-rw-r--r--. 1 dmtsai dmtsai 231 Jun 15 2014 bashrc
-rw-r--r--. 1 dmtsai dmtsai 231 Jun 16 00:54 bashrc

无论如何, touch 这个指令最常被使用的情况是:
、建立一个空的档案;
、将某个档案日期修订为目前(mtime 与atime)

umask:档案预设权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1. 第一个 0 表示特殊权限,后面 022 表示ugo所去除的权限:u什么都不去除,g、o去除写权限
[root@localhost ~]# umask
0022

2. 默认创建文件权限就是:u=rwx,g=rx,o=rx
[root@localhost ~]# umask -S
u=rwx,g=rx,o=rx

3. 因为文件防止是木马,所以默认创建没有执行权限
[root@localhost ~]# touch aaa
[root@localhost ~]# ll aaa
-rw-r--r--. 1 root root 0 11月 4 04:09 aaa

4. 目录默认是有 x 权限的
[root@localhost ~]# mkdir abc
[root@localhost ~]# ls -ld abc/
drwxr-xr-x. 2 root root 6 11月 4 04:09 abc/

chattr:设定档案隐藏属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

[root@study ~]# chattr [+-=][ASacdistu]档案或目录名称
选项与参数:
+ :增加某一个特殊参数,其他原本存在参数则不动。
- :移除某一个特殊参数,其他原本存在参数则不动。
= :设定一定,且仅有后面接的参数

A :当设定了A 这个属性时,若你有存取此档案(或目录)时,他的存取时间atime 将不会被修改,
可避免I/O 较慢的机器过度的存取磁碟。(目前建议使用档案系统挂载参数处理这个项目)
S :一般档案是非同步写入磁碟的(原理请参考前一章sync的说明),如果加上S这个属性时,
当你进行任何档案的修改,该更动会『同步』写入磁碟中。
a (常用):当设定a 之后,这个档案将只能增加资料,而不能删除也不能修改资料,只有root 才能设定这属性
c :这个属性设定之后,将会自动的将此档案『压缩』,在读取的时候将会自动解压缩,
但是在储存的时候,将会先进行压缩后再储存(看来对于大档案似乎蛮有用的!)
d :当dump 程序被执行的时候,设定d 属性将可使该档案(或目录)不会被dump 备份
i (常用):这个i 可就很厉害了!他可以让一个档案『不能被删除、修改、改名、设定软硬链接也无法写入或新增资料!』
(但是它可以读取和执行)对于系统安全性有相当大的助益!只有root 能设定此属性
s :当档案设定了s 属性时,如果这个档案被删除,他将会被完全的移除出这个硬碟空间,
所以如果误删了,完全无法救回来了喔!
u :与s 相反的,当使用u 来设定档案时,如果该档案被删除了,则资料内容其实还存在磁碟中,
可以使用来救援该档案喔!
注意1:属性设定常见的是a 与i 的设定值,而且很多设定值必须要身为root 才能设定
注意2:xfs 档案系统仅支援AadiS 而已




[root@study ~]# cd /tmp
[root@study tmp]# touch attrtest <==建立一个空档案
[root@study tmp]# chattr +i attrtest <==给予i的属性
[root@study tmp] # rm -rf attrtest <==尝试强制删除看看
rm: remove regular empty file `attrtest'? y
rm: cannot remove `attrtest': Operation not permitted
# 看到了吗?呼呼!连root 也没有办法将这个档案删除呢!赶紧解除设定!

范例:请将该档案的i属性取消!
[root@study tmp]# chattr -i attrtest

lsattr:显示档案隐藏属性

1
2
3
4
5
6
7
8
9
10

[root@study ~]# lsattr [-adR]档案或目录
选项与参数:
-a :将隐藏档的属性也秀出来;
-d :如果接的是目录,仅列出目录本身的属性而非目录内的档名;
-R :连同子目录的资料也一并列出来!

[root@study tmp]# chattr +aiS attrtest
[root@study tmp]# lsattr attrtest
--S-ia---------- attrtest

1、src 设置 +i 之后不能建立硬链接,但可以建立软链接,
2、如果 src 与 dest 是硬链接,对其中一个 +i,两个都会有 +i 属性
3、软链接不能 lsattr 查看,因为它仅仅是一个链接,而不是文件
4、+i 之后的文件,不可以修改,移动,删除,但能读取,执行
5、+i 施加于文件夹,这个文件夹则不能添加东西
6、+a 之后的文件,只能追加,不能修改

SUID/SGID/SBIT 前引

不是应该只有rwx吗?还有其他的特殊权限( s跟t )啊?
2020-11-04 16:57:16 先提一嘴,学到后面再进行更新。

1
2
3
4
[root@localhost test]# ll -d /usr/bin/passwd /etc/shadow /tmp/
----------. 1 root root 644 11月 3 18:55 /etc/shadow
drwxrwxrwt. 9 root root 4096 11月 4 06:07 /tmp/
-rwsr-xr-x. 1 root root 27832 6月 10 2014 /usr/bin/passwd

Set UID:4

当s这个标志出现在档案拥有者的x权限上时,
例如刚刚提到的/usr/bin/passwd这个档案的权限状态:『-rw s r-xr-x』,此时就被称为Set UID,简称为SUID的特殊权限。
那么SUID的权限对于一个档案的特殊功能是什么呢?

基本上SUID有这样的限制与功能:
、SUID 权限仅对二进位程式(binary program)有效;
、执行者对于该程式需要具有x 的可执行权限;
、本权限仅在执行该程式的过程中有效(run-time);
、执行者将具有该程式拥有者(owner) 的权限。

讲这么硬的东西你可能对于SUID还是没有概念,没关系,我们举个例子来说明好了。
我们的Linux系统中,所有帐号的密码都记录在/etc/shadow这个档案里面,
这个档案的权限为:『———- 1 root root』,意思是这个档案仅有root可读且仅有root可以强制写入而已。
既然这个档案仅有root可以修改,那么鸟哥的dmtsai这个一般帐号使用者能否自行修改自己的密码呢?
你可以使用你自己的帐号输入『passwd』这个指令来看看,嘿嘿!一般使用者当然可以修改自己的密码了!

唔!有没有冲突啊!明明/etc/shadow 就不能让dmtsai 这个一般帐户去存取的,为什么dmtsai 还能够修改这个档案内的密码呢?
这就是SUID 的功能啦!藉由上述的功能说明,我们可以知道
1、dmtsai 对于/usr/bin/passwd 这个程式来说是具有x 权限的,表示dmtsai 能执行passwd;
2、passwd 的拥有者是root 这个帐号;
3、dmtsai 执行passwd 的过程中,会『暂时』获得root 的权限;
4、/etc/shadow 就可以被dmtsai 所执行的passwd 所修改。
但如果dmtsai 使用cat 去读取/etc/shadow 时,他能够读取吗?
因为cat 不具有SUID 的权限,所以dmtsai 执行『cat /etc/shadow』 时,是不能读取/etc/shadow 的。
我们用一张示意图来说明如下:
在这里插入图片描述

另外,SUID仅可用在binary program上,不能够用在shell script上面!这是因为shell script只是将很多的binary执行档叫进来执行而已!
所以SUID的权限部分,还是得要看shell script呼叫进来的程式的设定,而不是shell script本身。
当然,SUID对于目录也是无效的~这点要特别留意。
(陶攀峰总结:运行时短暂获取拥有者的权限)

Set GID:2

当s 标志在档案拥有者的x 项目为SUID,那s 在群组的x 时则称为Set GID, SGID 啰!是这样没错!^_^。
举例来说,你可以用底下的指令来观察到具有SGID 权限的档案喔:

1
2
[root@localhost test]# ll /usr/bin/locate
-rwx--s--x. 1 root slocate 40520 4月 11 2018 /usr/bin/locate

:::如果没有 locate 命令,可使用yum -y install mlocate安装

与SUID 不同的是,SGID 可以针对档案或目录来设定!
如果是对档案来说, SGID 有如下的功能:
、SGID 对二进位程式有用;
、程式执行者对于该程式来说,需具备x 的权限;
、执行者在执行的过程中将会获得该程式群组的支援!

举例来说,上面的/usr/bin/locate 这个程式可以去搜寻/var/lib/mlocate/mlocate.db 这个档案的内容(详细说明会在下节讲述), mlocate.db 的权限如下:

1
2
3
[root@localhost test]# ll /usr/bin/locate /var/lib/mlocate/mlocate.db
-rwx--s--x. 1 root slocate 40520 4月 11 2018 /usr/bin/locate
-rw-r-----. 1 root slocate 582945 11月 4 06:58 /var/lib/mlocate/mlocate.db

:::如果出现:ls: 无法访问/var/lib/mlocate/mlocate.db: 没有那个文件或目录,可使用updatedb

与SUID 非常的类似,若我使用dmtsai 这个帐号去执行locate 时,那dmtsai 将会取得slocate 群组的支援, 因此就能够去读取mlocate.db 啦!非常有趣吧!

除了binary program 之外,事实上SGID 也能够用在目录上,这也是非常常见的一种用途!当一个目录设定了SGID 的权限后,他将具有如下的功能:
、使用者若对于此目录具有r 与x 的权限时,该使用者能够进入此目录;
、使用者在此目录下的有效群组(effective group)将会变成该目录的群组;
、用途:若使用者在此目录下具有w 的权限(可以新建档案),则使用者所建立的新档案,该新档案的群组与此目录的群组相同。

SGID对于专案开发来说是非常重要的!因为这涉及群组权限的问题,您可以参考一下本章后续情境模拟的案例,应该就能够对于SGID有一些了解的!^_^
(陶攀峰总结:获取群组权限,可读取具有群组的档案)

Sticky Bit:1

这个Sticky Bit, SBIT 目前只针对目录有效,对于档案已经没有效果了。SBIT 对于目录的作用是:
、当使用者对于此目录具有w, x 权限,亦即具有写入的权限时;
、当使用者在该目录下建立档案或目录时,仅有自己与root 才有权力删除该档案

换句话说:当甲这个使用者于A目录是具有群组或其他人的身份,并且拥有该目录w的权限,这表示『甲使用者对该目录内任何人建立的目录或档案均可进行”删除/更名/搬移”等动作。』
不过,如果将A目录加上了SBIT的权限项目时,则甲只能够针对自己建立的档案或目录进行删除/更名/移动等动作,而无法删除他人的档案。

举例来说,我们的/tmp 本身的权限是『drwxrwxrwt』, 在这样的权限内容下,任何人都可以在/tmp 内新增、修改档案,但仅有该档案/目录建立者与root 能够删除自己的目录或档案。这个特性也是挺重要的啊!
你可以这样做个简单的测试:
1、以root 登入系统,并且进入/tmp 当中;
2、touch test,并且更改test 权限成为777 ;
3、以一般使用者登入,并进入/tmp;
4、尝试删除test 这个档案!
由于SUID/SGID/SBIT牵涉到程序的概念,因此再次强调,这部份的资料在您读完第十六章关于程序方面的知识后,要再次的回来瞧瞧喔!
目前,你先有个简单的基础概念就好了!文末的参考资料也建议阅读一番喔!

(陶攀峰总结:这个目录里的东西只要自己和root可操作,其他人都不可以操作)

SUID/SGID/SBIT 权限设定

前面介绍过SUID与SGID的功能,那么如何设定档案使成为具有SUID与SGID的权限呢?这就需要第五章的数字更改权限的方法了!
现在你应该已经知道数字型态更改权限的方式为『三个数字』的组合,那么如果在这三个数字之前再加上一个数字的话,最前面的那个数字就代表这几个权限了!

1
2
3
4 为SUID
2 为SGID
1 为SBIT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1. 进入目录,建立一个测试用空档
[root@localhost test]# cd /tmp/
[root@localhost tmp]# touch test

2. 加入具有SUID的权限
[root@localhost tmp]# chmod 4755 test ; ll test
-rwsr-xr-x. 1 root root 0 11月 4 07:21 test

3. 加入具有SUID/SGID的权限
[root@localhost tmp]# chmod 6755 test ; ll test
-rwsr-sr-x. 1 root root 0 11月 4 07:21 test

4. 加入SBIT的功能!
[root@localhost tmp]# chmod 1755 test ; ll test
-rwxr-xr-t. 1 root root 0 11月 4 07:21 test

5. 具有空的SUID/SGID权限
[root@localhost tmp]# chmod 7666 test ; ll test
-rwSrwSrwT. 1 root root 0 11月 4 07:21 test

最后一个例子就要特别小心啦!怎么会出现大写的S与T呢?不都是小写的吗?因为s与t都是取代x这个权限的,但是你有没有发现阿,我们是下达7666喔!
也就是说, user, group以及others都没有x这个可执行的标志(因为666嘛),所以,这个S, T代表的就是『空的』啦!
怎么说?SUID是表示『该档案在执行的时候,具有档案拥有者的权限』,但是档案拥有者都无法执行了,哪里来的权限给其他人使用?当然就是空的啦!^_^

而除了数字法之外,妳也可以透过符号法来处理喔!其中SUID 为u+s ,而SGID 为g+s ,SBIT 则是o+t 啰!来看看如下的范例:

1
2
3
4
5
6
7
1. 设定权限成为-rws--x--x的模样
[root@localhost test]# chmod u=rwxs,go=x test ; ll test
-rws--x--x. 1 root root 0 11月 4 07:24 test

2. 承上,加上SGID与SBIT在上述的档案权限中!
[root@localhost test]# chmod g+s,o+t test ; ll test
-rws--s--t. 1 root root 0 11月 4 07:24 test

file:查看文件类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
1. empty 档
[root@localhost test]# touch a ; file a
a: empty

2. ASCII 纯文字档
[root@localhost test]# echo abc > a ; file a
a: ASCII text

2. UTF-8 与 Unicode 混合文字档
[root@localhost test]# echo 我是abc > a ; file a
a: UTF-8 Unicode text

3. 执行档的资料可就多的不得了!包括这个档案的suid 权限、相容于Intel x86-64 等级的硬体平台,使用的是Linux核心2.6.32的动态函式库连结等等。
[root@localhost test]# file /usr/bin/passwd
/usr/bin/passwd: setuid ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=1e5735bf7b317e60bcb907f1989951f6abd50e8d, stripped

4. sticky bit
[root@localhost test]# file /tmp/
/tmp/: sticky directory

5. setgid
[root@localhost test]# file /usr/bin/locate
/usr/bin/locate: setgid ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=00ac32414f03ea986730e50a0d446b2ca9bd05f1, stripped

6. data
[root@localhost test]# file /var/lib/mlocate/mlocate.db
/var/lib/mlocate/mlocate.db: data
[root@localhost test]#

透过这个指令,我们可以简单的先判断这个档案的格式为何喔!
包括未来你也可以用来判断使用tar 包裹时,该tarball 档案是使用哪一种压缩功能哩!

which:查看命令别名,所在位置

2020-11-05 10:25:20
寻找『执行档』

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@study ~]# which [-a] command
选项或参数:
-a :将所有由PATH 目录中可以找到的指令均列出,而不止第一个被找到的指令名称

范例一:搜寻ifconfig这个指令的完整档名
[root@study ~]# which ifconfig
/sbin/ifconfig

范例二:用which去找出which的档名为何?
[root@study ~]# which which
alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
/bin/alias
/usr/bin/which
# 竟然会有两个which ,其中一个是alias 这玩意儿呢!那是啥?
# 那就是所谓的『命令别名』,意思是输入which 会等于后面接的那串指令啦!
# 更多的资料我们会在bash 章节中再来谈的!

范例三:请找出history这个指令的完整档名
[root@study ~]# which history
/usr/bin/which: no history in (/usr/local/sbin:/usr/local/bin:/sbin:/bin:
/usr/sbin:/usr/bin:/root/bin)

[root@study ~]# history --help
-bash: history: --: invalid option
history: usage: history [-c] [-d offset] [n] or history -anrw [filename] or history -ps arg
# 瞎密?怎么可能没有history ,我明明就能够用root 执行history 的啊!

这个指令是根据『PATH』这个环境变数所规范的路径,去搜寻『执行档』的档名~所以,重点是找出『执行档』而已!
且which后面接的是『完整档名』喔!若加上-a选项,则可以列出所有的可以找到的同名执行档,而非仅显示第一个而已!

df:查看卷

列出档案系统的整体磁碟使用量;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
[root@study ~]# df [-ahikHTm] [目录或档名]
选项与参数:
-a :列出所有的档案系统,包括系统特有的/proc 等档案系统;
-k :以KBytes 的容量显示各档案系统;
-m :以MBytes 的容量显示各档案系统;
-h :以人们较易阅读的GBytes, MBytes, KBytes 等格式自行显示;
-H :以M=1000K 取代M=1024K 的进位方式;
-T :连同该partition 的filesystem 名称(例如xfs) 也列出;
-i :不用磁碟容量,而以inode 的数量来显示


-------------------------df #===> 列出我 Linux 的所有挂载点
文件系统 1K-块 已用 可用 已用% 挂载点
/dev/mapper/centos-root 10475520 2623152 7852368 26% /
devtmpfs 923704 0 923704 0% /dev
tmpfs 934328 0 934328 0% /dev/shm
tmpfs 934328 8800 925528 1% /run
tmpfs 934328 0 934328 0% /sys/fs/cgroup
/dev/sda1 2086912 134628 1952284 7% /boot
tmpfs 186868 0 186868 0% /run/user/0
-------------------------df -h #===> 容量会以 M/G 方式显示
文件系统 容量 已用 可用 已用% 挂载点
/dev/mapper/centos-root 10G 2.6G 7.5G 26% /
devtmpfs 903M 0 903M 0% /dev
tmpfs 913M 0 913M 0% /dev/shm
tmpfs 913M 8.6M 904M 1% /run
tmpfs 913M 0 913M 0% /sys/fs/cgroup
/dev/sda1 2.0G 132M 1.9G 7% /boot
tmpfs 183M 0 183M 0% /run/user/0
-------------------------df -Th #===> 显示挂载类型
文件系统 类型 容量 已用 可用 已用% 挂载点
/dev/mapper/centos-root xfs 10G 2.6G 7.5G 26% /
devtmpfs devtmpfs 903M 0 903M 0% /dev
tmpfs tmpfs 913M 0 913M 0% /dev/shm
tmpfs tmpfs 913M 8.6M 904M 1% /run
tmpfs tmpfs 913M 0 913M 0% /sys/fs/cgroup
/dev/sda1 xfs 2.0G 132M 1.9G 7% /boot
tmpfs tmpfs 183M 0 183M 0% /run/user/0
-------------------------df -ih #===> 显示磁碟的 INODE 数量
文件系统 Inode 已用(I) 可用(I) 已用(I)% 挂载点
/dev/mapper/centos-root 10M 34K 10M 1% /
devtmpfs 226K 369 226K 1% /dev
tmpfs 229K 1 229K 1% /dev/shm
tmpfs 229K 497 228K 1% /run
tmpfs 229K 13 229K 1% /sys/fs/cgroup
/dev/sda1 2.0M 330 2.0M 1% /boot
tmpfs 229K 1 229K 1% /run/user/0
-------------------------df -h /app/test/ #===> 显示 /app/test 所在挂载点的信息
文件系统 容量 已用 可用 已用% 挂载点
/dev/mapper/centos-root 10G 2.6G 7.5G 26% /
-------------------------df -aTh #===> 显示所有可用信息,包含 /sys,/proc 等
文件系统 类型 容量 已用 可用 已用% 挂载点
rootfs - - - - - /
sysfs sysfs 0 0 0 - /sys
proc proc 0 0 0 - /proc
devtmpfs devtmpfs 903M 0 903M 0% /dev
securityfs securityfs 0 0 0 - /sys/kernel/security
tmpfs tmpfs 913M 0 913M 0% /dev/shm
devpts devpts 0 0 0 - /dev/pts
tmpfs tmpfs 913M 8.6M 904M 1% /run
tmpfs tmpfs 913M 0 913M 0% /sys/fs/cgroup
cgroup cgroup 0 0 0 - /sys/fs/cgroup/systemd
pstore pstore 0 0 0 - /sys/fs/pstore
cgroup cgroup 0 0 0 - /sys/fs/cgroup/devices
cgroup cgroup 0 0 0 - /sys/fs/cgroup/perf_event
cgroup cgroup 0 0 0 - /sys/fs/cgroup/cpu,cpuacct
cgroup cgroup 0 0 0 - /sys/fs/cgroup/net_cls
cgroup cgroup 0 0 0 - /sys/fs/cgroup/memory
cgroup cgroup 0 0 0 - /sys/fs/cgroup/freezer
cgroup cgroup 0 0 0 - /sys/fs/cgroup/blkio
cgroup cgroup 0 0 0 - /sys/fs/cgroup/hugetlb
cgroup cgroup 0 0 0 - /sys/fs/cgroup/cpuset
configfs configfs 0 0 0 - /sys/kernel/config
/dev/mapper/centos-root xfs 10G 2.6G 7.5G 26% /
selinuxfs selinuxfs 0 0 0 - /sys/fs/selinux
systemd-1 - - - - - /proc/sys/fs/binfmt_misc
mqueue mqueue 0 0 0 - /dev/mqueue
debugfs debugfs 0 0 0 - /sys/kernel/debug
hugetlbfs hugetlbfs 0 0 0 - /dev/hugepages
/dev/sda1 xfs 2.0G 132M 1.9G 7% /boot
tmpfs tmpfs 183M 0 183M 0% /run/user/0
binfmt_misc binfmt_misc 0 0 0 - /proc/sys/fs/binfmt_misc

另外需要注意的是,如果使用-a 这个参数时,系统会出现/proc 这个挂载点,但是里面的东西都是 0 ,不要紧张!/proc 的东西都是Linux 系统所需要载入的系统资料,而且是挂载在『记忆体当中』的, 所以当然没有占任何的磁碟空间啰!

至于那个/dev/shm/目录,其实是利用记忆体虚拟出来的磁碟空间,通常是总实体记忆体的一半! 由于是透过记忆体模拟出来的磁碟,因此你在这个目录底下建立任何资料档案时,存取速度是非常快速的!(在记忆体内工作)不过,也由于他是记忆体模拟出来的,因此这个档案系统的大小在每部主机上都不一样,而且建立的东西在下次开机时就消失了!因为是在记忆体中嘛!

du:查看目录

评估档案系统的磁碟使用量(常用在推估目录所占容量)
注意:这里并不是真实的使用大小,而是占用的大小。因为单位至少 4KB,所以都是 4KB 的倍数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
[root@study ~]# du [-ahskm]档案或目录名称
选项与参数:
-a :列出所有的档案与目录容量,因为预设仅统计目录底下的档案量而已。
-h :以人们较易读的容量格式(G/M) 显示;
-s :列出总量而已,而不列出每个各别的目录占用容量;
-S :不包括子目录下的总计,与-s 有点差别。
-k :以KBytes 列出容量显示;
-m :以MBytes 列出容量显示;

-------------------------du . #===> 查询本地目录。默认 . 小数点可用省略。可把 . 小数点换成其他路径。
8 ./sh/function
40 ./sh/loop
28 ./sh/practice
140 ./sh
152 .
-------------------------du -a #===> 查询本地所有目录,与文件
12 ./.printf.txt.swp
4 ./sh/hello.sh
...
4 ./sh/loop/dir_perm.sh
4 ./sh/loop/for5.sh
4 ./sh/loop/a.sh
40 ./sh/loop
4 ./sh/practice/howami-pwd.sh
4 ./sh/practice/birthday.sh
4 ./sh/practice/sum.sh
4 ./sh/practice/check.sh
4 ./sh/practice/passwd.sh
4 ./sh/practice/passwd2.sh
28 ./sh/practice
140 ./sh
152 .
-------------------------du -S #===> 查看本地子目录的大小,并显示目录。但不显示目录的文件名称
8 ./sh/function
40 ./sh/loop
28 ./sh/practice
64 ./sh
12 .
------------------du -s /app/test/sh/ #===> 只查看当前目录的总量
140 /app/test/sh/
-------------------------du -h /app/ #===> 指定路径来查看文件,-h 显示 K/M/G
8.0K /app/test/sh/function
40K /app/test/sh/loop
28K /app/test/sh/practice
140K /app/test/sh
152K /app/test
84K /app/docker/redis
84K /app/docker
236K /app/

free:显示内存

显示系统中空闲内存和使用内存的数量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
-------------------------free --help

Usage:
free [options]

Options:
-b, --bytes 以 B 为单位显示输出
-k, --kilo 以 KB 为单位显示输出(默认)
-m, --mega 以 MB 为单位显示输出
-g, --giga 以 GB 为单位显示输出
--tera 以 TB 为单位显示输出
-h, --human 以 M/G 显示
--si 使用 1000 进制,而不是 1024
-l, --lohi 显示详细的低内存和高内存统计信息
-t, --total 显示 RAM + swap 的总数
-s N, --seconds N 每N秒重复打印一次
-c N, --count N 重复打印N次,然后退出
-w, --wide 宽范围输出



-------------------------free #===> 查看所有内存(KB)
total used free shared buff/cache available
Mem: 1868660 214968 1019884 8772 633808 1404892
Swap: 3145724 0 3145724
-------------------------free -h #===> 查看所有内存(M/G)
total used free shared buff/cache available
Mem: 1.8G 209M 995M 8.6M 618M 1.3G
Swap: 3.0G 0B 3.0G

ln:软硬链接

1
2
3
4
5
6
7
8
[root@study ~]# ln [-sf]来源档目标档
选项与参数:
-s :如果不加任何参数就进行连结,那就是hard link,至于-s就是symbolic link
-f :如果目标档存在时,就主动的将目标档直接移除后再建立!

ln src dest #===> 硬链接
ln -s src dest #===> 软链接
ln -f src dest #===> 强制硬链接,若dest存在,就移除再创建

删除软链接

2021-01-14 19:11:48
这个有必要拿出来说,有可能你会造成大错。

1
2
3
4
5
6
7
8
lrwxrwxrwx 1 root root   28 Jan 14 19:05 script -> /data/httpd/order-bff/script


# 删除软链接(注意,后面是没有 / 斜杠的)
rm -rf script

# 删除软链接指向的内容(注意,后面有 / 斜杠)
rm -rf script/

》》同理,你要想查看软链接的详情也不能加上 “/” 斜杠

1
2
3
4
5
6
7
8
9
10
11
# 加了 / 
----------------ll /script/
total 36
-rw-r--r-- 1 root root 650 Jan 14 19:35 azcopy_install.sh
-rw-r--r-- 1 root root 890 Jan 14 19:05 docker-bff-start.sh
...


# 不加 /
----------------ll /script
lrwxrwxrwx 1 root root 28 Jan 14 12:00 /script -> /data/httpd/order-bff/script

软硬链接分析

2020-11-09 14:33:44
inode 只是记录的一个编号,cp -l a aa,或 ln a aa 建立的都只是一个档名而已,而不是建立档案。
删除其中一个不会影响另外一个,是因为删除的是一个档名而已。
硬链接不能:目录

cp -s a aa,或 ln -s a aa 不同于 hard link,因为 aa 是指向 a 的一个快捷方式而已。
删除了 a 就相当于删除了档名和档案,所以 aa 就找不到 a 档案了。
软链接不能:删除文件,再对 dest 进行 cat

mount:挂载

2020-11-11 09:43:52
mount –bind m1 m2
执行了这个之后,会把 m2 的 inode 指向 m1,此时 两个目录都指向 m1 的 inode。
所以,进入 m2 就相当于进入 m1,其中一个删除或更新,另外一个就会改变,因为操作的是同一个 inode。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
[root@localhost ~]# mkdir -p /app/test/
[root@localhost ~]# cd /app/test/
[root@localhost test]#
[root@localhost test]#
[root@localhost test]# mkdir m1 m2
[root@localhost test]#
1. inode 不一致
[root@localhost test]# ll -id m1 m2
17873125 drwxr-xr-x. 2 root root 6 11月 10 08:25 m1
33767966 drwxr-xr-x. 2 root root 6 11月 10 08:25 m2
[root@localhost test]#
2. 挂载【将 m2 的 inode 也指向 m1】
[root@localhost test]# mount --bind m1 m2
[root@localhost test]#
[root@localhost test]# ll -id m1 m2
17873125 drwxr-xr-x. 2 root root 6 11月 10 08:25 m1
17873125 drwxr-xr-x. 2 root root 6 11月 10 08:25 m2
[root@localhost test]#
3. 查看挂载内容
[root@localhost test]# mount | grep /app/test/
/dev/mapper/centos-root on /app/test/m2 type xfs (rw,relatime,seclabel,attr2,inode64,noquota)
[root@localhost test]#
[root@localhost test]#
4. 卸载
[root@localhost test]# umount /app/test/m2


5. 当前目录不能在 /app/test/m2 里面,否则卸载不成功。回到 / 根目录再进行卸载。
[root@localhost test]# mount --bind m1 m2
[root@localhost test]#
[root@localhost test]#
[root@localhost test]# cd m2
[root@localhost m2]#
[root@localhost m2]# umount /app/test/m2/
umount: /app/test/m2:目标忙。
(有些情况下通过 lsof(8) 或 fuser(1) 可以
找到有关使用该设备的进程的有用信息)
[root@localhost m2]#
[root@localhost m2]# cd /
[root@localhost /]# umount /app/test/m2/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
mount [-hV]
mount -a [-fFnrsvw] [-t vfstype]
mount [-fnrsvw] [-o options [,...]] device | dir
mount [-fnrsvw] [-t vfstype] [-o options] device dir

-V:显示程序版本
-h:显示辅助讯息
-v:显示较讯息,通常和 -f 用来除错。
-a:将 /etc/fstab 中定义的所有档案系统挂上。
-F:这个命令通常和 -a 一起使用,它会为每一个 mount 的动作产生一个行程负责执行。在系统需要挂上大量 NFS 档案系统时可以加快挂上的动作。
-f:通常用在除错的用途。它会使 mount 并不执行实际挂上的动作,而是模拟整个挂上的过程。通常会和 -v 一起使用。
-n:一般而言,mount 在挂上后会在 /etc/mtab 中写入一笔资料。但在系统中没有可写入档案系统存在的情况下可以用这个选项取消这个动作。
-s-r:等于 -o ro
-w:等于 -o rw
-L:将含有特定标签的硬盘分割挂上。
-U:将档案分割序号为 的档案系统挂下。-L 和 -U 必须在/proc/partition 这种档案存在时才有意义。
-t:指定档案系统的型态,通常不必指定。mount 会自动选择正确的型态。
-o async:打开非同步模式,所有的档案读写动作都会用非同步模式执行。
-o sync:在同步模式下执行。
-o atime、-o noatime:当 atime 打开时,系统会在每次读取档案时更新档案的『上一次调用时间』。当我们使用 flash 档案系统时可能会选项把这个选项关闭以减少写入的次数。
-o auto、-o noauto:打开/关闭自动挂上模式。
-o defaults:使用预设的选项 rw, suid, dev, exec, auto, nouser, and async.
-o dev、-o nodev-o exec、-o noexec允许执行档被执行。
-o suid、-o nosuid:允许执行档在 root 权限下执行。
-o user、-o nouser:使用者可以执行 mount/umount 的动作。
-o remount:将一个已经挂下的档案系统重新用不同的方式挂上。例如原先是唯读的系统,现在用可读写的模式重新挂上。
-o ro:用唯读模式挂上。
-o rw:用可读写模式挂上。
-o loop=:使用 loop 模式用来将一个档案当成硬盘分割挂上系统。

# 将 /dev/hda1 挂在 /mnt 之下
mount /dev/hda1 /mnt

# 将 /dev/hda1 用唯读模式挂在 /mnt 之下
mount -o ro /dev/hda1 /mnt

磁碟空间之浪费问题

我们在前面的EXT2 data block介绍中谈到了一个block只能放置一个档案,因此太多小档案将会浪费非常多的磁碟容量。但你有没有注意到,整个档案系统中包括superblock, inode table与其他中介资料等其实都会浪费磁碟容量喔!所以当我们在/dev/vda4, /dev/vda5建立起xfs/ext4档案系统时,一挂载就立刻有很多容量被用掉了!

另外,不知道你有没有发现到,当你使用ls -l 去查询某个目录下的资料时,第一行都会出现一个『total』的字样!那是啥东西?其实那就是该目录下的所有资料所耗用的实际block 数量* block 大小的值。我们可以透过ll -s 来观察看看上述的意义:

1
2
3
4
5
6
7
8
9
10
[root@study ~]# ll -sh 
total 12K
4.0K -rw-------. 1 root root 1.8K May 4 17:57 anaconda-ks.cfg
4.0K -rw-r--r-- . 2 root root 451 Jun 10 2014 crontab
0 lrwxrwxrwx. 1 root root 12 Jun 23 22:31 crontab2 -> /etc/crontab
4.0K -rw-r--r--. 1 root root 1.9K May 4 18:01 initial-setup-ks.cfg
0 -rw-r--r--. 1 root root 0 Jun 16 01:11 test1
0 drwxr-xr-x. 2 root root 6 Jun 16 01:11 test2
0 -rw-rw -r--. 1 root root 0 Jun 16 01:12 test3
0 drwxrwxr-x. 2 root root 6 Jun 16 01:12 test4

从上面的特殊字体部分,那就是每个档案所使用掉block 的容量!
举例来说,那个crontab 虽然仅有451bytes , 不过他却占用了整个block (每个block 为4K),所以将所有的档案的所有的block 加总就得到12Kbytes 那个数值了。
如果计算每个档案实际容量的加总结果,其实只有不到5K 而已~所以啰,这样就耗费掉好多容量了!未来大家在讨论小磁碟、 大磁碟,档案容量的损耗时,要回想到这个区块喔!^_^

压缩后缀名

1
2
3
4
5
6
7
8
9
*.Z         compress 程式压缩的档案;
*.zip zip 程式压缩的档案;
*.gz gzip 程式压缩的档案;
*.bz2 bzip2 程式压缩的档案;
*.xz xz 程式压缩的档案;
*.tar tar 程式打包的资料,并没有压缩过;
*.tar.gz tar 程式打包的档案,其中并且经过gzip 的压缩
*.tar.bz2 tar 程式打包的档案,其中并且经过bzip2 的压缩
*.tar.xz tar 程式打包的档案,其中并且经过xz 的压缩

这些都是针对单一档案的压缩/解压,压缩一大推,需要用到 tar 命令。

gzip 可解压 compress,zip,gzip 压缩的档案
.gz 文件可被 Windows 的 WinRAR/7zip/Bandizip 解压

gzip,gunzip,zcat,zmore,zless,zgrep

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
[dmtsai@study ~]$ gzip [-cdtv#] 档名
[dmtsai@study ~]$ zcat 档名.gz
选项与参数:
-c :将压缩的资料输出到萤幕上,可透过资料流重导向来处理;
-d :解压缩的参数;
-t :可以用来检验一个压缩档的一致性~看看档案有无错误;
-v :可以显示出原档案/压缩档案的压缩比等资讯;
-# :# 为数字的意思,代表压缩等级,-1 最快,但是压缩比最差、-9 最慢,但是压缩比最好!预设是-6



1. gzip 不保留原文件,压缩
[root@localhost test]# ll
总用量 4
-rw-r--r--. 1 root root 125 11月 10 14:59 a
[root@localhost test]#
[root@localhost test]# gzip a
[root@localhost test]#
[root@localhost test]# ll
总用量 4
-rw-r--r--. 1 root root 127 11月 10 14:59 a.gz


2. gzip -c 保留原文件,压缩,可自定义压缩名称
[root@localhost test]# ll
总用量 4
-rw-r--r--. 1 root root 125 11月 10 14:59 a
[root@localhost test]# gzip -c a > aa.gz
[root@localhost test]#
[root@localhost test]# ll
总用量 8
-rw-r--r--. 1 root root 125 11月 10 14:59 a
-rw-r--r--. 1 root root 127 11月 10 15:39 aa.gz


3. gzip -d 解压缩文件(我更习惯使用这个命令替代 gunzip)
[root@localhost test]# ll
总用量 4
-rw-r--r--. 1 root root 127 11月 10 14:59 a.gz
[root@localhost test]#
[root@localhost test]# gzip -d a.gz ===> 同 gunzip a.gz
[root@localhost test]#
[root@localhost test]# ll
总用量 4
-rw-r--r--. 1 root root 125 11月 10 14:59 a

4. zcat,zmore,zless 查看 .gz 纯文本内容
[root@localhost test]# zcat a
Asdjlfks
dgfdsl;kgkdfm dfgk df dfgdfg fg fg fg
...

5. zgrep 搜索 .gz 纯文本内容,-n 显示行号
[root@localhost test]# zgrep -n 更快 a.gz
12:ghgkk世纪东方给大家地方了 飞得更快了

bzip2,bunzip2,bzcat,bzmore,bzless,bzgrep

1
2
3
4
5
6
7
8
9
10
11
[dmtsai@study ~]$ bzip2 [-cdkzv#] 档名
[dmtsai@study ~]$ bzcat 档名.bz2
选项与参数:
-c :将压缩的过程产生的资料输出到萤幕上!
-d :解压缩的参数
-k :保留原始档案,而不会删除原始的档案喔!
-z :压缩的参数(预设值,可以不加)
-v :可以显示出原档案/压缩档案的压缩比等资讯;
-# :与gzip 同样的,都是在计算压缩比的参数, -9 最佳, -1 最快!

用法同 gzip 命令

xz,unxz,xzcat,xzmore,xzless,xzgrep

1
2
3
4
5
6
7
8
9
10
11
[dmtsai@study ~]$ xz [-dtlkc#] 档名
[dmtsai@study ~]$ xzcat 档名.xz
选项与参数:
-d :就是解压缩啊!
-t :测试压缩档的完整性,看有没有错误
-l :列出压缩档的相关资讯
-k :保留原本的档案不删除~
-c :同样的,就是将资料由萤幕上输出的意思!
-# :同样的,也有较佳的压缩比的意思!

用法同 gzip 命令

zip,unzip

1
2
3
4
5
6
7
8
# 压缩文件
zip file.zip file

# 压缩目录,以及目录内容
zip -r dir.zip dir

# 解压
unzip dir.zip

tar:打包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

[dmtsai@study ~]$ tar [-z|-j|-J] [cv] [-f待建立的新档名] filename... <==打包与压缩
[dmtsai@study ~]$ tar [ -z|-j|-J] [tv] [-f既有的tar档名] <==察看档名
[dmtsai@study ~]$ tar [-z|-j|-J] [xv] [ -f既有的tar档名] [-C目录] <==解压缩
选项与参数:
-c :建立打包档案,可搭配-v 来察看过程中被打包的档名(filename)
-t :察看打包档案的内容含有哪些档名,重点在察看『档名』就是了;
-x :解打包或解压缩的功能,可以搭配-C (大写) 在特定目录解开
特别留意的是, -c, -t, -x 不可同时出现在一串指令列中。
-f :指定打包名(与其他命令放一起时,要放在后面)
-z :透过gzip 的支援进行压缩/解压缩:此时档名最好为*.tar.gz
-j :透过bzip2 的支援进行压缩/解压缩:此时档名最好为*.tar.bz2
-J :透过xz 的支援进行压缩/解压缩:此时档名最好为*.tar.xz
特别留意, -z, -j, -J 不可以同时出现在一串指令列中
-v :在压缩/解压缩的过程中,将正在处理的档名显示出来!
-f filename:-f 后面要立刻接要被处理的档名!建议-f 单独写一个选项啰!(比较不会忘记)
-C 目录:这个选项用在解压缩,若要在特定目录解压缩,可以使用这个选项。

其他后续练习会使用到的选项介绍:
-p(小写) :保留备份资料的原本权限与属性,常用于备份(-c)重要的设定档
-P(大写) :保留绝对路径,亦即允许备份资料中含有根目录存在之意;
--exclude=FILE:在压缩的过程中,不要将FILE 打包!



# 1. 打包
tar -cf file.tar file ===> 打包文件
tar -cf dir.tar dir ===> 打包目录,以及内容
tar -cf d1d2f1.tar dir1 dir2 file1 ===> 打包多个

# 2. 解压
tar -xf a.tar
tar -xf a.tar -C /root ===> 解压的内容放到 /root 目录下

# 3. 【-z】打包成 tar,再把 tar 使用 gzip 压缩,-j -J 同理于 bzip2,xz
tar -zcf ab.tar.gz a b ===> 会把 a b 打包成 ab.tar,再使用 gzip 进行压缩成 ab.tar.gz
tar -zxf ab.tar.gz ===> 解压成 ab.tar,再分解成 a b

# 4. 查看内容
tar -tf xxx.tar
tar -tf xxx.tar.gz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
### 1. 压缩 ###
[root@localhost test]# ll
总用量 0
drwxr-xr-x. 2 root root 24 11月 10 16:46 a
drwxr-xr-x. 2 root root 15 11月 10 16:46 b
[root@localhost test]#
[root@localhost test]# tar -zcf ab.tar.gz a b
[root@localhost test]#
[root@localhost test]# ll
总用量 4
drwxr-xr-x. 2 root root 24 11月 10 16:46 a
-rw-r--r--. 1 root root 191 11月 10 18:12 ab.tar.gz
drwxr-xr-x. 2 root root 15 11月 10 16:46 b

### 2. 解压 ###
[root@localhost test]# rm -rf a b
[root@localhost test]# ll
总用量 4
-rw-r--r--. 1 root root 191 11月 10 18:12 ab.tar.gz
[root@localhost test]#
[root@localhost test]# tar -zxf ab.tar.gz
[root@localhost test]# ll
总用量 4
drwxr-xr-x. 2 root root 24 11月 10 16:46 a
-rw-r--r--. 1 root root 191 11月 10 18:12 ab.tar.gz
drwxr-xr-x. 2 root root 15 11月 10 16:46 b

### 3. 查看 ###
[root@localhost test]# tar -tf ab.tar.gz
a/
a/a1
a/a2
b/
b/b1

### 4. 解压单个 ###
[root@localhost c]# ll
总用量 12
-rw-r--r--. 1 root root 10240 11月 10 21:44 a.tar
[root@localhost c]#
[root@localhost c]#
[root@localhost c]# tar -tf a.tar
a/
a/a1
a/a2
[root@localhost c]#
[root@localhost c]# tar -xf a.tar a/a1
[root@localhost c]#
[root@localhost c]# ll
总用量 12
drwxr-xr-x. 2 root root 15 11月 10 21:47 a
-rw-r--r--. 1 root root 10240 11月 10 21:44 a.tar
[root@localhost c]#
[root@localhost c]# ls a
a1


### 5. 打包时排除一些文件 ###
[root@localhost c]# ll
总用量 0
drwxr-xr-x. 2 root root 53 11月 10 21:52 a
[root@localhost c]# ls a
a1 a2 a3 aa1 aa2
[root@localhost c]#
[root@localhost c]#
[root@localhost c]# tar -cf a1.tar --exclude=a/a2 --exclude=a/a3 --exclude=aa* a
[root@localhost c]#
[root@localhost c]# ll
总用量 12
drwxr-xr-x. 2 root root 53 11月 10 21:52 a
-rw-r--r--. 1 root root 10240 11月 10 21:53 a1.tar
[root@localhost c]# tar -tf a1.tar
a/
a/a1

tarfile 是 .tar 文件,仅是打包文件
tarball 是 .tar.gz,或.tar.xz 等,它们是 tar 打包之后再 gzip,或xz压缩的文件

tar 与 wget

2020-12-25 16:21:10
参考关于tar解压重命名的问题

1
2
3
4
5
1、下载压缩包,并改名 mysql.tar.gz (此时直接解压后目录是:mysql-5.6.15-linux-glibc2.5-i686)
wget -O mysql.tar.gz http://oss.aliyuncs.com/aliyunecs/onekey/mysql/mysql-5.6.15-linux-glibc2.5-i686.tar.gz

2、解压,并修改解压后的目录名称为:mysql
mkdir mysql ; tar -zxf mysql.tar.gz -C mysql --strip-components=1

wget:下载器

2020-12-25 16:21:07

1、默认下载,会在当前路径保存index.html文件

1
wget www.baidu.com/index.html

2、下载并指定名称,会在当前路径保存diy.html文件

1
wget -O diy.html www.baidu.com/index.html

3、下载放入指定文件夹,没有则会递归创建

1
2
3
4
5
6
wget -P /a/b/c www.baidu.com/index.html

等同于

mkdir -p /a/b/c
再把下载的 index.html 放入 /a/b/c

4、下载放入指定文件夹并改名,这个是不可以的(-P 会失效,会在当前路径保存diy.html文件)

1
wget -O diy.html -P /aaa www.baidu.com/index.html

vim 快捷键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
h 或向左方向键(←) 游标向左移动一个字元
j 或向下方向键(↓) 游标向下移动一个字元
k 或向上方向键(↑) 游标向上移动一个字元
l 或向右方向键(→) 游标向右移动一个字元
30→ 或 30l 指针都会向右移动 30 次。

w 跳转到下一个单次的首字母

[Ctrl] + [f] 萤幕『向下』移动一页,相当于[Page Down]按键(常用)
[Ctrl] + [b] 萤幕『向上』移动一页,相当于[Page Up]按键(常用)
[Ctrl] + [d] 萤幕『向下』移动半页
[Ctrl] + [u] 萤幕『向上』移动半页

+ 游标移动到非空白字元的下一列
- 游标移动到非空白字元的上一列

n<space> 那个n 表示『数字』,例如20 。按下数字后再按空白键,游标会向右移动这一列的n 个字元。例如20<space> 则游标会向后面移动20 个字元距离。

0 或[Home]或^ 这是数字『 0 』:移动到这一列的最前面字元处(常用)
$ 或[End] 移动到这一列的最后面字元处(常用)

H 游标移动到当前萤幕的最上方那一列的第一个字元
M 游标移动到当前萤幕的中央那一列的第一个字元
L 游标移动到当前萤幕的最下方那一列的第一个字元

G 移动到这个档案的最后一列(常用)
nG n 为数字。移动到这个档案的第n 列。例如20G 则会移动到这个档案的第20 列(可配合:set nu)
gg 移动到这个档案的第一列,相当于1G啊!(常用)
n<Enter> n为数字。游标向下移动n列(常用)

/word 向游标之下寻找一个名称为word的字串。例如要在档案内搜寻vbird这个字串,就输入/vbird即可!(常用)
?word 向游标之上寻找一个字串名称为word 的字串。
n 这个n是英文按键。代表『重复前一个搜寻的动作』。
举例来说,如果刚刚我们执行/vbird去向下搜寻vbird这个字串,则按下n后,会向下继续搜寻下一个名称为vbird的字串。
如果是执行?vbird的话,那么按下n则会向上继续搜寻名称为vbird的字串!
N 这个N 是英文按键。与n 刚好相反,为『反向』进行前一个搜寻动作。
例如/vbird 后,按下N 则表示『向上』搜寻vbird 。
使用/word 配合n 及N 是非常有帮助的!可以让你重复的找到一些你搜寻的关键字!
:n1,n2s/word1/word2/g n1与n2为数字。在第n1与n2列之间寻找word1这个字串,并将该字串取代为word2 !
举例来说,在100到200列之间搜寻vbird并取代为VBIRD则:『:100,200s/vbird/VBIRD/g』。(常用)
:1,$s/word1/word2/g 从第一列到最后一列寻找word1字串,并将该字串取代为word2 !(常用)
:1,$s/word1/word2/gc 从第一列到最后一列寻找word1字串,并将该字串取代为word2 !且在取代前显示提示字元给使用者确认(confirm)是否需要取代!(常用)


x, X 在一列字当中,x为向后删除一个字元(相当于[del]按键), X为向前删除一个字元(相当于[backspace]亦即是倒退键) (常用)
nx n 为数字,连续向后删除n 个字元。举例来说,我要连续删除10 个字元, 『10x』。
dd 删除游标所在的那一整列(常用)
ndd n为数字。删除游标所在的向下n列,例如20dd则是删除20列(常用)
d1G 删除[第1行,当前行]所有资料
dG 删除[当前行,最后1行]所有资料
d$ 删除游标所在处,到该列的最后一个字元
d0 那个是数字的0 ,删除游标所在处,到该列的最前面一个字元
yy 复制游标所在的那一列(常用)
nyy n为数字。复制游标所在的向下n列,例如20yy则是复制20列(常用)
y1G 复制游标所在列到第一列的所有资料
yG 复制游标所在列到最后一列的所有资料
y0 复制游标所在的那个字元到该列行首的所有资料
y$ 复制游标所在的那个字元到该列行尾的所有资料
p, P p为将已复制的资料在游标下一列贴上,P则为贴在游标上一列!举例来说,我目前游标在第20列,且已经复制了10列资料。
则按下p后,那10列资料会贴在原本的20列之后,亦即由21列开始贴。
但如果是按下P呢?那么原本的第20列会被推到变成30列。(常用)
J 将游标所在列与下一列的资料结合成同一列
c 重复删除多个资料,例如向下删除10 列,[ 10cj ]
u 复原前一个动作。(常用)
[Ctrl]+r 重做上一个动作。(常用)
这个u 与[Ctrl]+r 是很常用的指令!一个是复原,另一个则是重做一次~ 利用这两个功能按键,你的编辑,嘿嘿!很快乐的啦!

. 不要怀疑!这就是小数点!意思是重复前一个动作的意思。
如果你想要重复删除、重复贴上等等动作,按下小数点『.』就好了!(常用)

i, I 进入插入模式(Insert mode):
i为『从目前游标所在处插入』, I为『在目前所在列的第一个非空白字元处开始插入』。(常用)
a, A 进入插入模式(Insert mode):
a为『从目前游标所在的下一个字元处开始插入』, A为『从游标所在列的最后一个字元处开始插入』。(常用)
o, O 进入插入模式(Insert mode):
这是英文字母o的大小写。o为『在目前游标所在的下一列处插入新的一列』; O为在目前游标所在处的上一列插入新的一列!(常用)
r, R 进入取代模式(Replace mode):
r只会取代游标所在的那一个字元一次;R会一直取代游标所在的文字,直到按下ESC为止;(常用)
上面这些按键中,在vi 画面的左下角处会出现『--INSERT--』或『--REPLACE--』的字样。由名称就知道该动作了吧!!
特别注意的是,我们上面也提过了,你想要在档案里面输入字元时, 一定要在左下角处看到INSERT 或REPLACE 才能输入喔!

[Esc] 退出编辑模式,回到一般指令模式中(常用)

:w 将编辑的资料写入硬碟档案中(常用)
:w! 若档案属性为『唯读』时,强制写入该档案。不过,到底能不能写入, 还是跟你对该档案的档案权限有关啊!
:q 离开vi (常用)
:q! 若曾修改过档案,又不想储存,使用! 为强制离开不储存档案。
注意一下啊,那个惊叹号(!) 在vi 当中,常常具有『强制』的意思~
:wq 储存后离开,若为:wq!则为强制储存后离开(常用)
ZZ 这是大写的Z 喔!若档案没有更动,则不储存离开,若档案已经被更动过,则储存后离开!
:w [filename] 将编辑的资料储存成另一个档案(类似另存新档)
:r [filename] 在编辑的资料中,读入另一个档案的资料。亦即将『filename』 这个档案内容加到游标所在列后面
:n1,n2 w [filename] 将n1 到n2 的内容储存成filename 这个档案。
:! command 暂时离开vi到指令列模式下执行command的显示结果!例如
『:! ls /home』即可在vi当中察看/home底下以ls输出的档案资讯!

:set nu 显示行号,设定之后,会在每一列的字首显示该列的行号
:set nonu 与set nu 相反,为取消行号!
:set autoindent " 自动缩排(回车后,会自动与上一行对齐)

可写入配置文件,永久生效
[root@localhost ~]# cat ~/.vimrc
set nu " 显示行号
set autoindent " 自动缩排(回车后,会自动与上一行对齐)

" 注释 这个文件的注释符合是英文双引号 ",而不是井号 #
[root@localhost ~]#

vim 的暂存档、救援回复与开启时的警告讯息

正在 vim 页面,退出 bash 时,当前编辑的目录会出现 .xxx.swp

可以查看 ls -la . 查看文件,通常删除文件后,再进行编辑。

区块选择快捷键(Visual Block)

1
2
3
4
5
6
7
8
9
10
11
v   字元选择,会将游标经过的地方反白选择!
V 列选择,会将游标经过的行反白选择!(相同于 shift + v)
[Ctrl]+v 区块选择,可以用长方形的方式选择资料

y 将反白的地方复制起来
d 将反白的地方删除掉
p 将刚刚复制的区块,在游标所在处贴上!


p(小写):粘贴在后面
P(大写):粘贴在前面

多档案编辑

进入多档案:vim file1 file2

1
2
3
:n  编辑下一个档案
:N 编辑上一个档案
:files 列出目前这个vim 的开启的所有档案

多视窗功能

1
2
3
4
5
:sp [filename]  开启一个新视窗,如果有加filename, 表示在新视窗开启一个新档案,否则表示两个视窗为同一个档案内容(同步显示)。
[ctrl]+w+ j 或 [ctrl]+w+↓ 按键的按法是:先按下[ctrl] + w 松开, 然后再按下j (或向下方向键),则游标可移动到下方的视窗。
[ctrl]+w+ k 或 [ctrl]+w+↑ 同上,不过游标移动到上面的视窗。

关闭鼠标所在窗口:ZZ,:q! :w 同保存哪些命令一样。

vim 的补全功能

1
2
3
4
5
6
7
[ctrl]+x -> [ctrl]+n    透过目前正在编辑的这个『档案的内容文字』作为关键字,予以补齐
例如当前档案已存在内容有 taopanfeng,此时输入 tao,再按下 Ctrl+x 松开,再按下 Ctrl+n 即可补全为 taopanfeng、
如果当前没有 tao 开头的单次,即使按了也不会补全。
[ctrl]+x -> [ctrl]+f 以当前目录内的『档名』作为关键字,予以补齐
把当前目录的所有文件名作为【关键字】进行补全
[ctrl]+x -> [ctrl]+o 以副档名作为语法补充,以vim 内建的关键字,予以补齐
用当前文件的 xxx.java,或 xxx.css xxx.html 作为语法,提示对应关键字。只有部分后缀名可以提示

~/.vimrc, ~/.viminfo:vim 环境设定与记录

我们进入文件,输入搜索命令 :/xxx,此时 xxx 都进行高亮。
即使我们退出,或进入了其他文件后,再次进入该文件,依然有搜索的高亮提示。
这就是因为文件 ~/.viminfo 文件的作用。它记录了我们的 vim 操作。

关于 ~/.vimrc,是我们可以进行自定义的 vim 设置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
:set nu
:set nonu 就是设定与取消行号啊!

:set hlsearch
:set nohlsearch hlsearch 就是high light search(高亮度搜寻)。这个就是设定是否将搜寻的字串反白的设定值。预设值是hlsearch

:set autoindent
:set noautoindent 是否自动缩排?autoindent 就是自动缩排。

:set backup 是否自动储存备份档?一般是nobackup 的, 如果设定backup 的话,那么当你更动任何一个档案时,则原始档案会被另存成一个档名为filename~ 的档案。举例来说,我们编辑hosts ,设定:set backup ,那么当更动hosts 时,在同目录下,就会产生hosts~ 档名的档案,记录原始的hosts 档案内容
:set ruler 还记得我们提到的右下角的一些状态列说明吗?这个ruler 就是在显示或不显示该设定值的啦!
:set showmode 这个则是,是否要显示--INSERT-- 之类的字眼在左下角的状态列。
:set backspace=(012) 一般来说, 如果我们按下i 进入编辑模式后,可以利用倒退键(backspace) 来删除任意字元的。但是,某些distribution 则不许如此。此时,我们就可以透过backspace 来设定啰~ 当backspace 为2 时,就是可以删除任意值;0 或1 时,仅可删除刚刚输入的字元, 而无法删除原本就已经存在的文字了!
:set all 显示目前所有的环境参数设定值。
:set 显示与系统预设值不同的设定参数, 一般来说就是你有自行变动过的设定参数啦!

:syntax on
:syntax off 是否依据程式相关语法显示不同颜色?举例来说,在编辑一个纯文字档时,如果开头是以# 开始,那么该列就会变成蓝色。如果你懂得写程式,那么这个:syntax on 还会主动的帮你除错呢!但是, 如果你仅是编写纯文字档案,要避免颜色对你的萤幕产生的干扰,则可以取消这个设定。

:set bg=dark
:set bg=light 可用以显示不同的颜色色调,预设是『 light 』。如果你常常发现注解的字体深蓝色实在很不容易看, 那么这里可以设定为dark 喔!试看看,会有不同的样式呢!

在这个档案中,使用『 set hlsearch 』或『 :set hlsearch 』,亦即最前面有没有冒号『 : 』效果都是一样的!
至于双引号则是注解符号!不要用错注解符号,否则每次使用vim 时都会发生警告讯息喔!
建立好这个档案后,当你下次重新以vim 编辑某个档案时,该档案的预设环境设定就是上头写的啰~
这样,是否很方便你的操作啊!多多利用vim 的环境设定功能呢!^_^

vim 指令总结图

vim 常用指令示意图

dos2unix:DOS 与Linux 的断行字元

因为Windows换行符 CRLF 是M$结尾,而Linux是LF $结尾
可以使用cat -A 脚本文件来查看Shell隐藏内容

1
2
3
4
5
[dmtsai@study ~]$ dos2unix [-kn] file [newfile] 
[dmtsai@study ~]$ unix2dos [-kn] file [newfile]
选项与参数:
-k :保留该档案原本的mtime 时间格式(不更新档案上次内容经过修订的时间)
-n :保留原本的旧档,将转换后的内容输出到新档案,如: dos2unix -n old new

iconv:语系编码转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

[dmtsai@study ~]$ iconv --list ===> 查看所有格式
[dmtsai@study ~]$ iconv -f 原本编码 -t 新编码 filename [-o newfile]
选项与参数:
--list :列出iconv 支援的语系资料
-f :from ,亦即来源之意,后接原本的编码格式;
-t :to ,亦即后来的新编码要是什么格式;
-o file:如果要保留原本的档案,那么使用-o 新档名,可以建立新编码档案。





1. 下载 big5 格式文件
[root@localhost a]# wget http://linux.vbird.org/linux_basic/0310vi/vi.big5
--2020-11-11 02:40:33-- http://linux.vbird.org/linux_basic/0310vi/vi.big5
正在解析主机 linux.vbird.org (linux.vbird.org)... 140.116.44.180
正在连接 linux.vbird.org (linux.vbird.org)|140.116.44.180|:80... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度:1498 (1.5K) [text/plain]
正在保存至: “vi.big5”

100%[====================================================================>] 1,498 --.-K/s 用时 0s

2020-11-11 02:40:39 (247 MB/s) - 已保存 “vi.big5” [1498/1498])
[root@localhost a]# ll
总用量 4
-rw-r--r--. 1 root root 1498 1月 13 2009 vi.big5

2. big5 -> utf8
[root@localhost a]# iconv -f big5 -t utf8 vi.big5 -o vi.utf8

3. utf8 繁体 -> big5 -> gb2312 国标 -> utf8 简体
[root@localhost a]# iconv -f utf8 -t big5 vi.utf8 | iconv -f big5 -t gb2312 | iconv -f gb2312 -t utf8 -o vi.gb.utf8


4. 查看三个文件内容
[root@localhost a]# head -n 3 vi.big5
¨C­Өt²κ޲z­󤀳¸Ӧܤ֭n¾Ƿ|¤@ºؤ奲¤¶­±ªº¤寑³B²z¾¹¡A¥H¤誋¨t²Τ鰠ªºº޲z¦欰¡C
¦b Linux ¤W󿿙ªº¤寑³B²z³nƩ«D±`ªº¦h¡A¤£¹L¡A³¾­񂙬O«ل³¨ϥ̠vi ³o­ӥ¿³Wªº¤寑³B²z¾¹¡C
³o¬O¦]¬° vi ´X¥G¦b¥𧳤@­ѠUnix Like ªº¾󾹳£¦s¦b¡A¾Ƿ|¥L¡A»´«ܦh°ڡI
[root@localhost a]#
[root@localhost a]#
[root@localhost a]# head -n 3 vi.utf8
每個系統管理員都應該至少要學會一種文字介面的文書處理器,以方便系統日常的管理行為。
在 Linux 上頭的文書處理軟體非常的多,不過,鳥哥還是建議使用 vi 這個正規的文書處理器。
這是因為 vi 幾乎在任何一個 Unix Like 的機器都存在,學會他,輕鬆很多啊!
[root@localhost a]#
[root@localhost a]#
[root@localhost a]#
[root@localhost a]# head -n 3 vi.gb.utf8
每个系统管理员都应该至少要学会一种文字介面的文书处理器,以方便系统日常的管理行为。
在 Linux 上头的文书处理软体非常的多,不过,鸟哥还是建议使用 vi 这个正规的文书处理器。
这是因为 vi 几乎在任何一个 Unix Like 的机器都存在,学会他,轻松很多啊!
[root@localhost a]#

bash,shell 区别?

首先,问这个问题,就表示你不是很理解。
你 -> Shell,或 APP -> Kernel -> 硬件
“shell”就是一个能访问内核的壳子。这个壳子也是一个程序(APP),这个程序的名称叫“bash”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Linux 有哪些Shell,是在 /etc/shells 文件中写入。

/bin/sh (已经被/bin/bash 所取代)
/bin/bash (就是Linux 预设的shell)
/bin/tcsh (整合C Shell ,提供更多的功能)
/bin/csh (已经被/bin/tcsh 所取代)

[root@localhost test]# cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin

bash 的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14

1、history 历史命令
每次退出会把当前的最后 $HISTFILESIZE 条记录,写在 ~/.bash_history 中

2、命令/档案补全功能 tab

3、命令别名 alias

4、工作控制,前后台运行控制
Ctrl + C 结束命令

5、脚本编写(shell script)

6、通配符 * ? [] ^

type:查询指令是否为Bash shell 的内建命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

[dmtsai@study ~]$ type [-tpa] name
选项与参数:
:不加任何选项与参数时,type 会显示出name 是外部指令还是bash 内建指令
-t :当加入-t 参数时,type 会将name 以底下这些字眼显示出他的意义:
file :表示为外部指令;
alias :表示该指令为命令别名所设定的名称;
builtin :表示该指令为bash 内建的指令功能;
-p :如果后面接的name 为外部指令时,才会显示完整档名;
-a :会由PATH 变数定义的路径中,将所有含name 的指令都列出来,包含alias


-------------------------------------type ls
ls 是 `ls --color=auto' 的别名
-------------------------------------
-------------------------------------type cd
cd 是 shell 内嵌
-------------------------------------
-------------------------------------type java
java 是 /usr/bin/java
-------------------------------------

bash 快捷键

1
2
3
4
5
6
7
8
9
10
11
12
13
\ 表示跳脱
Ctrl + U 删除执行以前
Ctrl + K 删除指针以后
Ctrl + A 指针移动到命令最前面
Ctrl + E 指针移动到命令最后面
Ctrl + C 终止当前命令
Ctrl + D 输入结束(EOF)
Ctrl + M Enter
Ctrl + S 暂停屏幕输出
Ctrl + Q 恢复屏幕输出
Ctrl + Z 『暂停』目前的命令

shift + PageUp/PageDown:命令行上下翻页

set,env,export,unset:变量操作

$$:当前 bash PID
$?:上一条命令执行结果。0 表示正常执行,非 0 表示错误代码
$!:后台运行的最后一个进程的PID进程号(Linux要运行很多进程,一个进程对应一个PID号)

1
2
3
4
5
[root@localhost test]# echo $$
4235
[root@localhost test]# echo $?
0
[root@localhost test]# echo $!

PS1:表示 bash 的左侧提示 -> [root@localhost test]#
PATH:环境变量,冒号:分割。执行命令时会按顺序搜索,搜索到就不往下搜索了,搜索不到就报 -bash:xxx command not found

$变量名 或 ${变量名}:表示变量的使用。例如(变量追加):PATH=”$PATH”:/app/test/diy 或 PATH=${PATH}:/app/test/diy

$(命令) 或 命令:表示命令的引用。例如:cd /lib/modules/$(uname -r)/kernel ===> 先执行 uname,再执行 cd

‘:一切符号失去含义,当做字符串。例如:a=阿 ; b=’$a’B ; echo $b 输出 $aB
“:一切符号可以当做特殊含义使用。例如:a=阿 ; b=”$a”B ; echo $b 输出 阿B

变量名=变量内容:声明变量
export 变量名:声明环境变量
set:当前 bash 变量(子 bash 不可用)
env:环境变量(子 bash 可用,不包含 declare 声明类型)
export:环境变量(子 bash 可用,包含 declare 声明类型)
unset 变量名:取消变量(可取消环境变量,或自变量)

数值运算

1
2
3
4
5
6
7
8
9
10
11
#===> 方式1:用declare -i声明数值类型
declare -i c=$a+$b

#===> 方式2:调用expr数值运算工具,运行符号两边需要空格
c=$(expr $aa + $bb)

#===> 方式3(常用)
c=$(($a+$b))

#===> 方式4
c=$[$a+$b]

运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
优先级值越大越优先


优先级 运算符 说明
13 -,+ 单目负,单目正
12 !,~ 逻辑非,按位取反或补码
11 *,/,% 乘,除,取模
10 +,- 加,减
9 <<,>> 按位左移,按位右移
8 <=,>=,<,> 小于或等于,大于或等于,小于,大于
7 ==,!= 等于,不等于
6 & 按位与
5 ^ 按位异或
4 | 按位或
3 && 逻辑与
2 | 逻辑或
1 =,+=,-=,*=,/=,%=,&=,^=,|=,<<=,>>= 赋值,运算且赋值

PS1:bash 前缀提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@localhost test]# set | grep PS1
PS1='[\u@\h \W]\$ '


\d :可显示出『星期月日』的日期格式,如:"Mon Feb 2"
\H :完整的主机名称。举例来说,鸟哥的练习机为『study.centos.vbird』
\h :仅取主机名称在第一个小数点之前的名字,如鸟哥主机则为『study』后面省略
\t :显示时间,为24 小时格式的『HH:MM:SS』
\T :显示时间,为12 小时格式的『HH:MM:SS』
\A :显示时间,为24 小时格式的『HH:MM』
\@ :显示时间,为12 小时格式的『am/pm』样式
\u :目前使用者的帐号名称,如『dmtsai』;
\v :BASH 的版本资讯,如鸟哥的测试主机版本为4.2.46(1)-release,仅取『4.2』显示
\w :完整的工作目录名称,由根目录写起的目录名称。但家目录会以~ 取代;
\W :利用basename 函数取得工作目录名称,所以仅会列出最后一个目录名。
\# :下达的第几个指令。
\$ :提示字元,如果是root 时,提示字元为# ,否则就是$ 啰~

read:读取屏幕输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@localhost test]# chmod 777 read.sh
[root@localhost test]# cat read.sh
#!/bin/bash

# -t 等待输入时间(秒),-p提示信息,输入的内容会被保存为变量 username
read -t 30 -p "请输入用户名: " username
echo $username

# -s 表示输入内容不可见
read -s -t 30 -p "请输入密码: " password
echo $password

# -n执行输入字符数,这里指定 1 个,输入一个 字符之后就自动结束
read -n 1 -t 30 -p "确认登录吗?[Y/N]: " confirm

echo -e "\n"
[root@localhost test]#
[root@localhost test]# ./read.sh
请输入用户名: tao pan feng
tao pan feng
请输入密码: 12345667890
确认登录吗?[Y/N]: y

[root@localhost test]#

declare:声明变量类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
1. 默认字符串
[root@localhost test]# aa=1
[root@localhost test]# bb=2
[root@localhost test]# cc=$aa+$bb
[root@localhost test]# echo $cc
1+2


2. -i 声明为整形
[root@localhost test]#
[root@localhost test]# declare -i cc
[root@localhost test]# cc=$aa+$bb
[root@localhost test]# echo $cc
3
[root@localhost test]#


3. -p 查看声明类型
[root@localhost ~]# declare -p cc
declare -i cc="3"


4. -x 设置为环境变量
[root@localhost ~]# declare -x cc
[root@localhost ~]# export | grep cc
declare -ix cc="3"
[root@localhost ~]#


5. -r 设置为只读(readOnly)变量,不可改变,删除
[root@localhost ~]# declare -r cc
[root@localhost ~]# declare -p cc
declare -irx cc="3"
[root@localhost ~]#
[root@localhost ~]# cc=123
-bash: cc: 只读变量


6. +x 取消环境变量(-代表赋值,+代表取消)
[root@localhost ~]# declare +x cc
[root@localhost ~]# declare -p cc
declare -ir cc="3"


7. 只读变量不可改变、删除,只有退出重新登入才可
[root@localhost ~]# unset cc
-bash: unset: cc: 无法反设定: 只读 variable
[root@localhost ~]#
[root@localhost ~]# exit
登出

array:数组变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
声明:变量名[index]=content
使用:${变量名[index]} ===> 用 大括号 {} 括起来


[root@localhost test]# qwerty[100]=xxx ===> 声明
[root@localhost test]#
[root@localhost test]# echo ${qwerty[99]} ===> 正确,但下标不存在

[root@localhost test]# echo ${qwerty[100]} ===> 正确
xxx
[root@localhost test]#
[root@localhost test]# echo $qwert[100] ===> 错误
[100]
[root@localhost test]# echo $"qwert[100]" ===> 错误
qwert[100]
[root@localhost test]# echo "$qwert[100]" ===> 错误
[100]

# ## % %% / //:变数内容的删除与取代

1
2
3
4
5
6
7
8
${变数#关键字}   若变数内容从头开始的资料符合『关键字』,则将符合的最短资料删除
${变数##关键字} 若变数内容从头开始的资料符合『关键字』,则将符合的最长资料删除

${变数%关键字} 若变数内容从尾向前的资料符合『关键字』,则将符合的最短资料删除
${变数%%关键字} 若变数内容从尾向前的资料符合『关键字』,则将符合的最长资料删除

${变数/旧字串/新字串} 若变数内容符合『旧字串』则『第一个旧字串会被新字串取代』
${变数//旧字串/新字串} 若变数内容符合『旧字串』则『全部的旧字串会被新字串取代』
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

[root@localhost test]# tpf=taopanfeng-taopanfeng ; echo $tpf
taopanfeng-taopanfeng

1. 删除最左边,最短匹配串
[root@localhost test]# echo ${tpf#*pan}
feng-taopanfeng

2. 删除最左边,最长匹配串
[root@localhost test]# echo ${tpf##*pan}
feng

3. 删除最右边,最短匹配串
[root@localhost test]# echo ${tpf%pan*}
taopanfeng-tao

4. 删除最右边,最长匹配串
[root@localhost test]# echo ${tpf%%pan*}
tao

5. 替换左边第一个
[root@localhost test]# echo ${tpf/tao/TAO}
TAOpanfeng-taopanfeng

6. 替换全部
[root@localhost test]# echo ${tpf//tao/TAO}
TAOpanfeng-TAOpanfeng

- + = ? :变数的测试与内容替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
变数设定方式        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

alias:别名

1
2
3
4
5
6
7
alias 别名='原命令'

1. 定义别名
alias tpf='ls -l'

2. 删除别名
unalias tpf

history:历史命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[dmtsai@study ~]$ history [n] 
[dmtsai@study ~]$ history [-c]
[dmtsai@study ~]$ history [-raw] histfiles
选项与参数:
n :数字,意思是『要列出最近的n 笔命令列表』的意思!
-c :将目前的shell 中的所有history 内容全部消除
-a :将目前新增的history 指令新增入histfiles 中,若没有加histfiles ,
则预设写入~/.bash_history
-r :将histfiles 的内容读到目前这个shell 的history 记忆中;
-w :将目前的history 记忆内容写入histfiles 中!


范例一:列出目前记忆体内的所有history记忆
[dmtsai@study ~]$ history
#前面省略
1017 man bash
1018 ll
1019 history
1020 history
# 列出的资讯当中,共分两栏,第一栏为该指令在这个shell 当中的代码,
# 另一个则是指令本身的内容喔!至于会秀出几笔指令记录,则与HISTSIZE 有关!

范例二:列出目前最近的3笔资料
[dmtsai@study ~]$ history 3
1019 history
1020 history
1021 history 3

范例三:立刻将目前的资料写入histfile当中
[dmtsai@study ~]$ history -w
#在预设的情况下,会将历史纪录写入~/.bash_history当中!
[dmtsai@study ~]$ echo ${HISTSIZE}
1000

、当我们以bash 登入Linux 主机之后,系统会主动的由家目录的/.bash_history 读取以前曾经下过的指令,那么/.bash_history 会记录几笔资料呢?这就与你bash 的 HISTFILESIZE 这个变数设定值有关了!

、假设我这次登入主机后,共下达过100次指令,『等我登出时,系统就会将1011100这总共1000笔历史命令更新到/.bash_history当中。』也就是说,历史命令在我登出时,会将最近的HISTFILESIZE笔记录到我的纪录档当中啦!

、当然,也可以用history -w 强制立刻写入的!那为何用『更新』两个字呢?因为~/.bash_history 记录的笔数永远都是HISTFILESIZE 那么多,旧的讯息会被主动的拿掉!仅保留最新的!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[dmtsai@study ~]$ !number 
[dmtsai@study ~]$ !command
[dmtsai@study ~]$ !!
选项与参数:
number :执行第几笔指令的意思;
command :由最近的指令向前搜寻『指令串开头为command』的那个指令,并执行;
!! :就是执行上一个指令(相当于按↑按键后,按Enter)

[dmtsai@study ~]$ history
66 man rm
67 alias
68 man history
69 history
[dmtsai@study ~]$ !66 <==执行第66笔指令
[dmtsai@study ~]$ !! <==执行上一个指令,本例中亦即!66
[dmtsai@study ~]$ !al <==执行最近以al为开头的指令(上头列出的第67个)

我们知道,只有退出 bash 界面,才会把命令写入 ~/.bash_history
一般我们会开启多个 bash 页面,关闭时,会按照顺序一个一个写入

路径与指令搜寻顺序

如果一个指令(例如ls)被下达时,到底是哪一个ls被拿来运作?很有趣吧!
基本上,指令运作的顺序可以这样看:

1
2
3
4
1、以相对/绝对路径执行指令,例如『 /bin/ls 』或『 ./ls 』;
2、由alias 找到该指令来执行;
3、由bash 内建的(builtin) 指令来执行;
4、透过$PATH 这个变数的顺序搜寻到的第一个指令来执行。

举例来说,你可以下达/bin/ls 及单纯的ls 看看,会发现使用ls 有颜色但是/bin/ls 则没有颜色。
因为/bin/ls 是直接取用该指令来下达,而ls 会因为『 alias ls=’ls –color=auto’ 』这个命令别名而先使用!
如果想要了解指令搜寻的顺序,其实透过type -a ls 也可以查询的到啦!

1
2
3
4
5
6
7
8

[dmtsai@study ~]$ alias echo='echo -n'
[dmtsai@study ~]$ type -a echo
echo is aliased to `echo -n'
echo is a shell builtin
echo is /usr/bin/echo

# 瞧!很清楚吧!先alias 再builtin 再由$PATH 找到/bin/echo 啰!

/etc/issue, /etc/motd: bash 的进站与欢迎讯息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/etc/issue      虚拟机开机提示
/etc/issue.net telnet 登录提示
/etc/motd Xshell 登录提示


issue 内的各代码意义
\d 本地端时间的日期;
\l 显示第几个终端机介面;
\m 显示硬体的等级(i386/i486/i586/i686...);
\n 显示主机的网路名称;
\O 显示domain name;
\r 作业系统的版本(相当于uname -r)
\t 显示本地端时间的时间;
\S 作业系统的名称;
\v 作业系统的版本。

login shell

bash 在读完了整体环境设定的/etc/profile 并借此呼叫其他设定档后,接下来则是会读取使用者的个人设定档。
在login shell 的bash 环境中,所读取的个人偏好设定档其实主要有三个,依序分别是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/etc/profile    系统整体的设定,不要动
-> /etc/profile.d/*.sh #===> 建立一个 diy.sh
-> /etc/locale.conf 设置语言 LANG

[root@localhost ~]# cat > /etc/profile.d/diy.sh << "eof"
> alias k='kill -9 '
> alias pgrep='ps aux | grep '
> eof



1、~/.bash_profile 个人设定
-> ~/.bashrc
-> /etc/bashrc
2、~/.bash_login
3、~/.profile

按照顺序读取,读取其中一个其他就不会读取了。

source :读入环境设定档的指令

1
2
3
4
5
6
7
立即生效:source 或 小数点 .

[dmtsai@study ~]$ source设定档档名

范例:将家目录的~/.bashrc的设定读入目前的bash环境中
[dmtsai@study ~]$ source ~/.bashrc <==底下这两个指令是一样的!
[dmtsai@study ~]$ . ~/.bashrc

non-login shell

1
2
3
4
5
6
~/.bashrc       根据 UID 规定了 umask,PS1,并读取 /etc/profile.d/*.sh 的设定
-> 如果删了,命令行会变成 `-bash-4.2$ `,想恢复可以复制/etc/skel/.bashrc 到你的家目录。
-> 并使用source 去呼叫~/.bashrc ,那你的命令提示字元就会回来啦!
~/.bash_history 命令记录,默认 1000 条。与 HISTFILESIZE 变量有关。

~/.bash_logout 退出 bash 要做的事情,默认什么也不做

* ?等:万用字元与特殊符号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
*   代表『 0 个到无穷多个』任意字元
? 代表『一定有一个』任意字元
[] 同样代表『一定有一个在括号内』的字元(非任意字元)。例如[abcd] 代表『一定有一个字元, 可能是a, b, c, d 这四个任何一个』
[-] 若有减号在中括号内时,代表『在编码顺序内的所有字元』。例如[0-9] 代表 0 到9 之间的所有数字,因为数字的语系编码是连续的!
[^] 若中括号内的第一个字元为指数符号(^) ,那表示『反向选择』,例如[^abc] 代表一定有一个字元,只要是非a, b, c 的其他字元就接受的意思。


# 注解符号:这个最常被使用在script 当中,视为说明!在后的资料均不执行
\ 跳脱符号:将『特殊字元或万用字元』还原成一般字元
| 管线(pipe):分隔两个管线命令的界定(后两节介绍);
; 连续指令下达分隔符号:连续性命令的界定(注意!与管线命令并不相同)
~ 使用者的家目录
$ 取用变数前置字元:亦即是变数之前需要加的变数取代值
& 工作控制(job control):将指令变成后台运行
! 逻辑运算意义上的『非』 not 的意思!
/ 目录符号:路径分隔的符号
>, >> 资料流重导向:输出导向,分别是『取代』与『累加』
<, << 资料流重导向:输入导向(这两个留待下节介绍)
' ' 单引号,不具有变数置换的功能($ 变为纯文字)
" " 具有变数置换的功能!($ 可保留相关功能)
` ` 两个『 ` 』中间为可以先执行的指令,相当于使用$( )
( ) 在中间为子shell 的起始与结束
{ } 在中间为命令区块的组合!

< << > >> 2> 2>>:箭头资料流重导向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
标准输入(stdin) :代码为0 ,使用< 或<< ;
标准输出(stdout):代码为1 ,使用> 或>> ;
标准错误输出(stderr):代码为2 ,使用2> 或2>> ;


# 标准输出重定向
`命令 > 文件` 覆盖。命令正确则会覆盖,命令错误则清空内容
`命令 >> 文件` 追加。正确会追加,错误则输出执行结果,原文件不动

# 标准错误输出重定向
`错误命令 2> 文件` 覆盖。命令错误则会覆盖,命令正确则直接输出屏幕并清空文件内容
`错误命令 2>> 文件` 追加。错误会追加,正确则输出执行结果,原文件不动

# 正确输出和错误输出同时保存
`命令 > 文件 2>&1` 或 `命令 &> 文件` 覆盖。不论正确还是错误
`命令 >> 文件 2>&1` 或 `命令 &>> 文件` 追加。不论正确还是错误
`命令 >> 文件1 2>> 文件2` 正确追加到文件1,错误追加到文件2
1
2
3
4
5
6
7
8
9
cat 配合 资料重导向,eof 使用
[dmtsai@study ~]$ cat > catfile << "eof"
> This is a test.
> OK now stop
> eof <==输入这关键字,立刻就结束而不需要输入[ctrl]+d

[dmtsai@study ~]$ cat catfile
This is a test.
OK now stop <==只有这两行,不会存在关键字那一行!

为什么需要重导向?
、萤幕输出的资讯很重要,而且我们需要将他存下来的时候;
、背景执行中的程式,不希望他干扰萤幕正常的输出结果时;
、一些系统的例行命令(例如写在/etc/crontab 中的档案) 的执行结果,希望他可以存下来时;
、一些执行命令的可能已知错误讯息时,想以『 2> /dev/null 』将他丢掉时;
、错误讯息与正确讯息需要分别输出时。

》》》关于“<< eof”,再做解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
你可能不太理解。<< 是输入重定向。 
你可以把 EOF 换成其他的字符串,
例如:
"""
cat > a << ppp
我是a
ppp
"""
这个就会创建 a 文件,里面的内容就一行"我是a"

EOF 翻译是 : 文件结束符(end of file) 通常 << 以 EOF 来表示,或者 有的人使用小写 eof,
其实这个 字符串 EOF 可以是其他的字符串,但要成对出现,


* cmd << text
从命令行读取输入,直到一个与text相同的行结束。
除非使用引号把输入括起来,才能进行shell变量替换。

/dev/null:文件黑洞

不管错误,正确。写入的都不能读取。

1
2
3
4
5
6
[root@localhost test]# ll /dev/null
crw-rw-rw-. 1 root root 1, 3 11月 16 22:26 /dev/null
[root@localhost test]#
[root@localhost test]# echo abc > /dev/null
[root@localhost test]# cat /dev/null
[root@localhost test]#

; , &&, ||:命令执行的判断依据

1
2
3
4
5
6
7
8
命令1 ; 命令2   顺序执行
命令1 && 命令2 顺序执行。1执行 $?=0,2才执行。
命令1 || 命令2 顺序执行。1执行 $? 不为 0,2才执行。


例如:command1 && command2 || command3

command1 执行 $?=0,开始执行 command2,command2执行 $? 不为 0,才会执行 command3

| 管线命令(pipe)

1
2
3
4
5
6
   STDOUT->STDIN STDOUT->STDIN
command1 | command2 | command3

command1 的标志输出,通过标准输入给 command2,
command2 的标志输出,通过标准输入给 command3,
command3 的标志输出至屏幕

注意点:
、管线命令仅会处理standard output,对于standard error output 会予以忽略
、管线命令必须要能够接受来自前一个指令的资料成为standard input 继续处理才行。

cut:列分割

按照列进行分割

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
[dmtsai@study ~]$ cut -d '分隔字元' -f fields  <==用于有特定分隔字元
[dmtsai@study ~]$ cut -c 字元区间 <==用于排列整齐的讯息
选项与参数:
-d :后面接分隔字元。与-f 一起使用;
-f :依据-d 的分隔字元将一段讯息分割成为数段,用-f 取出第几段的意思;
-c :以字元(characters) 的单位取出固定字元区间;


# 案例1,-d,-f 使用
[root@localhost test]# echo ${PATH}
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
[root@localhost test]# echo ${PATH} | cut -d ':' -f 1 ===> 取第1列
/usr/local/sbin
[root@localhost test]# echo ${PATH} | cut -d ':' -f 1,3 ===> 取第1列,和 第3列
/usr/local/sbin:/usr/sbin
[root@localhost test]# echo ${PATH} | cut -d ':' -f 1-3 ===> 取第1列,到 第3列
/usr/local/sbin:/usr/local/bin:/usr/sbin
[root@localhost test]# echo ${PATH} | cut -d ':' -f 3- ===> 取第3列,到 最后1列
/usr/sbin:/usr/bin:/root/bin

# 案例2,-c 使用
[root@localhost test]# export | head -n 3
declare -x HISTCONTROL="ignoredups"
declare -x HISTSIZE="1000"
declare -x HOME="/root"
[root@localhost test]# export | head -n 3 | cut -c 3 ===> 取第3个字符
c
c
c
[root@localhost test]# export | head -n 3 | cut -c 12- ===> 取第12个字符,到 最后1个字符
HISTCONTROL="ignoredups"
HISTSIZE="1000"
HOME="/root"
[root@localhost test]#

# 案例3,-d 使用
[root@localhost test]# last | head -n 3
root pts/0 192.168.1.8 Mon Nov 16 23:51 still logged in
root pts/0 192.168.1.8 Mon Nov 16 23:51 - 23:51 (00:00)
root pts/0 192.168.1.8 Mon Nov 16 23:35 - 23:51 (00:15)
[root@localhost test]# last | head -n 3 | cut -d ' ' -f 1 ===> 按照空格分割,取第1列
root
root
root
[root@localhost test]# last | head -n 3 | cut -d ' ' -f 2 ===> 按照空格分割,取第2列。



[root@localhost test]# 第2列取不到,是因为root pts/0之间有很多空格。多空格可以使用 awk 处理。

grep:行搜索

1
2
3
4
5
6
7
8
9
10
11

[dmtsai@study ~]$ grep [-acinv] '搜寻字串' filename
选项与参数:
-a :将binary 档案以text 档案的方式搜寻资料
-c :显示匹配行数。 一共有多少行含有'搜寻字串'
-i :忽略大小写的不同,所以大小写视为相同
-n :顺便输出行号
-v :反向选择,亦即显示出没有'搜寻字串' 内容的那一行!
-m :显示多少行记录(只显示匹配的前 n行)
-A :after 显示匹配记过后几行
-B :before 显示匹配记过前几行

grep “表达式” 文件 参数

表达式:
1、or “陶攀峰|陶凯文” 无序
2、and “陶攀峰.*陶凯文” 注意顺序

文件:
查找多个文件,可以使用* 星号
例如(在当月的日志中找):grep 陶攀峰 /data/logs/2021-03*.log

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

[root@localhost test]# cat > catfile << "eof"
> 第 1 行
> 陶攀峰-陶攀峰
> 第 3 行
> 陶凯文-陶凯文
> 欧阳夏飞-欧阳夏飞
> eof

1. 搜索并高亮显示
[root@localhost test]# cat catfile | grep 陶
陶攀峰-陶攀峰
陶凯文-陶凯文

2. -c 统计含有搜索字串的行数
[root@localhost test]# cat catfile | grep -c 陶
2

3. -v 反选字串
[root@localhost test]# cat catfile | grep -v 陶
第 1 行
第 3 行
欧阳夏飞-欧阳夏飞
[root@localhost test]# cat catfile | grep -v ^陶攀 ===> 搜索不以'陶攀'开头的行
第 1 行
第 3 行
陶凯文-陶凯文
欧阳夏飞-欧阳夏飞


4. -n 搜索字串,并显示行号
[root@localhost test]# cat catfile | grep -n 陶
2:陶攀峰-陶攀峰
4:陶凯文-陶凯文
[root@localhost test]#


5. -i 忽略大小写
[root@localhost test]# cat > catfile << "eof"
> abc
> ABC
> Abc
> eof
[root@localhost test]#
[root@localhost test]#
[root@localhost test]#
[root@localhost test]# cat catfile | grep abc
abc
[root@localhost test]#
[root@localhost test]# cat catfile | grep -i abc
abc
ABC
Abc

6. 搜索多个字符串
[root@localhost ~]# cat -n /etc/passwd | grep "root\|tao" #===> 竖线 | 需要转义
1 root:x:0:0:root:/root:/bin/bash
10 operator:x:11:0:operator:/root:/sbin/nologin
22 taopanfeng:x:1000:1000::/home/taopanfeng:/bin/bash

[root@localhost ~]# cat -n /etc/passwd | egrep 'root|tao' #===> 若不想使用反斜线 \ 转义,可以使用 egrep 命令
1 root:x:0:0:root:/root:/bin/bash
10 operator:x:11:0:operator:/root:/sbin/nologin
22 taopanfeng:x:1000:1000::/home/taopanfeng:/bin/bash

grep:(进阶)显示前后n行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

[dmtsai@study ~]$ grep [-A] [-B] '搜寻字串' filename
选项与参数:
-A n:为after 的意思,除了匹配行,后续的n 行也显示;
-B n:为befer 的意思,除了匹配行,前面的n 行也显示;

[root@localhost test]# cat -n g | grep 陶 -A 1 -B 2
2 b
3 c
4 d陶攀峰
5 e
--
8 h
9 i
10 j陶凯文
11 k

sort:排序

默认 String 字符串 排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

[dmtsai@study ~]$ sort [-fbMnrtuk] [file or stdin]
选项与参数:
-f :忽略大小写的差异,例如A 与a 视为编码相同;
-b :忽略最前面的空白字元部分;
-M :以月份的名字来排序,例如JAN, DEC 等等的排序方法;
-n :使用『纯数字』进行排序(预设是以文字型态来排序的);
-r :反向排序;
-u :就是uniq ,相同的资料中,仅出现一行代表;
-t :分隔符号,预设是用[tab] 键来分隔;
-k :以那个区间(field) 来进行排序的意思

1. 默认 String 字符串 排序
cat /etc/passwd | sort

2. -t ':' 以冒号分割,-k 3 取第三列数据,-n 以数字进行正向排序(升序)
cat /etc/passwd | sort -nt ':' -k 3

3. -f 忽略大小写,-r 反向排序

uniq:去重

使用之前要排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

[dmtsai@study ~]$ uniq [-ic]
选项与参数:
-i :忽略大小写
-c :进行计数

[root@localhost test]# last | cut -d ' ' -f 1 | sort | uniq

reboot
root
wtmp
[root@localhost test]# last | cut -d ' ' -f 1 | sort | uniq -c
1
3 reboot
40 root
1 wtmp

# 从上面的结果可以发现reboot 有3 次, root 登入则有40 次!大部分是以root 来操作!
# wtmp 与第一行的空白都是last 的预设字元,那两个可以忽略的!

有时候,你想按照月份,来统计登录次数,uniq 这个命令就比较实用了。

wc:统计行/词/字节

wordcount

1
2
3
4
5
6
7
8
9
10
11
[root@localhost test]# wc /etc/passwd   ===> 统计多少行,多少单词,多少字节
21 43 1040 /etc/passwd

[root@localhost test]# wc -l /etc/passwd ===> 统计行数
21 /etc/passwd
[root@localhost test]# wc -w /etc/passwd ===> 统计单次数
43 /etc/passwd
[root@localhost test]# wc -m /etc/passwd ===> 统计字节数(byte)
1040 /etc/passwd
[root@localhost test]# ll /etc/passwd
-rw-r--r--. 1 root root 1040 11月 3 18:55 /etc/passwd

tee:双向重导向

1
2
3
4
5
6
7
8
9
10
11
12
13
STDIN -> tee -> Screen
|

file


[dmtsai@study ~]$ tee [-a] file
选项与参数:
-a :以累加(append) 的方式,将资料加入file 当中!

并把 ls -al /root/ 的内容,输入到 teefile 文件中
[root@localhost test]# ls -al /root/ | tee -a teefile | wc -l
13

》》》“tee” 代替 “cat >”

1
2
3
4
5
6
7
8
9
10
11
12
----------------------------cat > a << "eof"
> a
> eof
----------------------------tee b << "eof"
> b
> eof
b
----------------------------cat a
a
----------------------------cat b
b
----------------------------

tr:删除,替换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

[dmtsai@study ~]$ tr [-ds] str ...
选项与参数:
-d :删除上面命名当中的 str 这个字串;
-s :取代掉重复的字元!

# 案例1、将 小写 替换成 大写
[root@localhost test]# last | head -n 3
root pts/1 192.168.1.8 Tue Nov 17 02:05 still logged in
root pts/0 192.168.1.8 Mon Nov 16 23:51 still logged in
root pts/0 192.168.1.8 Mon Nov 16 23:51 - 23:51 (00:00)
[root@localhost test]# last | head -n 3 | tr '[a-z]' '[A-Z]'
ROOT PTS/1 192.168.1.8 TUE NOV 17 02:05 STILL LOGGED IN
ROOT PTS/0 192.168.1.8 MON NOV 16 23:51 STILL LOGGED IN
ROOT PTS/0 192.168.1.8 MON NOV 16 23:51 - 23:51 (00:00)


# 案例2、删除内容
[root@localhost test]# cat /etc/passwd | head -n 3
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@localhost test]# cat /etc/passwd | head -n 3 | tr -d ':'
rootx00root/root/bin/bash
binx11bin/bin/sbin/nologin
daemonx22daemon/sbin/sbin/nologin

# 案例3、实现 dos2unix 功能
[root@localhost test]# cp /etc/passwd . && unix2dos passwd
unix2dos: converting file passwd to DOS format ...
[root@localhost test]# file /etc/passwd passwd
/etc/passwd: ASCII text
passwd: ASCII text, with CRLF line terminators ===> CRLF Windows(\r\n),LF Linux(\n)
[root@localhost test]#
[root@localhost test]# cat passwd | tr -d '\r' > passwd.linux ===> 删除 \r 符号
[root@localhost test]#
[root@localhost test]# ll /etc/passwd passwd*
-rw-r--r--. 1 root root 1040 11月 3 18:55 /etc/passwd
-rw-r--r--. 1 root root 1061 11月 17 03:26 passwd
-rw-r--r--. 1 root root 1040 11月 17 03:27 passwd.linux
# 处理过后,发现 passwd.linux 档案大小与原本的 /etc/passwd 就一致了!

col:tab转空格

1
2
3
4
5
6
7
8
9
10

[dmtsai@study ~]$ col [-xb]
选项与参数:
-x :将tab 键转换成对等的空白键

范例一:利用cat -A显示出所有特殊按键,最后以col将[tab]转成空白
[dmtsai@study ~]$ cat -A /etc/man_db.conf <==此时会看到很多^I的符号,那就是tab
...
[dmtsai@study ~]$ cat /etc/man_db.conf | col -x | cat -A
#嘿嘿!如此一来, [tab]按键会被取代成为空白键,输出就美观多了!

join:多行合并

两个档案当中,有”相同资料” 的那一行,才将他加在一起

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
[dmtsai@study ~]$ join [-ti12] file1 file2
选项与参数:
-t :join 预设以空白字元分隔资料,并且比对『第一个栏位』的资料,
如果两个档案相同,则将两笔资料联成一行,且第一个栏位放在第一个!
-i :忽略大小写的差异;
-1 :这个是数字的1 ,代表『第一个档案要用那个栏位来分析』的意思;
-2 :代表『第二个档案要用那个栏位来分析』的意思。


# 案例一,用root的身份,将/etc/passwd与/etc/shadow相关资料整合成一栏
[root@localhost test]# head -n 3 /etc/passwd /etc/shadow
==> /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

==> /etc/shadow <==
root:$6$2YkSvs.5fQRTXxX7$5/boPraToML3eeZy7FxDe3vbwShbyIR...
bin:*:16659:0:99999:7:::
daemon:*:16659:0:99999:7:::
# 由输出的资料可以发现这两个档案的最左边栏位都是相同帐号!且以: 分隔
[root@localhost test]#
[root@localhost test]# join -t ':' /etc/passwd /etc/shadow | head -n 3
root:x:0:0:root:/root:/bin/bash:$6$2YkSvs.5fQRTXxX7$5/boPraToML3eeZy7FxDe3vbwShbyIR...
bin:x:1:1:bin:/bin:/sbin/nologin:*:16659:0:99999:7:::
daemon:x:2:2:daemon:/sbin:/sbin/nologin:*:16659:0:99999:7:::
#透过上面这个动作,我们可以将两个档案第一栏位相同者整合成一列!
# 第二个档案的相同栏位并不会显示(因为已经在最左边的栏位出现了啊!)

# 案例二,我们知道/etc/passwd 第四个栏位是GID ,那个GID 记录在
/etc/group当中的第三个栏位,请问如何将两个档案整合?
[root@localhost test]# head -n 3 /etc/passwd /etc/group
==> /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

==> /etc/group <==
root:x:0:
bin:x:1:
daemon:x:2:

[root@localhost test]# join -t ':' -1 4 /etc/passwd -2 3 /etc/group | head -n 3
join: /etc/passwd:6: is not sorted: sync:x:5:0:sync:/sbin:/bin/sync
join: /etc/group:11: is not sorted: wheel:x:10:
0:root:x:0:root:/root:/bin/bash:root:x:
1:bin:x:1:bin:/bin:/sbin/nologin:bin:x:
2:daemon:x:2:daemon:/sbin:/sbin/nologin:daemon:x:
#同样的,相同的栏位部分被移动到最前面了!所以第二个档案的内容就没再显示。
# 请读者们配合上述显示两个档案的实际内容来比对!

paste:多行合并

这个 paste 就要比 join 简单多了!
paste 就直接『将两行贴在一起,且中间以 [tab] 键隔开』而已!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

[dmtsai@study ~]$ paste [-d] file1 file2
选项与参数:
-d :后面可以接分隔字元。预设是以[tab] 来分隔的!
- :如果file 部分写成- ,表示来自standard input 的资料的意思。

# 用root身份,将/etc/passwd与/etc/shadow同一行贴在一起
# 注意喔!同一行中间是以[tab] 按键隔开的!
[root@localhost test]# paste /etc/passwd /etc/shadow | head -n 3
root:x:0:0:root:/root:/bin/bash root:$6$2YkSvs.5fQRTXxX7$5/boPraToML3eeZy7FxDe3vbwShbyIR...
bin:x:1:1:bin:/bin:/sbin/nologin bin:*:16659:0:99999:7:::
daemon:x:2:2:daemon:/sbin:/sbin/nologin daemon:*:16659:0:99999:7:::

# 这个例子的重点在那个-的使用!那玩意儿常常代表stdin喔!
[root@localhost test]# cat /etc/group | paste /etc/passwd /etc/shadow - | head -n 3
root:x:0:0:root:/root:/bin/bash root:$6$2YkSvs.5fQRTXxX7$5/boPraToML3eeZy7FxDe3vbwShbyIR... root:x:0:
bin:x:1:1:bin:/bin:/sbin/nologin bin:*:16659:0:99999:7::: bin:x:1:
daemon:x:2:2:daemon:/sbin:/sbin/nologin daemon:*:16659:0:99999:7::: daemon:x:2:

expand:tab转空格

1
2
3
4
5
6
7
8
9
10
11
12

[dmtsai@study ~]$ expand [-t] file
选项与参数:
-t :后面可以接数字。一般来说,一个tab 按键可以用8 个空白键取代。
我们也可以自行定义一个[tab] 按键代表多少个字元呢!

[root@localhost test]# cat -A a
a^Ib$
[root@localhost test]# cat a | expand | cat -A
a b$
[root@localhost test]# cat a | expand -t 2 | cat -A ===> 一个 tab 转换为两个 空白
a b$

也可以参考一下unexpand 这个将空白转成[tab] 的指令功能啊!^_^

split:分割文件

如果你有档案太大,导致一些携带式装置无法复制的问题,嘿嘿!找split 就对了!
他可以帮你将一个大档案,依据档案大小或行数来分割,就可以将大档案分割成为小档案了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

[dmtsai@study ~]$ split [-bl] file PREFIX
选项与参数:
-b :后面可接欲分割成的档案大小,可加单位,例如b, k, m 等;
-l :以行数来进行分割。
PREFIX :代表前置字元的意思,可作为分割档案的前导文字。


# 案例1、按照大小进行分割
# 按照 1 byte 进行分割。(如果想按照 300k 进行分割,可以写成 -b 300k)
[root@localhost test]# cat a
abc
[root@localhost test]# split -b 1 a a-split-
[root@localhost test]#
[root@localhost test]# ll
总用量 20
-rw-r--r--. 1 root root 4 11月 17 04:31 a
-rw-r--r--. 1 root root 1 11月 17 04:39 a-split-aa
-rw-r--r--. 1 root root 1 11月 17 04:39 a-split-ab
-rw-r--r--. 1 root root 1 11月 17 04:39 a-split-ac
-rw-r--r--. 1 root root 1 11月 17 04:39 a-split-ad
# 小档案就会以 xxxaa, xxxab, xxxac 等方式来建立小档案的!

# 案例2、分割的档案,合并
# 将上面的小档案合成一个档案,档名为 a.bak 。就用资料流重导向就好啦!
[root@localhost test]# cat a-split-a* >> a.bak
[root@localhost test]# cat a.bak
abc

# 案例3、按照行,进行分割
[root@localhost test]# cat b
a
b
c
d
e
f
g
[root@localhost test]# split -l 3 b b-split-
[root@localhost test]#
[root@localhost test]# ll
总用量 16
-rw-r--r--. 1 root root 14 11月 17 04:44 b
-rw-r--r--. 1 root root 6 11月 17 04:45 b-split-aa
-rw-r--r--. 1 root root 6 11月 17 04:45 b-split-ab
-rw-r--r--. 1 root root 2 11月 17 04:45 b-split-ac
[root@localhost test]# cat b-split-aa
a
b
c
[root@localhost test]# cat b-split-ab
d
e
f
[root@localhost test]# cat b-split-ac
g
[root@localhost test]# wc -l b-split-a*
3 b-split-aa
3 b-split-ab
1 b-split-ac
7 总用量

[root@localhost test]# ll
总用量 4
-rw-r--r--. 1 root root 14 11月 17 04:44 b
[root@localhost test]#
[root@localhost test]#
[root@localhost test]#
# 重点在那个(- 减号)啦!一般来说,如果需要stdout/stdin 时,但偏偏又没有档案,
# 有的只是(- 减号)时,那么那个(- 减号)就会被当成stdin 或stdout ~
[root@localhost test]# cat b | split -l 3 - b-split-
[root@localhost test]# ll
总用量 16
-rw-r--r--. 1 root root 14 11月 17 04:44 b
-rw-r--r--. 1 root root 6 11月 17 04:46 b-split-aa
-rw-r--r--. 1 root root 6 11月 17 04:46 b-split-ab
-rw-r--r--. 1 root root 2 11月 17 04:46 b-split-ac

xargs:参数代换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

[dmtsai@study ~]$ xargs [-0epn] command
选项与参数:
-0 :如果输入的stdin 含有特殊字元,例如`, \, 空白键等等字元时,这个-0 参数
可以将他还原成一般字元。这个参数可以用于特殊状态喔!
-e :这个是EOF (end of file) 的意思。后面可以接一个字串,当xargs 分析到这个字串时,
就会停止继续工作!
-p :在执行每个指令的argument 时,都会询问使用者的意思;
-n :后面接次数,每次command 指令执行时,要使用几个参数的意思。
当xargs 后面没有接任何的指令时,预设是以echo 来进行输出喔!


# 范例一:将/etc/passwd内的第一栏取出,仅取三行,使用id这个指令将每个帐号内容秀出来
[root@localhost test]# PS1=----------------------------------------------
-------------------------------------id root
uid=0(root) gid=0(root) 组=0(root) ===> 这个id指令可以查询使用者的UID/GID等资讯
-------------------------------------id $(cut -d ':' -f 1 /etc/passwd | head -n 3)
id: 额外的操作数 "bin"
Try 'id --help' for more information.
# 虽然使用$(cmd)可以预先取得参数,但可惜的是, id这个指令『仅』能接受一个参数而已!
# 所以上述的这个指令执行会出现错误!根本不会显示用户的ID 啊!
-------------------------------------cut -d ':' -f 1 /etc/passwd | head -n 3 | id
uid=0(root) gid=0(root) 组=0(root) 环境=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 ===> 我不是要查自己啊!
# 因为id并不是管线命令,因此在上面这个指令执行后,前面的东西通通不见!只会执行id!
-------------------------------------
-------------------------------------cut -d ':' -f 1 /etc/passwd | head -n 3 | xargs id
id: 额外的操作数 "bin"
Try 'id --help' for more information.
# 依旧会出现错误!这是因为xargs一口气将全部的资料通通丢给id处理~但id就接受1个啊最多!
-------------------------------------
-------------------------------------cut -d ':' -f 1 /etc/passwd | head -n 3 | xargs -n 1 id
uid=0(root) gid=0(root) 组=0(root)
uid=1(bin) gid=1(bin) 组=1(bin)
uid=2(daemon) gid=2(daemon) 组=2(daemon)
# 透过-n 来处理,一次给予一个参数,因此上述的结果就OK 正常的显示啰!


范例二:同上,但是每次执行id时,都要询问使用者是否动作?
-------------------------------------cut -d ':' -f 1 /etc/passwd | head -n 3 | xargs -pn 1 id
id root ?...y
uid=0(root) gid=0(root) 组=0(root)
id bin ?...n
id daemon ?...n

范例三:将所有的/etc/passwd内的帐号都以id查阅,但查到sync就结束指令串
-------------------------------------cut -d ':' -f 1 /etc/passwd | xargs -e'sync' -n 1 id
uid=0(root) gid=0(root) 组=0(root)
uid=1(bin) gid=1(bin) 组=1(bin)
uid=2(daemon) gid=2(daemon) 组=2(daemon)
uid=3(adm) gid=4(adm) 组=4(adm)
uid=4(lp) gid=7(lp) 组=7(lp)
# 仔细与上面的案例做比较。也同时注意,那个-e'sync'是连在一起的,中间没有空白键。
# 上个例子当中,第六个参数是sync 啊,那么我们下达-e'sync' 后,则分析到sync 这个字串时,
# 后面的其他stdin 的内容就会被xargs 舍弃掉了!
-------------------------------------

# 范例四:找出/usr/sbin底下具有特殊权限的档名,并使用ls -l列出详细属性
-------------------------------------find /usr/sbin -perm /7000 | xargs ls -l
-rwxr-sr-x. 1 root root 11208 11月 20 2015 /usr/sbin/netreport
-rwsr-xr-x. 1 root root 11208 8月 18 2015 /usr/sbin/pam_timestamp_check
-rwxr-sr-x. 1 root postdrop 218552 6月 10 2014 /usr/sbin/postdrop
-rwxr-sr-x. 1 root postdrop 259992 6月 10 2014 /usr/sbin/postqueue
-rwsr-xr-x. 1 root root 36264 8月 18 2015 /usr/sbin/unix_chkpwd
-rwsr-xr-x. 1 root root 11272 11月 20 2015 /usr/sbin/usernetctl
# 聪明的读者应该会想到使用『 ls -l $(find /usr/sbin -perm /7000) 』来处理这个范例!
# 都OK!能解决问题的方法,就是好方法!

- 减号的用途解释

管线命令在bash 的连续的处理程序中是相当重要的!
另外,在log file 的分析当中也是相当重要的一环, 所以请特别留意!
另外,在管线命令当中,常常会使用到前一个指令的stdout 作为这次的stdin ,
某些指令需要用到档案名称(例如tar) 来进行处理时,该stdin 与stdout 可以利用减号”-“ 来替代, 举例来说:

1
2
[root@study ~]# mkdir /tmp/homeback 
[root@study ~]# tar -cf - /home | tar -xf - -C /tmp/homeback ===> -C 是解压至哪儿目录中

上面这个例子是说:『我将/home 里面的档案给他打包,但打包的资料不是纪录到档案,而是传送到stdout;
经过管线后,将tar -cvf - /home 传送给后面的tar - xvf - 』。
后面的这个- 则是取用前一个指令的stdout, 因此,我们就不需要使用filename 了!
这是很常见的例子喔!注意注意!

正则表达式:扩展

正则表达式

vi, grep, awk ,sed 正则
cp, ls 通配符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
特殊符号    代表意义
[:alnum:] 代表英文大小写字元及数字,亦即0-9, AZ, az
[:alpha:] 代表任何英文大小写字元,亦即AZ, az
[:blank:] 代表空白键与[Tab] 按键两者
[:cntrl:] 代表键盘上面的控制按键,亦即包括CR, LF, Tab, Del.. 等等
[:digit:] 代表数字而已,亦即0-9
[:graph:] 除了空白字元(空白键与[Tab] 按键) 外的其他所有按键
[:lower:] 代表小写字元,亦即az
[:print:] 代表任何可以被列印出来的字元
[:punct:] 代表标点符号(punctuation symbol),亦即:" ' ? ! ; : # $...
[:upper:] 代表大写字元,亦即AZ
[:space:] 任何会产生空白的字元,包括空白键, [Tab], CR 等等
[:xdigit:] 代表16 进位的数字类型,因此包括: 0-9, AF, af 的数字与字元


[root@localhost test]# cat -n a
1 ab
2 cd
3 123ab
4 Gd
5 sdf65
6 EF
7 Ec
8 Ab
[root@localhost test]# grep -n '[[:digit:]]' a ===> 含有数字
3:123ab
5:sdf65
[root@localhost test]#
[root@localhost test]# grep -n '^[[:digit:]]' a ===> 数字开头
3:123ab
[root@localhost test]#
[root@localhost test]# grep -n '^[[:lower:]]' a ===> 小写字母开头
1:ab
2:cd
5:sdf65

sed:替换、删除、新增、截取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

[dmtsai@study ~]$ sed [-nefr] [动作]
选项与参数:
-n :使用安静(silent)模式。在一般sed 的用法中,所有来自STDIN 的资料一般都会被列出到萤幕上。
但如果加上-n 参数后,则只有经过sed 特殊处理的那一行(或者动作)才会被列出来。
-e :直接在指令列模式上进行sed 的动作编辑;
-f :直接将sed 的动作写在一个档案内, -f filename 则可以执行filename 内的sed 动作;
-r :sed 的动作支援的是延伸型正规表示法的语法。(预设是基础正规表示法语法)
-i :直接修改读取的档案内容,而不是由萤幕输出。

动作说明: [n1[,n2]]function
n1, n2 :不见得会存在,一般代表『选择进行动作的行数』,举例来说,如果我的动作
是需要在10 到20 行之间进行的,则『 10,20[动作行为] 』

function 有底下这些咚咚:
a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~
i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
d :删除,因为是删除啊,所以d 后面通常不接任何咚咚;
p :列印,亦即将某个选择的资料印出。通常p 会与参数sed -n 一起运作~
c :取代, c 的后面可以接字串,这些字串可以取代n1,n2 之间的行!
s :取代,可以直接进行取代的工作哩!通常这个s 的动作可以搭配正规表示法!
例如1,20s/old/new/g 就是啦!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# 1. a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~
[root@localhost test]# PS1=-----------------------------------
-----------------------------------
-----------------------------------cat -n /etc/passwd | head -n 3 | sed '1a 陶攀峰'
1 root:x:0:0:root:/root:/bin/bash
陶攀峰
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
-----------------------------------cat -n /etc/passwd | head -n 3 | sed '1,2a 陶攀峰'
1 root:x:0:0:root:/root:/bin/bash
陶攀峰
2 bin:x:1:1:bin:/bin:/sbin/nologin
陶攀峰
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin

# 2. i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行);
-----------------------------------cat -n /etc/passwd | head -n 3 | sed '1i 陶攀峰'
陶攀峰
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
-----------------------------------cat -n /etc/passwd | head -n 3 | sed '1,2i 陶攀峰'
陶攀峰
1 root:x:0:0:root:/root:/bin/bash
陶攀峰
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin

# 3. d :删除,因为是删除啊,所以d 后面通常不接任何咚咚;
-----------------------------------cat -n /etc/passwd | head -n 3 | sed '2d'
1 root:x:0:0:root:/root:/bin/bash
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
-----------------------------------
-----------------------------------
-----------------------------------cat -n /etc/passwd | head -n 3 | sed '1,2d'
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin

# 4. p :列印,亦即将某个选择的资料印出。通常p 会与参数sed -n 一起运作~
-----------------------------------cat -n /etc/passwd | head -n 3 | sed '1p' ===> 把第一行复制一次
1 root:x:0:0:root:/root:/bin/bash
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
-----------------------------------cat -n /etc/passwd | head -n 3 | sed '1,2p' ===> 把第一行,第二行都复制一次
1 root:x:0:0:root:/root:/bin/bash
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
-----------------------------------cat -n /etc/passwd | head -n 3 | sed -n '1,2p' ===> 把第一行,第二行打印出来
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
# 5. c :取代, c 的后面可以接字串,这些字串可以取代n1,n2 之间的行!
-----------------------------------cat -n /etc/passwd | head -n 3 | sed '1c 陶攀峰'
陶攀峰
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
-----------------------------------cat -n /etc/passwd | head -n 3 | sed '1,2c 陶攀峰'
陶攀峰
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin


# 一个特殊符号 \t,\n,... 的使用,前面要加 \
# 例如:\t 就输入 \\t,\n 就输出 \\n。
# 除一个外,后面不需要加 \
--------------------------cat -n /etc/passwd | head -n 3 | sed '1,2c \\t陶攀峰'
陶攀峰
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
--------------------------cat -n /etc/passwd | head -n 3 | sed '1,2c \\t陶攀峰\t陶攀峰2\n'
陶攀峰 陶攀峰2

3 daemon:x:2:2:daemon:/sbin:/sbin/nologin

# 6. s :取代,可以直接进行取代的工作哩!通常这个s 的动作可以搭配正规表示法!也可以用作删除。
-----------------------------------cat -n /etc/passwd | head -n 3 | sed 's/bin/AAAAAAAAAA/g' ===> 全局替换
1 root:x:0:0:root:/root:/AAAAAAAAAA/bash
2 AAAAAAAAAA:x:1:1:AAAAAAAAAA:/AAAAAAAAAA:/sAAAAAAAAAA/nologin
3 daemon:x:2:2:daemon:/sAAAAAAAAAA:/sAAAAAAAAAA/nologin
-----------------------------------cat -n /etc/passwd | head -n 3 | sed '1s/bin/AAAAAAAAAA/g' ===> 仅替换第一行
1 root:x:0:0:root:/root:/AAAAAAAAAA/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
-----------------------------------cat -n /etc/passwd | head -n 3 | sed '1,2s/bin/AAAAAAAAAA/g' ===> 仅替换第一行,和 第二行
1 root:x:0:0:root:/root:/AAAAAAAAAA/bash
2 AAAAAAAAAA:x:1:1:AAAAAAAAAA:/AAAAAAAAAA:/sAAAAAAAAAA/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
-----------------------------------cat -n /etc/passwd | head -n 3 | sed '1s/bin//g' ===> 删除第一行,"bin" 字符串
1 root:x:0:0:root:/root://bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
-----------------------------------cat -n /etc/passwd | head -n 3 | sed '1s/^.*bin//g' ===> 删除第一行,"xxxbin" 字符串
/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
# 利用 sed 配合正则,查询本机 IP
-----------------------------------ifconfig eno16777736 | grep 'inet ' | sed 's/.*inet *//g' | sed 's/ *netmask.*//g'
192.168.1.3


# 7. -i :直接修改读取的档案内容,而不是由萤幕输出。(谨慎操作。会修改原文件内容)
--------------------------sed -i '$a 陶攀峰' regular_express.txt ===> a表示下面新增一行,$表示最后,在下面新增一行“陶攀峰”
--------------------------sed -i 's/^bind .*/bind 0.0.0.0/g' redis.conf ===> 直接修改文件

# 8. -e :直接在指令列模式上进行sed 的动作编辑;
# 下面两个命令效果是相同的,一个是执行一次 sed,一个是执行两次 sed。
# 其中 \\t 进代表 \t (tab) 制表符的作用。
--------------------------cat -n /etc/passwd | head -n 3 | sed '2d' | sed '1c \\tno oneline'
no one line
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
--------------------------cat -n /etc/passwd | head -n 3 | sed -e '2d' -e '1c \\tno oneline'
no one line
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin

printf:格式化列印

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
[dmtsai@study ~]$ printf '列印格式'实际内容
选项与参数:
关于格式方面的几个特殊样式:
\a 警告声音输出
\b 倒退键(backspace)
\f 清除萤幕(form feed)
\n 输出新的一行
\r 亦即Enter 按键
\t 水平的[tab] 按键
\v 垂直的[tab] 按键
\xNN NN 为两位数的数字,可以转换数字成为字元。
关于C 程式语言内,常见的变数格式
%ns 那个n 是数字, s 代表string ,亦即多少个字元;
%ni 那个n 是数字, i 代表integer ,亦即多少整数位数;
%N.nf 那个n 与N 都是数字, f 代表floating (浮点),如果有小数位数,
假设我共要十个位数,但小数点有两位,即为%10.2f 啰!


[root@localhost test]# PS1=------------------------

# 1. %s 后面会看成字符串
------------------------printf %s 1 2 3 4 5 6
123456------------------------

# 2. 只有第一个 %s 起作用,后面都看着 String
------------------------printf %s %s %s 1 2 3 4 5 6
%s%s123456------------------------

# 3. 单引号引起来就不会当做 String
# 一个 %s 代表一个字符,第一次会打印"1 2"(一个两个%s直接有空格),第二次打印"3 4",随之"5 6"
------------------------printf '%s %s' 1 2 3 4 5 6
1 23 45 6------------------------

# 4. 同上,但两个 %s 之间没有空格,所以,第一次打印"12",随之打印"34","56"
------------------------printf '%s%s' 1 2 3 4 5 6
123456------------------------

# 5. 一个 %s 代表一个字符(注意%s 之间有空格),第一次打印"1 2 3",随之打印"4 5 6"
------------------------printf '%s %s %s' 1 2 3 4 5 6
1 2 34 5 6------------------------

# 6. 加上换行符 \n,第一次输出"1 2 3\n",随之"4 5 6\n"
------------------------printf '%s %s %s\n' 1 2 3 4 5 6
1 2 3
4 5 6
------------------------

# 7. 这个只有一个字符串"123456",所以只有第一个 %s 表示"123456",其他两个 %s 表示空白,随之输出 \n
------------------------printf '%s %s %s\n' 123456
123456
------------------------


# 8. 学会了,上面的之后,我们再进行一个测试。试着打印不一样长的字串。
------------------------printf '%s %s %s\n' 11 2 333 4 5 6 7777 8 9
11 2 333
4 5 6
7777 8 9
------------------------
# 咦?上面怎么对不齐了?!
# 因为 %s 仅代表一个字符串,如果想锁定字符串长度,需要使用 %ns,这个 n 就是字串长度
# 锁定长度之后,字符串长度不够,用空白代替。
------------------------printf '%4s %s %3s\n' 11 2 333 4 5 6 7777 8 9
11 2 333
4 5 6
7777 8 9
# 也可以把长度设置的大一点
------------------------printf '%6s %3s %5s\n' 11 2 333 4 5 6 7777 8 9
11 2 333
4 5 6
7777 8 9

小案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
------------------------cat printf.txt
Name Chinese English Math Average
Kevin 80 60 92 77.33
Panfeng 75 55 80 70.00
VBird 60 90 70 73.33

# 都看作字符串,\t(tab 制表符)
# 因为 printf 不支持管道符,流处理。
# 所以要使用 $(cat printf.txt) 或 `cat printf.txt`,使命令先执行,把结果给 printf 引用。
------------------------printf '%s\t%s\t%s\t%s\t%s\t\n' $(cat printf.txt)
Name Chinese English Math Average
Kevin 80 60 92 77.33
Panfeng 75 55 80 70.00
VBird 60 90 70 73.33

# 使用具体类型
------------------------printf '%10s\t%5i\t%5i\t%5i\t%8.2f\t\n' $(cat printf.txt | grep -v Name)
Kevin 80 60 92 77.33
Panfeng 75 55 80 70.00
VBird 60 90 70 73.33

注意:
字元宽度: 12345678
%8.2f意义:00000.00

这里的8代表,整体字串是 8,其中小数占 2。
8.2,如果小数点之后小于2,补0。大于2,舍去多余位。数字整体 xxx.xx 小于8,补0。大于8,保留原 数字大小。

例如:%1.2f === 123.4 -> 123.40

awk:好用的资料处理工具

用于操作 列。分割成一列一列的。
cut 命令只能按照符号分割列,但不能按照空格分割。
但 awk 是可以按照空格分割列的。所以,可以说 awk 包含 cut 功能。

1
2
3
4
5
6
7
8
# awk '条件1{动作1} 条件2{动作2} ...' 文件名
默认分隔符:空格(space)或 制表符(tab)
表示符合条件1执行动作1,符号条件2执行动作2,…
条件:例如x>10 x>=10 x<=10
动作:格式化输出(print 或 printf),流程控制语句
print 比 printf 多了一个 \n 换行功能

$0 代表一整行。$1 代表第一列。$2 代表第二列。...

》》》简单使用,了解“动作”的使用。 print printf $0 $1 $2《《《

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

# 原文件内容
--------------------------cat awkfile
Name Chinese English Math Average
Kevin 80 60 92 77.33
Panfeng 75 55 80 70.00
VBird 60 90 70 73.33

# 打印第一列数据
--------------------------cat awkfile | awk 'printf $1'
awk: cmd. line:1: printf $1
awk: cmd. line:1: ^ syntax error

# 语法错误!~ 因为没有大括号 {},他不属于一个“动作”
# 要使用 单引号 '',里面再写 大括号 {},表示一个动作。
# 此时,大括号 {}前面,也就是动作前面,没有任何条件,可以直接执行。
--------------------------cat awkfile | awk '{printf $1}'
NameKevinPanfengVBird--------------------------

# 上述输出,没有换行。我们可以加上换行符 \n
--------------------------cat awkfile | awk '{printf $1\n}'
awk: cmd. line:1: {printf $1\n}
awk: cmd. line:1: ^ backslash not last character on line
awk: cmd. line:1: {printf $1\n}
awk: cmd. line:1: ^ syntax error

# 语法错误!~ 因为只有 $1 $2 这种不需要加双引号 "",其他输出都需要加双引号 ""
--------------------------cat awkfile | awk '{printf $1"\n"}'
Name
Kevin
Panfeng
VBird

# 上面的 printf \n 等价于 print
--------------------------cat awkfile | awk '{print $1}'
Name
Kevin
Panfeng
VBird
# 输出第一列,和 第三列
--------------------------cat awkfile | awk '{print $1 $3}'
NameEnglish
Kevin60
Panfeng55
VBird90

# 1,3两列之间加上空格
--------------------------cat awkfile | awk '{print $1" "$3}'
Name English
Kevin 60
Panfeng 55
VBird 90
# 1,3两列之间加上制表符(tab)
--------------------------cat awkfile | awk '{print $1"\t"$3}'
Name English
Kevin 60
Panfeng 55
VBird 90

# 为什么说 $0 是一整行,下面我们来实验一下。
# 在 $0 前后分别加上 AAA,BBB
--------------------------cat awkfile | awk '{print "AAA" $0 "BBB"}'
AAAName Chinese English Math Average BBB
AAAKevin 80 60 92 77.33 BBB
AAAPanfeng 75 55 80 70.00 BBB
AAAVBird 60 90 70 73.33 BBB

# 讲完了 $0,现在我们可以对下面这行代码做一个完整的解释了。
# 1. 读入第一行,并将第一行的资料填入$0, $1, $2.... 等变数当中;
# 2. 依据"条件" 的限制,判断是否需要进行后面的"动作";(此时无条件,所以后面”动作“直接执行)
# 3. 做完所有的动作与条件类型;
# 4. 上面 123 步仅处理了第一行的数据,接着处理第二行的数据,重复 123 步,直到所有的资料都读完为止。
--------------------------cat awkfile | awk '{print $1 "\t" $3}'
Name English
Kevin 60
Panfeng 55
VBird 90

上面我们知道了列,如果想知道行号,和每一行的列数怎么办?

1
2
3
4
5
6
7
8
9
10
NF  (F field)每一行($0) 拥有的栏位总数
NR (R row)目前awk 所处理的是『第几行』资料
FS 目前的分隔字元,预设是空白键

# 输出第一列,并输出当前的列总数 NF,与 当前行数 NR。
--------------------------cat awkfile | awk '{print NF "\t" NR "\t" $1}'
5 1 Name
5 2 Kevin
5 3 Panfeng
5 4 VBird

既然需要“条件”,就要有逻辑运算~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
>   大于
< 小于
>= 大于或等于
<= 小于或等于
== 等于(== 是等于,= 是变量赋值)
!= 不等于

# 回顾一下 cat sort
# 1. 查看 /etc/passwd
# 2. 根据冒号 : 分割列,取 1,2,3 三列数据
# 3. -r表示降序(默认升序),-n表示数字排序(默认字符串),-t 分割符号,-k 按照第三列排序
--------------------------cat /etc/passwd | cut -d ":" -f 1-3 | sort -rnt ":" -k 3
systemd-bus-proxy:x:999
systemd-network:x:998
polkitd:x:997
avahi-autoipd:x:170
nobody:x:99
postfix:x:89
dbus:x:81
sshd:x:74
tss:x:59
ftp:x:14
games:x:12
operator:x:11
mail:x:8
halt:x:7
shutdown:x:6
sync:x:5
lp:x:4
adm:x:3
daemon:x:2
bin:x:1
root:x:0

# /etc/password 第一列是账号,第三列是 UID。
# 查询第三列小于 10 以下的数据,仅输出1,3两列
--------------------------cat /etc/passwd | awk '{FS=":"} $3<10 {print $1 "\t" $3}'
root:x:0:0:root:/root:/bin/bash
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8

# 上面为什么第一行没有正确显示出来?
# 是因为我们读入第一行的时候,那些 $1,$2,... 还是使用默认空白键分割的
# 虽然我们定义了 FS=":",但仅在第二行后生效。
# 基于上面的问题,我们可以使用 BEGIN 关键字来解决。
--------------------------cat /etc/passwd | awk 'BEGIN {FS=":"} $3<10 {print $1 "\t" $3}'
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8

# 对于上面的,不能够对齐,怎么办。我们可以使用 printf 进行处理。
# 注意:printf 的表达式要用双引号,其后面要跟上对于的列,用逗号分割。
--------------------------cat /etc/passwd | awk 'BEGIN {FS=":"} $3<10 {printf "%20s\t%10s\n",$1,$3}'
root 0
bin 1
daemon 2
adm 3
lp 4
sync 5
shutdown 6
halt 7
mail 8
# 对于 BEGIN,是否有 END 关键字呢~!@
# 有的。这里就不举例了。
# BEGIN:所有动作都未执行,先执行 BEGIN。
# END:所有动作都执行完毕,再执行END。


# 计算功能。计算总成绩
--------------------------cat printf.txt
Name Chinese English Math Average
Kevin 80 60 92 77.33
Panfeng 75 55 80 70.00
VBird 60 90 70 73.33

# 给第一行添加列:"Total"。除第一行外添加列,是 2,3,4 三列之和。
--------------------------cat printf.txt | awk 'NR==1 {printf "%10s%10s%10s%10s%10s%10s\n",$1,$2,$3,$4,$5,"Total"} \
> NR>1 {printf "%10s%10s%10s%10s%10s%10s\n",$1,$2,$3,$4,$5,$2+$3+$4}'
Name Chinese English Math Average Total
Kevin 80 60 92 77.33 232
Panfeng 75 55 80 70.00 210
VBird 60 90 70 73.33 220

# 也可以使用 awk 变量(使用时,无需加 $)。
# 动作中多个命令使用 ; 分割。
--------------------------cat printf.txt | awk 'NR==1 {printf "%10s%10s%10s%10s%10s%10s\n",$1,$2,$3,$4,$5,"Total"} \
> NR>1 {total=$2+$3+$4 ; printf "%10s%10s%10s%10s%10s%10s\n",$1,$2,$3,$4,$5,total}'
Name Chinese English Math Average Total
Kevin 80 60 92 77.33 232
Panfeng 75 55 80 70.00 210
VBird 60 90 70 73.33 220
--------------------------

diff:找不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

[dmtsai@study ~]$ diff [-bBi] from-file to-file
选项与参数:
from-file :一个档名,作为原始比对档案的档名;
to-file :一个档名,作为目的比对档案的档名;
注意,from-file 或to-file 可以- 取代,那个- 代表『Standard input』之意。

-b :忽略一行当中,仅有多个空白的差异(例如"about me""about me" 视为相同
-B :忽略空白行的差异。
-i :忽略大小写的不同。

# 1. 找出两个文件(纯文本)的不同之处

--------------------------cp /etc/passwd p.old
--------------------------cat p.old | sed -e '4d' -e '6c \\tno six line' > p.new
--------------------------diff p.old p.new
4d3 <==左边第四行被删除(d)掉了,基准是右边的第三行
< adm:x:3:4:adm:/var/adm:/sbin/nologin <==这边列出左边(<)档案被删除的那一行内容
6c5 <==左边档案的第六行被取代(c)成右边档案的第五行
< sync:x:5:0:sync:/sbin:/bin/sync <==左边(<)档案第六行内容
---
> no six line <==右边(>)档案第五行内容
--------------------------
--------------------------diff p.new p.old
3a4 ===> 在左边第三行(a)下面插入一行下方内容,所在位置是右边的第四行
> adm:x:3:4:adm:/var/adm:/sbin/nologin
5c6 ===> 在左边(<)第五行 "no six line",替换为右边(>)第六行 "sync:x:5:0:sync:/sbin:/bin/sync"
< no six line
---
> sync:x:5:0:sync:/sbin:/bin/sync
--------------------------




# 2. 找出两个目录的不同之处
--------------------------ll /etc/rc0.d/
总用量 0
lrwxrwxrwx. 1 root root 20 11月 3 18:52 K50netconsole -> ../init.d/netconsole
lrwxrwxrwx. 1 root root 17 11月 3 18:52 K90network -> ../init.d/network
--------------------------
--------------------------ll /etc/rc5.d/
总用量 0
lrwxrwxrwx. 1 root root 20 11月 3 18:52 K50netconsole -> ../init.d/netconsole
lrwxrwxrwx. 1 root root 17 11月 3 18:52 S10network -> ../init.d/network
--------------------------
--------------------------
--------------------------
--------------------------diff /etc/rc0.d/ /etc/rc5.d/
只在 /etc/rc0.d/ 存在:K90network
只在 /etc/rc5.d/ 存在:S10network

cmp:提示两文件第一个不同之处

相对于diff 的广泛用途, cmp 似乎就用的没有这么多了~

可以比较二进制。

1
2
3
4
5
6
7
8
9
10

[dmtsai@study ~]$ cmp [-l] file1 file2
选项与参数:
-l :将所有的不同点的位元组处都列出来。因为cmp 预设仅会输出第一个发现的不同点。

# 比较两个文件的不同。
# 仅显示第一个不同之处。
--------------------------cmp p.old p.new
p.old p.new 不同:第 106 字节,第 4 行
--------------------------

patch

省略学习

pr:档案分页显示

感觉没卵用啊

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
[root@localhost ~]# pr /etc/man_db.conf


2014-06-10 05:35 /etc/man_db.conf 第 1 页


#
#
# This file is used by the man-db package to configure the man and cat paths.
...省略




2014-06-10 05:35 /etc/man_db.conf 第 2 页


# location of catpaths and the creation of database caches; it has no effect
# on privileges.
...省略





2014-06-10 05:35 /etc/man_db.conf 第 3 页


#---------------------------------------------------------
# Range of terminal widths permitted when displaying cat pages. If the
# terminal falls outside this range, cat pages will not be created (if
...省略


[root@localhost ~]#

案例1.对谈式脚本:变数内容由使用者决定

1
2
3
4
5
6
7
[root@localhost sh]# cat showname.sh
#!/bin/bash

read -p "Please input your last name:" lastname
read -p "Please input your first name:" firstname

echo -e "Your full name is:${firstname} ${lastname}"

案例2.随日期变化:利用date 进行档案的建立

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[root@localhost sh]# cat create-3-filename.sh
#!/bin/bash

echo -e "I will use ‘touch’ command to create 3 files"

read -p "Please input your filename:" fileuser

# fileuser 不存在或为空串,使用filename。
# 否则,使用${fileuser}
filename=${fileuser:-"filename"}

# 当前时间:2020-11-23 11:27:46
# date1=20201121
# date1=20201122
# date1=20201123
date1=$(date --date='2 days ago' +%Y%m%d)
date2=$(date --date='1 days ago' +%Y%m%d)
date3=$(date +%Y%m%d)


# 拼接字符串
file1=${filename}${date1}
file2=${filename}${date2}
file3=${filename}${date3}

touch "${file1}"
touch "${file2}"
touch "${file3}"

案例3.数值运算:简单的加减乘除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@localhost sh]# cat multiplying.sh
#!/bin/bash

echo -e "Your should input 2 numbers,I will multiplying them!"

read -p "first number:" firstnum
read -p "second number:" secondnum

# $((运算))
# 等同于 declare -i total=${firstnum}*${secondnum}
# 两种都为数值运算。
total=$((${firstnum}*${secondnum}))

echo -e "The result of ${firstnum} x ${secondnum} is ===> ${total}"

案例4.数值运算:透过bc 计算π

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@localhost sh]# cat cal-pi.sh
#!/bin/bash

echo -e "This program will calculate pi values."

echo -e "Your should input a float number to calculate pi values."

read -p "The scale number (10~1W):" scale

num=${scale:-"10"}

echo -e "Starting calculate pi value。。。"

# 计算的 π 的固定写法。其中 num 表示输出的 π 的位数
# echo "scale=${num} ; 4*a(1)" | bc -lq
# 加上 time 可计算耗时
time echo "scale=${num} ; 4*a(1)" | bc -lq

script 的执行方式差异(source, sh script, ./script)

1、利用直接执行的方式来执行script

1
2
3
4
5
6
7
8
9
10

[dmtsai@study bin]$ echo ${firstname} ${lastname}
<==确认了,这两个变数并不存在喔!
[dmtsai@study bin]$ sh showname.sh
Please input your first name: VBird <==这个名字是鸟哥自己输入的
Please input your last name: Tsai

Your full name is: VBird Tsai <==看吧!在script运作中,这两个变数有生效
[dmtsai@study bin]$ echo ${firstname} ${lastname}
<==事实上,这两个变数在父程序的bash中还是不存在的!

在这里插入图片描述

2、利用source 来执行脚本:在父程序中执行

1
2
3
4
5
6
7
8

[dmtsai@study bin]$ source showname.sh
Please input your first name: VBird
Please input your last name: Tsai

Your full name is: VBird Tsai
[dmtsai@study bin]$ echo ${firstname} ${lastname}
VBird Tsai <==嘿嘿!有资料产生喔!

竟然生效了!没错啊!因为source 对script 的执行方式可以使用底下的图示来说明!
showname.sh 会在父程序中执行的,因此各项动作都会在原本的bash 内生效!
这也是为啥你不登出系统而要让某些写入~/.bashrc 的设定生效时,需要使用『 source ~/.bashrc 』而不能使用『 bash ~/.bashrc 』是一样的啊!
在这里插入图片描述

总结:source:当前bash执行。其他模式:开一个子shell执行。

test:测试功能

当我要检测系统上面某些档案或者是相关的属性时,利用test 这个指令来工作真是好用得不得了

test -e /root/anaconda-ks.cfg

===> 为了看得清楚,我使用“空格”代替的一个空格
===> 下面的这些符号:”-e”、”-f”、”==”、”!”、… 等等,这些符号使用时,两边要有空格
===> 否则会提示:-bash: test: : 期待一元表达式这样等字样
test空格-e空格/root/anaconda-ks.cfg

===> 数据不会显示,可以使用 $? 或 && || 来显示
test -e /sdfs ; echo $?
test -e /sdfs && echo "exist" || echo "not exist"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

1. 关于某个档名的『档案类型』判断,如test -e filename 表示存在否
-e 该『档名』是否存在?(常用)
-f 该『档名』是否存在且为档案(file)?(常用)
-d 该『档名』是否存在且为目录(directory)?(常用)
-b 该『档名』是否存在且为一个block device 装置?
-c 该『档名』是否存在且为一个character device 装置?
-S 该『档名』是否存在且为一个Socket 档案?
-p 该『档名』是否存在且为一个FIFO (pipe) 档案?
-L 该『档名』是否存在且为一个连结档?

2. 关于档案的权限侦测,如test -r filename 表示可读否(但root 权限常有例外)
-r 侦测该档名是否存在且具有『可读』的权限?
-w 侦测该档名是否存在且具有『可写』的权限?
-x 侦测该档名是否存在且具有『可执行』的权限?
-u 侦测该档名是否存在且具有『SUID』的属性?
-g 侦测该档名是否存在且具有『SGID』的属性?
-k 侦测该档名是否存在且具有『Sticky bit』的属性?
-s 侦测该档名是否存在且为『非空白档案』?

3. 两个档案之间的比较,如: test file1 -nt file2
-nt (newer than)判断file1 是否比file2 新
-ot (older than)判断file1 是否比file2 旧
-ef 判断file1 与file2 是否为同一档案,可用在判断hard link 的判定上。主要意义在判定,两个档案是否均指向同一个inode 哩!

4. 关于两个整数之间的判定,例如test n1 -eq n2
-eq 两数值相等(equal)
-ne 两数值不等(not equal)
-gt n1 大于n2 (greater than)
-lt n1 小于n2 (less than)
-ge n1 大于等于n2 (greater than or equal)
-le n1 小于等于n2 (less than or equal)

5. 判定字串的资料
test -z string 若string 为空字串,则为true
test -n string 若string为空字串,则为false
注: -n亦可省略
----------------------[ -z "${abc}" ] && echo true || echo false #===> abc 变量不存在
true
----------------------[ -n "${abc}" ] && echo true || echo false
false

test str1 == str2 判定str1 是否等于str2 ,若相等,则回传true
test str1 != str2 判定str1 是否不等于str2 ,若相等,则回传false

6. 多重条件判定,例如: test -r filename -a -x filename
-a (and)两状况同时成立!例如test -r file -a -x file,则file 同时具有r 与 x 权限时,才回传true
-o (or)两状况任何一个成立!例如test -r file -o -x file,则file 具有r 或 x 权限时,就可回传true
! 反相状态,如test ! -x file ,当file 不具有x 时,回传true

》》》 小练习
1、控制台输入一个档案名
2、判断档案是否存在,不存在要给予提示
3、若是文件、目录,也要给予提示
4、提示执行者身份具有它的rwx哪些权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@localhost sh]# cat file_perm.sh
#!/bin/bash
# 2020-11-26 10:22:05

read -p "Please input path:" path

test -z ${path} && echo "You must input a path" && exit 0
test ! -e ${path} && echo -e "Filename does not exist" && exit 0

test -f ${path} && echo -e "Filename is regular file"
test -d ${path} && echo -e "Filename is directory"

# 权限最好别用 root 测试。因为 root 隐式具有 rw 权限
test -r ${path} && permission="r"
test -w ${path} && permission="${permission} w"
test -x ${path} && permission="${permission} x"
echo -e "Permission is:${permission}"

[]:判断符号

这个功能和上面的 test 一致。为了简化 test 命令

1
2
3
4
5
test -e file_perm.sh && echo true || echo false #===> 正确,"-e"左右两边有空格
[ -e file_perm.sh ] && echo true || echo false #===> 正确,"-e"左右两边有空格,并且左右中括号内侧有空格

[-e file_perm.sh] && echo true || echo false #===> 错误,左右中括号内侧无空格
[ -e file_perm.sh] && echo true || echo false #===> 错误,右中括号内侧无空格

》》》变量的使用

1
2
3
4
5
6
7
8
9
-----------------------name="tao panfeng"
-----------------------[ ${name} == "tao" ]
-bash: [: 参数太多
-----------------------
-----------------------[ "${name}" == "tao" ]
-----------------------

#===> 没有双引号,会当成:[ tao panfeng == "tao" ]
#===> 加了双引号,会当成:[ "tao panfeng" == "tao" ]

》》》注意事项,总结:
1、在中括号[] 内的每个元素都需要有空白键来分隔;
2、在中括号内的变量,最好都以双引号括号起来;
3、在中括号内的字符串,最好都以单或双引号括号起来。

》》》小练习
1、执行一个程序,让你输入 Y/N
2、输入 Y 或 y 时,提示“继续”
3、输入 N 或 n 时,提示“中断”
4、输入不是 Y/y/N/n 时,提示“不知道你输入的是什么”

1
2
3
4
5
6
7
8
9
10
-----------------------cat ans-yn.sh
#!/bin/bash
#2020-11-26 11:23:49

read -p "Please input (Y/N):" yn

[ "${yn}" == "Y" -o "${yn}" == "y" ] && echo "continue" && exit 0
[ "${yn}" == "N" -o "${yn}" == "n" ] && echo "interrupt" && exit 0

echo "I don't know you input is what"

$@, $*, $#, $0, $1…:Shell script 的预设变数

命令可以带有选项和参数,例如 ls -la 可以察看包含隐藏档的所有属性与权限。
那么 shell script 能不能在脚本档名后面带有参数呢?

举例来说,如果你想要重新启动系统的网络,可以这样做:

1
2
-----------------------file /etc/init.d/network
/etc/init.d/network: Bourne-Again shell script, ASCII text executable

由上,可知“/etc/init.d/network”是一个 shell script。

1
2
3
/etc/init.d/network restart #===> 重启网络
/etc/init.d/network start #===> 开启网络
/etc/init.d/network stop #===> 关闭网络

这里的 restart,start,stop。就是这个 shell script 的参数了。
这个脚本可以根据参数的不同,而执行对应的操作。

我们在之前学习了“read”指令,它可以根据输入的内容,来接收参数,但是它需要用户输入。
如果不想输入的话,就要透过命令后面接参数的方式了,这样比较方便啦。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
-----------------------cat params.sh
#!/bin/bash
# 2020-11-26 15:10:07

echo "\$0:$0" #===> 命令本身
echo "\$1:$1" #===> 第一个参数。注意:大于等于第10个参数,使用${10}
echo "\$2:$2" #===> 第二个参数
echo "\$#:$#" #===> 参数总个数
echo "\$@:$@" #===> 参数集合:"$1" "$2" "$3" ...
echo "\$*:$*" #===> 参数总体:"$1 $2 $3 ...",默认空格分割
-----------------------/app/test/sh/params.sh
$0:/app/test/sh/params.sh #===> 命令本身
$1
$2
$#:0
$@
$*:
-----------------------./params.sh
$0:./params.sh #===> 命令本身
$1
$2
$#:0
$@
$*:
-----------------------./params.sh one
$0:./params.sh
$1:one
$2
$#:1
$@:one
$*:one
-----------------------./params.sh one two
$0:./params.sh
$1:one
$2:two
$#:2
$@:one two
$*:one two
-----------------------./params.sh one two three
$0:./params.sh
$1:one
$2:two
$#:3
$@:one two three
$*:one two three

》》》小练习
1、程序名称是什么?
2、共有几个参数?
3、若参数小于 2,则提示参数数量太少
4、全部参数内容是什么?
5、第一个参数是什么?
6、第二个参数是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-----------------------cat how_paras.sh
#!/bin/bash
# 2020-11-26 14:09:48

echo -e "程序档名:$0"

echo -e "共有参数:$# 个"

[ $# -lt 2 ] && echo "warning:参数数量太少"

echo -e "全部参数内容:$*"

echo -e "第一个参数:$1"
echo -e "第二个参数:$2"
echo -e "\$13:$13"
echo -e "\${13}:${13}"
-----------------------./how_paras.sh a b c d e f g h i j k l m n
程序档名:./how_paras.sh
共有参数:14 个
全部参数内容:a b c d e f g h i j k l m n
第一个参数:a
第二个参数:b
$13:a3 #===> 当做 $1 加上字符串 3
${13}:m

shift:造成参数变数号码偏移

造成参数往后偏移一位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
-----------------------cat how_paras.sh
#!/bin/bash
# 2020-11-26 14:09:48

shift #===> 在所有参数前面,加入一次(忽略第一个参数)

echo -e "程序档名:$0"

echo -e "共有参数:$# 个"

[ $# -lt 2 ] && echo "warning:参数数量太少"

echo -e "全部参数内容:$*"

echo -e "第一个参数:$1"
echo -e "第二个参数:$2"
echo -e "\$13:$13"
echo -e "\${13}:${13}"
-----------------------./how_paras.sh a b c d e f g h i j k l m n
程序档名:./how_paras.sh
共有参数:13 个
全部参数内容:b c d e f g h i j k l m n
第一个参数:b
第二个参数:c
$13:b3
${13}:n
-----------------------cat how_paras.sh
#!/bin/bash
# 2020-11-26 14:09:48

shift;shift #===> 在所有参数前面,加入二次(忽略第二个参数)

echo -e "程序档名:$0"

echo -e "共有参数:$# 个"

[ $# -lt 2 ] && echo "warning:参数数量太少"

echo -e "全部参数内容:$*"

echo -e "第一个参数:$1"
echo -e "第二个参数:$2"

echo -e "\$13:$13"
echo -e "\${13}:${13}"
-----------------------./how_paras.sh a b c d e f g h i j k l m n
程序档名:./how_paras.sh
共有参数:12 个
全部参数内容:c d e f g h i j k l m n
第一个参数:c
第二个参数:d
$13:c3
${13}
-----------------------cat how_paras.sh
#!/bin/bash
# 2020-11-26 14:09:48

shift;shift #===> 在所有参数前面,加入二次(忽略第二个参数)

echo -e "程序档名:$0"

echo -e "共有参数:$# 个"

[ $# -lt 2 ] && echo "warning:参数数量太少"

echo -e "全部参数内容:$*"

echo -e "第一个参数:$1"
echo -e "第二个参数:$2"

shift #===> 再跳过一次

echo -e "\$13:$13"
echo -e "\${13}:${13}"
-----------------------./how_paras.sh a b c d e f g h i j k l m n
程序档名:./how_paras.sh
共有参数:12 个
全部参数内容:c d e f g h i j k l m n
第一个参数:c
第二个参数:d
$13:d3
${13}

经过以上,三次添加 shift,想必了解的差不多了吧。
下面我们来看“条件判断”。

if语句

语法参考:if语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#===> 单分支。写法 1(常用)
if [ 条件判断式 ];then
命令1
命令2
...
fi

#===> 单分支。写法 2
if [ 条件判断式 ]
then
命令1
命令2
...
fi

#===> 双分支
if [ 条件判断式 ];then
条件成立时,执行的程序
else
以上条件不成立是,执行的程序
fi

#===> 多分支
if [ 条件判断式1 ];then
条件1成立时,执行的程序1
elif [ 条件判断式2 ];then
条件2成立时,执行的程序2
elif [ 条件判断式3 ];then
条件3成立时,执行的程序3
...

else
所有条件都不成立时,最后执行此程序
fi

注意:
1、if开头,fi结尾。(将if反过来写,就成为fi啦!结束if之意!)
2、是 elif,不是 else if
3、if、elif都是判断式,后面都带有then来处理。但else 是没有成立的结果了, 所以else 后面没有then!

》》》多重写法

1
2
3
4
5
6
7
8
9
&& 代表 and
|| 代表 or

[ "${yn}" == "Y" -o "${yn}" == "y" ]
等同于
[ "${yn}" == "Y" ] || [ "${ yn}" == "y" ]

之所以这样改,很多人是习惯问题!
很多人则是喜欢一个中括号仅有一个判别式的原因。

》》》小练习
1、执行一个程序,让你输入 Y/N
2、输入 Y 或 y 时,提示“继续”
3、输入 N 或 n 时,提示“中断”
4、输入不是 Y/y/N/n 时,提示“不知道你输入的是什么”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
-----------------------cp ans-yn.sh ans-yn-2-if.sh #===> 拷贝一份更快
-----------------------cat ans-yn-2-if.sh
#!/bin/bash
#2020-11-26 11:23:49

read -p "Please input (Y/N):" yn

if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ];then
echo "continue"
elif [ "${yn}" == "N" ] || [ "${yn}" == "n" ];then
echo "interrupt"
else
echo "I don't know you input is what"
fi

》》》小练习
1、输入一个日期
2、计算现在日期对比输入日期
3、由两个日期的比较来显示【还需要几天】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
--------------------cat cal_retired.sh
#!/bin/bash
# 2020-11-26 17:26:56

read -p "Please input a date(YYYYmmdd):" input_day

now_day=$(date +%Y%m%d)
echo -e "当前日期:${now_day}"

now_second=$(date --date="${now_day}" +%s)
echo -e "当前日期的秒数:${now_second}"

input_second=$(date --date="${input_day}" +%s)
[ $? != "0" ] && exit 0 #===> 输入日期错误
echo -e "输入日期的秒数:${input_second}"

day=$(((${input_second}-${now_second})/24/60/60))
echo -e "计算差值多少秒,再除以 24*60*60,还需要 ${day} 天"
--------------------
--------------------
--------------------./cal_retired.sh
Please input a date(YYYYmmdd):20201130
当前日期:20201126
当前日期的秒数:1606320000
输入日期的秒数:1606665600
计算差值多少秒,再除以 24*60*60,还需要 4 天 #===> 正
--------------------
--------------------
--------------------./cal_retired.sh
Please input a date(YYYYmmdd):20201125
当前日期:20201126
当前日期的秒数:1606320000
输入日期的秒数:1606233600
计算差值多少秒,再除以 24*60*60,还需要 -1 天 #===> 负

case

通常字符串不加双引号,通常有空格等特殊字符才加双引号。
我建议:最好加上双引号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
case ${变量名} in
"值1")
如果变量的值等于1,执行程序1
;;
"值2")
如果变量的值等于2,执行程序2
;;
"值3"|"值4")
如果变量的值等于3或4,执行程序3
;;

...省略其他分支...

*)
如果变量的值都不是以上的值,则执行此程序
;;
esac

》》》小练习
1、参数传 a 输出 1
2、传 b 或 c 输出 2
3、传空字符串,或不传参数 输出 3
4、其他参数 输出 4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
----------------------------cat abc123.sh
#!/bin/bash
# 2020-11-26 17:57:29

case ${1} in
"a")
echo "${1}:1"
;;
"b"|"c")
echo "${1}:2"
;;
"")
echo "空:3"
;;
*)
echo "${1}:4"
;;
esac
----------------------------
----------------------------
----------------------------./abc123.sh a
a:1
----------------------------./abc123.sh b
b:2
----------------------------./abc123.sh c
c:2
----------------------------./abc123.sh d
d:4
----------------------------./abc123.sh
空:3
----------------------------./abc123.sh ""
空:3
---------------------

》》》关于 $0 的独特用途

1
2
3
4
5
6
7
8
9
10
----------------------------/etc/init.d/netconsole
用法:/etc/init.d/netconsole {start|stop|status|restart|condrestart}
----------------------------cat -n /etc/init.d/netconsole #===> 查看脚本
...
34 usage ()
35 {
36 echo $"Usage: $0 {start|stop|status|restart|condrestart}" 1>&2
37 RETVAL=2
38 }
...

》》》关于“case ${变量} in”,其中 ${变量} 有两种获得方式。
1、直接下达式:例如上面提到的,利用『 script.sh variable 』的方式来直接给予$1这个变数的内容,这也是在/etc/init.d目录下大多数程式的设计方式。
2、互动式:透过read这个指令来让使用者输入变数的内容。

举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

----------------------------cat abc123.2.sh
#!/bin/bash
# 2020-11-27 10:50:59

read -p "Please input a word:" input
case ${input} in
#case ${1} in
"a")
echo "1"
;;
"b"|"c")
echo "2"
;;
"")
echo "3"
;;
*)
echo "4"
;;
esac
----------------------------
----------------------------./abc123.2.sh
Please input a word:a
1
----------------------------

function:函数

1
2
3
4
5
6
7
8
9
10
11
#===> 定义

[function] 函数名(){
命令1;
命令2;
[return 0~255]
}

#===> 使用

函数名 [参数1 参数2]

》》》注意点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1、因为shell script的执行方式是由上而下,由左而右。
因此在shell script当中的function的定义一定要在使用之前

2、$0 在脚本的任何位置都是一样的。

3、若在函数定义内使用`${n}、$*、$@$#`,则表示函数调用的参数。
若在函数定义内使用`${n}、$*、$@$#`,则表示脚本调用的参数。

4、$? 在函数调用后使用,表示函数返回值[0,255]
$? 在命令后使用,表示命令执行结果:0命令执行正确,其他都是命令执行错误。

5、函数定义时,关键字 function 可以省略。

6、函数的返回值,默认是 return 0。
0 表示函数调用正常,其他表示函数调用异常

》》》小练习1
1、定义一个函数,并传入一个返回值 a,在函数内打印出来
2、函数返回值 1
3、外部传入参数,并打印出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
----------------------------cat fun1.sh
#!/bin/bash
# 2020-11-27 11:48:22

fun1(){
echo "fun1 接收到:${1}"
return 1
}

fun1 a
echo "fun1 返回值:$?"

echo "脚本参数:${1}"
----------------------------./fun1.sh aa
fun1 接收到:a
fun1 返回值:1
脚本参数:aa

》》》小练习2
1、脚本参数传 aa,bb
2、函数参数传 a
3、在函数内外打印 $0。$1,$2,$*,$@,$#,…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
----------------------------cat fun2.sh
#!/bin/bash
# 2020-11-27 11:48:22

fun1(){
echo "start1"
echo "\$0:$0"
echo "\$1:$1"
echo "\$2:$2"
echo "\$*:$*"
echo "\$@:$@"
echo "\$#:$#"
echo "end1"
return 1
}

fun1 a
echo -e "fun1 返回值:$? \n"

echo "start2"
echo "\$0:$0"
echo "\$1:$1"
echo "\$2:$2"
echo "\$*:$*"
echo "\$@:$@"
echo "\$#:$#"
echo "end2"
----------------------------./fun2.sh aa bb
start1
$0:./fun2.sh #===> 就 $0 输出一致
$1:a
$2
$*:a
$@:a
$#:1
end1
fun1 返回值:1

start2
$0:./fun2.sh
$1:aa
$2:bb
$*:aa bb
$@:aa bb
$#:2
end2

》》》查看/etc/init.d/netconsole {start|stop|status|restart|condrestart}的设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
----------------------------cat -n /etc/init.d/netconsole
...
34 usage ()
35 {
36 echo $"Usage: $0 {start|stop|status|restart|condrestart}" 1>&2
37 RETVAL=2
38 }
...
118
119
120 restart () #===> 重启 = 停止,开启
121 {
122 stop
123 start
124 }
125
126 condrestart ()
127 {
128 [ -e /var/lock/subsys/netconsole ] && restart
129 }
130
131 #===> 根据参数 1 的不同,去调用不同的函数
132 case "$1" in
133 stop) stop ;;
134 status) status ;;
135 start|restart|reload|force-reload) restart ;;
136 condrestart) condrestart ;;
137 *) usage ;;
138 esac
139
140 exit $RETVAL

while,until:循环

1
2
3
4
5
6
7
8
9
10
11
#===> while:当“条件成立执行循环”,直到条件不成立时才停止
while [ 条件判断式 ]
do
程序
done

#===> until:与 while 相反,“条件不成立执行循环”
until [ 条件判断式 ]
do
程序
done

》》》小练习
计算1 + 2 + 3 + ... + 100 = ?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
----------------------------cat while.sh
#!/bin/bash
# 2020-11-27 15:42:46

sum=0
i=0

while [ ${i} -le 100 ] #===> i <= 100
do
sum=$((${sum} + i))
i=$((${i}+1))
done

echo -e "1 + 2 + 3 + ... + 100 = ${sum}"
----------------------------cat until.sh
#!/bin/bash
# 2020-11-27 15:42:46

sum=0
i=0

until [ ${i} -gt 100 ] #===> i > 100
do
sum=$((${sum} + i))
i=$((${i}+1))
done

echo -e "1 + 2 + 3 + ... + 100 = ${sum}"
----------------------------./while.sh
1 + 2 + 3 + ... + 100 = 5050
----------------------------./until.sh
1 + 2 + 3 + ... + 100 = 5050

for:循环语法1

一行写法:for taskId in 30 ; do curl --location --request PUT "http://127.0.0.1:80/mgmt/taskCfg/stop/${taskId}" ; echo -e "\n"; done

1
2
3
4
5
#===> 语法 1(第一次循环 变量=值1,第二次循环 变量=值2,...)
for 变量 in 值1 值2 值3 ...
do
程序
done

》》》小练习1:依次循环输出 morning afternoon evening

1
2
3
4
5
6
7
8
9
10
11
12
13
----------------------------cat for1.sh
#!/bin/bash
# 2020-11-27 16:10:17

for time in morning afternoon evening
do
echo "${time}"
done

----------------------------./for1.sh
morning
afternoon
evening

》》》小练习2:输入三个参数,比较 $@,$* 的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
----------------------------cat for2.sh
#!/bin/bash
# 2020-11-27 16:13:00

i=0
for param in "$@" #===> 当做集合(加双引号),等同于 <==> for param in a b c "d e"
do
i=$((${i}+1))
echo "${i}${param}"
done

echo "++++++++++++++"

i=0
for param in "$*" #===> 当做整体(加双引号),等同于 <==> for param in "a b c d e"
do
i=$((${i}+1))
echo "${i}${param}"
done
----------------------------./for2.sh a b c "d e"
1:a
2:b
3:c
4:d e
++++++++++++++
1:a b c d e

》》》注意:上面是加了双引号的,若不加双引号,二者效果一致

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
----------------------------cat for2.sh
#!/bin/bash
# 2020-11-27 16:13:00

i=0
for param in $@ #===> 不加双引号,等同于 <==> for param in a b c d e
do
i=$((${i}+1))
echo "${i}${param}"
done

echo "++++++++++++++"

i=0
for param in $* #===> 不加双引号,等同于 <==> for param in a b c d e
do
i=$((${i}+1))
echo "${i}${param}"
done
----------------------------./for2.sh a b c "d e"
1:a
2:b
3:c
4:d
5:e
++++++++++++++
1:a
2:b
3:c
4:d
5:e

》》》小练习3:循环打印/etc/passwd的用户名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
----------------------------cat for3.sh
#!/bin/bash
# 2020-11-27 16:25:07

users=$(cat /etc/passwd | cut -d ':' -f 1)
i=0

for username in ${users}
do
i=$((${i}+1))
echo "${i}${username}"
done
----------------------------./for3.sh
1:root
2:bin
3:daemon
...
21:sshd

》》》小练习4:借助seq 命令计算1 + 2 + 3 + ... + 100 = ?

1
2
3
4
5
6
7
8
9
10
11
12
13
-----------------------cat for4.sh
#!/bin/bash
# 2020-11-27 17:05:39

sum=0
for n in $(seq 100)
do
sum=$((${sum}+n))
done

echo -e "1 + 2 + 3 + ... + 100 = ${sum}"
-----------------------./for4.sh
1 + 2 + 3 + ... + 100 = 5050

》》》小练习5:输入一个目录,显示里面所有档案的 rwx 权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-----------------------cat dir_perm.sh
#!/bin/bash
# 2020-11-27 17:11:10

read -p "Please input a directory path:" dir

if [ "${dir}" == "" ] || [ ! -d "${dir}" ];then
echo "The ${dir} is Not exists in your system."
exit 1
fi

for filename in $(ls ${dir})
do
file_path=${dir}/${filename}
permission=""
[ -r "${file_path}" ] && permission="r"
[ -w "${file_path}" ] && permission="${permission} w"
[ -x "${file_path}" ] && permission="${permission} x"
echo "The file ${file_path}'s permission is ${permission}"
done
-----------------------./dir_perm.sh
Please input a directory path:/etc/rc.d/init.d
The file /etc/rc.d/init.d/functions's permission is r w
The file /etc/rc.d/init.d/netconsole's permission is r w x
The file /etc/rc.d/init.d/network's permission is r w x
The file /etc/rc.d/init.d/README's permission is r w

for:循环语法2

1
2
3
4
5
#===> 语法 2(注意:for 是两个小括号)
for((初始值;循环控制条件;变量变化))
do
程序
done

》》》小练习:计算1 + 2 + 3 + ... + 100 = ?

1
2
3
4
5
6
7
8
9
10
11
12
13
-----------------------cat for5.sh
#!/bin/bash
# 2020-11-27 17:28:12

sum=0
for((i=1;i<=100;i=i+1))
do
sum=$((${sum}+${i}))
done

echo -e "1 + 2 + 3 + ... + 100 = ${sum}"
-----------------------./for5.sh
1 + 2 + 3 + ... + 100 = 5050

RANDOM:随机数

》》》Linux 中 $RANDOM 用于生成 [0,32767] 的随机数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#===> 生成 [0,32767] 的随机数:0,2^15-1
---------------------------echo $RANDOM
10001
---------------------------echo $RANDOM
23013
---------------------------echo $RANDOM
20119

#===> 生成随机字符串
---------------------------echo $RANDOM | md5sum
62abadf53d1bea813de068adc6b9dde8 -
---------------------------echo $RANDOM | md5sum
d579c8e6da35e0d5c92b462cb2d49005 -
---------------------------echo $RANDOM | md5sum
992436a2e3c561d6a41d2b9a2868a761 -
---------------------------echo $RANDOM | md5sum
cbdf7e5be0b460c4230fef64cd0631cf -

#===> 取 [0,999] 的随机数
---------------------------expr $RANDOM % 1000
790
---------------------------expr $RANDOM % 1000
602
---------------------------expr $RANDOM % 1000
946

#===> 取 [0,99] 的随机数
---------------------------expr $RANDOM % 100
33
---------------------------expr $RANDOM % 100
16
---------------------------expr $RANDOM % 100
92

#===> 取 [0,9] 的随机数
---------------------------expr $RANDOM % 10
0
---------------------------expr $RANDOM % 10
5
---------------------------expr $RANDOM % 10
8

shell script 的追踪与debug

语法检测(个人感觉没太大用啊)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[dmtsai@study ~]$ sh [-nvx] scripts.sh
选项与参数:
-n :不要执行script,仅查询语法的问题;
-v :再执行sccript 前,先将scripts 的内容输出到萤幕上;
-x :将使用到的script 内容显示到萤幕上,这是很有用的参数!

范例一:测试dir_perm.sh有无语法的问题?
[dmtsai@study ~]$ sh -n dir_perm.sh
#若语法没有问题,则不会显示任何资讯!

范例二:将show_animal.sh的执行过程全部列出来~
[dmtsai@study ~]$ sh -x show_animal.sh
+ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local /bin:/usr/local/sbin:/root/bin
+ export PATH
+ for animal in dog cat elephant
+ echo 'There are dogs.... '
There are dogs....
+ for animal in dog cat elephant
+ echo 'There are cats.... '
There are cats....
+ for animal in dog cat elephant
+ echo 'There are elephants.... '
There are elephants....

习题

whoami,pwd

请建立一支script ,当你执行该script 的时候,该script 可以显示:

1、你目前的身份(用whoami )
2、你目前所在的目录(用pwd)

1
2
3
4
5
6
7
8
9
----------------cat howami-pwd.sh
#!/bin/bash
# 2020-11-30 09:22:08

echo -e "当前用户:$(whoami)"
echo -e "当前目录:$(pwd)"
----------------./howami-pwd.sh
当前用户:root
当前目录:/app/test/sh/practice

几天生日

请自行建立一支程式,该程式可以用来计算『你还有几天可以过生日』啊?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
----------------cat birthday.sh
#!/bin/bash

read -p "下一次生日日期(YYYYmmdd):" input

now_day=$(date +%Y%m%d)
now_second=$(date --date="${now_day}" +%s)
input_second=$(date --date="${input}" +%s)
[ $? != 0 ] && exit #===> 输入日期错误
second=$((${input_second}-${now_second}))
day=$((second/24/60/60))

echo -e "距离下一次生日还有 ${day} 天"
----------------./birthday.sh
下一次生日日期(YYYYmmdd):20201215
距离下一次生日还有 18 天

累加数字

让使用者输入一个数字,程式可以由1+2+3… 一直累加到使用者输入的数字为止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
----------------cat sum.sh
#!/bin/bash
# 2020-11-30 10:07:27

read -p "请输入一个正确的 integer 数字:" number

sum=0
for((i=1;i<=${number};i=i+1))
do
sum=$((${sum}+${i}))
done

echo -e "1 + ... + ${number} = ${sum}"
----------------./sum.sh
请输入一个正确的 integer 数字:5
1 + ... + 5 = 15

判断档案

撰写一支程式,他的作用是:

1、先查看一下/root/test/logical 这个名称是否存在;
2、若不存在,则建立一个档案,使用touch 来建立,建立完成后离开;
3、如果存在的话,判断该名称是否为档案,若为档案则将之删除后建立一个目录,档名为logical ,之后离开;
4、如果存在的话,而且该名称为目录,则移除此目录!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
----------------cat ./check.sh
#!/bin/bash
# 2020-11-30 10:08:02

path=/root/test/logical

if [ -f ${path} ];then
rm -rf ${path}
mkdir ${path}
elif [ -d ${path} ];then
rm -rf ${path}
else
mkdir -p $(dirname ${path})
touch ${path}
fi
----------------ll -d /root/test/logical
ls: 无法访问/root/test/logical: 没有那个文件或目录
----------------./check.sh #===> 不存在,新建文件
----------------ll -d /root/test/logical
-rw-r--r--. 1 root root 0 11月 27 05:10 /root/test/logical
----------------
----------------
----------------./check.sh #===> 存在文件,删除文件,新建同名目录
----------------ll -d /root/test/logical
drwxr-xr-x. 2 root root 6 11月 27 05:10 /root/test/logical
----------------
----------------
----------------
----------------./check.sh #===> 存在目录,删除目录
----------------ll -d /root/test/logical
ls: 无法访问/root/test/logical: 没有那个文件或目录

分割 /etc/passwd

我们知道/etc/passwd 里面以: 冒号来分隔,第一栏为帐号名称。
请写一只程式,可以将/etc/passwd 的第一栏取出,而且每一栏都以一行字串『The 1 account is “root” 』来显示,那个1 表示行数。
》》》方式1:awk NR 记录行号

1
2
3
4
5
6
7
8
9
10
11
----------------cat passwd.sh
#!/bin/bash
# 2020-11-30 10:39:26

cat /etc/passwd | cut -d ":" -f 1 | awk '{print "The " NR " account is \"" $1 "\""}'
----------------./passwd.sh
The 1 account is "root"
The 2 account is "bin"
The 3 account is "daemon"
...省略
The 21 account is "sshd"

》》》方式2:定义变量,for 循环 ++ 记录行号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
----------------cat passwd2.sh
#!/bin/bash
# 2020-11-30 10:44:12

users=$(cat /etc/passwd | cut -d ":" -f 1)
row_num=0

for username in ${users} ; do
row_num=$((${row_num}+1))
echo -e "The ${row_num} account is \"${username}\""
done
----------------./passwd2.sh
The 1 account is "root"
The 2 account is "bin"
The 3 account is "daemon"
...省略
The 21 account is "sshd"

useradd:添加用户

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

[root@study ~]# useradd [-u UID] [-g初始群组] [-G次要群组] [-mM]\
> [-c说明栏] [-d家目录绝对路径] [- s shell]使用者帐号名
选项与参数:
-u :后面接的是UID ,是一组数字。直接指定一个特定的UID 给这个帐号;
-g :后面接的那个群组名称就是我们上面提到的initial group 啦~
该群组的GID 会被放置到/etc/passwd 的第四个栏位内。
-G :后面接的群组名称则是这个帐号还可以加入的群组。
这个选项与参数会修改/etc/group 内的相关资料喔!
-M :强制!不要建立使用者家目录!(系统帐号预设值)
-m :强制!要建立使用者家目录!(一般帐号预设值)
-c :这个就是/etc/passwd 的第五栏的说明内容啦~可以随便我们设定的啦~
-d :指定某个目录成为家目录,而不要使用预设值。务必使用绝对路径!
-r :建立一个系统的帐号,这个帐号的UID 会有限制(参考/etc/login.defs)
-s :后面接一个shell ,若没有指定则预设是/bin/bash 的啦~
-e :后面接一个日期,格式为『YYYY-MM-DD』此项目可写入shadow 第八栏位,
亦即帐号失效日的设定项目啰;
-f :后面接shadow 的第七栏位项目,指定密码是否会失效。0为立刻失效,
-1 为永远不失效(密码只会过期而强制于登入时重新设定而已。)
1
2
3
4
5
6
7
8
----------------useradd taopanfeng
drwx------. 2 taopanfeng taopanfeng 59 11月 27 06:30 taopanfeng
----------------ll -d /home/taopanfeng/
drwx------. 2 taopanfeng taopanfeng 59 11月 27 06:30 /home/taopanfeng/
----------------grep taopanfeng /etc/passwd /etc/shadow /etc/group
/etc/passwd:taopanfeng:x:1000:1000::/home/taopanfeng:/bin/bash
/etc/shadow:taopanfeng:!!:18592:0:99999:7:::
/etc/group:taopanfeng:x:1000:

其实系统已经帮我们规定好非常多的预设值了,所以我们可以简单的使用『 useradd 帐号』来建立使用者即可。
CentOS 这些预设值主要会帮我们处理几个项目:
1、在/etc/passwd 里面建立一行与帐号相关的资料,包括建立UID/GID/家目录等;
2、在/etc/shadow 里面将此帐号的密码相关参数填入,但是尚未有密码;
3、在/etc/group 里面加入一个与帐号名称一模一样的群组名称;
4、在/home 底下建立一个与帐号同名的目录作为使用者家目录,且权限为700

使用useradd 建立使用者帐号时,其实会更改不少地方,至少我们就知道底下几个档案:
1、使用者帐号与密码参数方面的档案:/etc/passwd, /etc/shadow
2、使用者群组相关方面的档案:/etc/group, /etc/gshadow
3、使用者的家目录:/home/帐号名称

passwd:设置密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@study ~]# passwd [--stdin] [帐号名称]   <==所有人均可使用来改自己的密码
[root@study ~]# passwd [-l] [-u] [--stdin] [-S] \
> [-n日数] [-x日数] [-w日数] [-i日数]帐号 <==root功能
选项与参数:
--stdin :可以透过来自前一个管线的资料,作为密码输入,对shell script 有帮助!
-l :是Lock 的意思,会将/etc/shadow 第二栏最前面加上! 使密码失效;
-u :与-l 相对,是Unlock 的意思!
-S :列出密码相关参数,亦即shadow 档案内的大部分资讯。
-n :后面接天数,shadow 的第4 栏位,多久不可修改密码天数
-x :后面接天数,shadow 的第5 栏位,多久内必须要更动密码
-w :后面接天数,shadow 的第6 栏位,密码过期前的警告天数
-i :后面接天数,shadow 的第7 栏位,密码失效天数


----------------passwd taopanfeng
更改用户 taopanfeng 的密码 。
新的 密码: #===> 设置密码为 123
无效的密码: 密码少于 8 个字符 #===> 给予了提示
重新输入新的 密码: #===> 仍旧确认
passwd:所有的身份验证令牌已经成功更新。 #===> 更新成功了~!!!

-------------------echo "456" | passwd --stdin taopanfeng #===> 直接修改密码为“456”
更改用户 taopanfeng 的密码 。
passwd:所有的身份验证令牌已经成功更新。

usermod:修改用户

如果你仔细的比对,会发现usermod的选项与useradd非常类似!这是因为usermod也是用来微调useradd增加的使用者参数嘛!
不过usermod还是有新增的选项,那就是-L与-U ,不过这两个选项其实与passwd的-l, -u是相同的!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
[root@study ~]# usermod [-cdegGlsuLU] username
选项与参数:
-c :后面接帐号的说明,即/etc/passwd 第五栏的说明栏,可以加入一些帐号的说明。
-d :后面接帐号的家目录,即修改/etc/passwd 的第六栏;
-e :后面接日期,格式是YYYY-MM-DD 也就是在/etc/shadow 内的第八个栏位资料啦!
-f :后面接天数,为shadow 的第七栏位。
-g :后面接初始群组,修改/etc/passwd 的第四个栏位,亦即是GID 的栏位!
-G :后面接次要群组,修改这个使用者能够支援的群组,修改的是/etc/group 啰~
-a :与-G 合用,可『增加次要群组的支援』而非『设定』喔!
-l :后面接帐号名称。亦即是修改帐号名称, /etc/passwd 的第一栏!
-s :后面接Shell 的实际档案,例如/bin/bash 或/bin/csh 等等。
-u :后面接UID 数字啦!即/etc/passwd 第三栏的资料;
-L :暂时将使用者的密码冻结,让他无法登入。其实仅改/etc/shadow 的密码栏。
-U :将/etc/shadow 密码栏的! 拿掉,解冻啦!





范例一:修改使用者vbird2的说明栏,加上『VBird's test』的说明。
[root@study ~]# usermod -c "VBird's test" vbird2
[root@study ~]# grep vbird2 /etc/passwd
vbird2:x:1500:100: VBird's test :/home/vbird2:/bin/bash

范例二:使用者vbird2这个帐号在2015/12/31失效。
[root@study ~]# usermod -e "2015-12-31" vbird2
[root@study ~]# chage -l vbird2 | grep 'Account expires'
Account expires : Dec 31, 2015

范例三:我们建立vbird3这个系统帐号时并没有给予家目录,请建立他的家目录
[root@study ~]# ll -d ~vbird3
ls: cannot access /home/vbird3: No such file or directory <= =确认一下,确实没有家目录的存在!
[root@study ~]# cp -a /etc/skel /home/vbird3
[root@study ~]# chown -R vbird3:vbird3 /home/vbird3
[root@study ~]# chmod 700 /home/vbird3
[root @study ~]# ll -a ~vbird3
drwx------. 3 vbird3 vbird3 74 May 4 17:51 . <==使用者家目录权限
drwxr-xr-x. 10 root root 4096 Jul 20 22:51 ..
-rw-r--r--. 1 vbird3 vbird3 18 Mar 6 06:06 .bash_logout
-rw-r--r--. 1 vbird3 vbird3 193 Mar 6 06:06 .bash_profile
-rw-r--r--. 1 vbird3 vbird3 231 Mar 6 06:06 .bashrc
drwxr-xr-x. 4 vbird3 vbird3 37 May 4 17:51 .mozilla
# 使用chown -R 是为了连同家目录底下的使用者/群组属性都一起变更的意思;
# 使用chmod 没有-R ,是因为我们仅要修改目录的权限而非内部档案的权限!

范例三:给用户再添加一个组
[root@localhost ~]# id taopanfeng
uid=1000(taopanfeng) gid=1000(taopanfeng) 组=1000(taopanfeng) #===> 一个组
[root@localhost ~]#
[root@localhost ~]#
[root@localhost ~]# usermod -a -G wheel taopanfeng #===> 给用户 taopanfeng 添加组 wheel
[root@localhost ~]#
[root@localhost ~]# id taopanfeng
uid=1000(taopanfeng) gid=1000(taopanfeng) 组=1000(taopanfeng),10(wheel) #===> 两个组

userdel:删除用户

这个功能就太简单了,目的在删除使用者的相关资料,而使用者的资料有:
1、使用者帐号/密码相关参数:/etc/passwd, /etc/shadow
2、使用者群组相关参数:/etc/group, /etc/gshadow
3、使用者个人档案资料: /home/username, /var/spool/mail/username..

1
2
3
4
5
6
[root@study ~]# userdel [-r] username
选项与参数:
-r :连同使用者的家目录也一起删除

范例一:删除vbird2 ,连同家目录一起删除
[root@study ~]# userdel -r vbird2

如果想要完整的将某个帐号完整的移除,最好可以在下达userdel -r username之前,
先以『 find / -user username 』查出整个系统内属于username的档案,然后再加以删除吧!

id:查看用户

id 这个指令则可以查询某人或自己的相关UID/GID 等等的资讯,他的参数也不少,不过,都不需要记~反正使用id 就全部都列出啰!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

[root@study ~]# id [username]

范例一:查阅root自己的相关ID资讯!
[root@study ~]# id
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:
s0-s0:c0.c1023
# 上面资讯其实是同一行的资料!包括会显示UID/GID 以及支援的所有群组!
# 至于后面那个context=... 则是SELinux 的内容,先不要理会他!

范例二:查阅一下vbird1吧~
[root@study ~]# id vbird1 #===> 查看加入了哪些组
uid=1003(vbird1) gid=1004(vbird1) groups=1004(vbird1)

[root@study ~]# id vbird100
id: vbird100: No such user <== id这个指令也可以用来判断系统上面有无某帐号!

finger:查看用户信息

通过yum -y install finger安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[root@study ~]# finger [-s] username
选项与参数:
-s :仅列出使用者的帐号、全名、终端机代号与登入时间等等;
-m :列出与后面接的帐号相同者,而不是利用部分比对(包括全名部分)

[root@localhost 13]# finger
Login Name Tty Idle Login Time Office Office Phone Host
root root pts/0 47 Nov 27 09:37 (192.168.1.8)
root root pts/2 Nov 27 06:50 (192.168.1.8)
[root@localhost 13]# finger taopanfeng
Login: taopanfeng Name:
Directory: /home/taopanfeng Shell: /bin/bash
Last login 五 11月 27 09:41 (CST) on pts/0
No mail.
No Plan.
[root@localhost 13]# finger root
Login: root Name: root
Directory: /root Shell: /bin/bash
On since 五 11月 27 09:37 (CST) on pts/0 from 192.168.1.8
47 minutes 13 seconds idle
On since 五 11月 27 06:50 (CST) on pts/2 from 192.168.1.8
1 second idle
New mail received 五 11月 27 09:16 2020 (CST)
Unread since 五 11月 27 08:44 2020 (CST)
No Plan.

Login:为使用者帐号,亦即/etc/passwd 内的第一栏位;
Name:为全名,亦即/etc/passwd 内的第五栏位(或称为注解);
Directory:就是家目录了;
Shell:就是使用的Shell 档案所在;
Never logged in.:figner 还会调查使用者登入主机的情况喔!
No mail.:调查/var/spool/mail 当中的信箱资料;
No Plan.:调查~vbird1/.plan 档案,并将该档案取出来说明!

groupadd:添加群组

1
2
3
4
5
6
7
8
9
10
11

[root@study ~]# groupadd [-g gid] [-r]群组名称
选项与参数:
-g :后面接某个特定的GID ,用来直接给予某个GID ~
-r :建立系统群组啦!与/etc/login.defs内的GID_MIN有关。

范例一:新建一个群组,名称为group1
[root@study ~]# groupadd group1
[root@study ~]# grep group1 /etc/group /etc/gshadow
/etc/group:group1:x: 1503 :
/etc /gshadow:group1:!:: #群组的GID也是会由1000以上最大GID+1来决定!

groupmod:修改群组

跟usermod类似的,这个指令仅是在进行group相关参数的修改而已。

1
2
3
4
5
6
7
8
9
10
11

[root@study ~]# groupmod [-g gid] [-n group_name]群组名
选项与参数:
-g :修改既有的GID数字;
-n :修改既有的群组名称

范例一:将刚刚上个指令建立的group1名称改为mygroup , GID为201
[root@study ~]# groupmod -g 201 -n mygroup group1
[root@study ~]# grep mygroup /etc/group /etc/gshadow
/etc/group :mygroup:x: 201 :
/etc/gshadow:mygroup:!::

groupdel:删除群组

1
2
3
4
5
6
7
8
9

[root@study ~]# groupdel [groupname]

范例一:将刚刚的mygroup删除!
[root@study ~]# groupdel mygroup

范例二:若要删除vbird1这个群组的话?
[root@study ~]# groupdel vbird1
groupdel: cannot remove the primary group of user 'vbird1'

为什么mygroup可以删除,但是vbird1就不能删除呢?
原因很简单,『有某个帐号(/etc/passwd)的initial group使用该群组!』如果查阅一下,你会发现在/etc/passwd内的vbird1第四栏的GID就是/etc/group内的vbird1那个群组的GID ,
所以啰,当然无法删除~否则vbird1这个使用者登入系统后,就会找不到GID ,那可是会造成很大的困扰的!那么如果硬要删除vbird1这个群组呢?
你『必须要确认/etc/passwd内的帐号没有任何人使用该群组作为initial group』才行喔!

所以,你可以:
、修改vbird1 的GID ,或者删除vbird1 这个使用者。

gpasswd:群组管理员功能

如果系统管理员太忙碌了,导致某些帐号想要加入某个专案时找不到人帮忙!这个时候可以建立『群组管理员』喔!

什么是群组管理员呢?就是让某个群组具有一个管理员,这个群组管理员可以管理哪些帐号可以加入/移出该群组!

那要如何『建立一个群组管理员』呢?就得要透过gpasswd 啰!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

#关于系统管理员(root)做的动作:
[root@study ~]# gpasswd groupname
[root@study ~]# gpasswd [-A user1,...] [-M user3,...] groupname
[root @study ~]# gpasswd [-rR] groupname
选项与参数:
:若没有任何参数时,表示给予groupname一个密码(/etc/gshadow)
-A :将groupname的主控权交由后面的使用者管理(该群组的管理员)
-M :将某些帐号加入这个群组当中!
-r :将groupname的密码移除
-R :让groupname的密码栏失效

#关于群组管理员(Group administrator)做的动作:
[someone@study ~]$ gpasswd [-ad] user groupname
选项与参数:
-a :将某位使用者加入到groupname这个群组当中!
-d :将某位使用者移除出groupname这个群组当中。

范例一:建立一个新群组,名称为testgroup且群组交由vbird1管理:
[root@study ~]# groupadd testgroup <==先建立群组
[root@study ~]# gpasswd testgroup <==给这个群组一个密码吧!
Changing the password for group testgroup
New Password:
Re-enter new password: #输入两次密码就对了!
[root@study ~]# gpasswd -A vbird1 testgroup <==加入群组管理员为vbird1
[root@study ~]# grep testgroup /etc/group /etc/gshadow
/etc/gshadow:testgroup: $6$MnmChP3D$ mrUn.Vo.buDjObMm8F2emTkvGSeuWikhRzaKHxpJ...:vbird1: #很有趣吧!此时vbird1则拥有testgroup的主控权喔!身份有点像板主啦!范例二:以vbird1登入系统,并且让他加入vbird1, vbird3成为testgroup成员
[vbird1@study ~]$ id
uid=1003(vbird1) gid=1004(vbird1) groups=1004(vbird1) ... #看得出来,vbird1尚未加入testgroup群组喔!
[vbird1@study ~]$

/etc/group:testgroup:x:1503:




gpasswd -a vbird1 testgroup
[vbird1@study ~]$ gpasswd -a vbird3 testgroup
[vbird1@study ~]$ grep testgroup /etc/group
testgroup:x:1503: vbird1,vbird3

setfacl,getfacl

省略学习

su:身份切换

1
2
3
4
5
6
7
[root@study ~]# su [-lm] [-c指令] [username]
选项与参数:
- :单纯使用-如『 su - 』代表使用login-shell的变数档案读取方式来登入系统;
若使用者名称没有加上去,则代表切换为root的身份。
-l :与-类似,但后面需要加欲切换的使用者帐号!也是login-shell的方式。
-m :-m与-p是一样的,表示『使用目前的环境设定,而不读取新使用者的设定档』
-c :仅进行一次指令,所以-c后面可以加上指令喔!

上表的解释当中有出现之前第十章谈过的login-shell设定档读取方式,如果你忘记那是啥东西,请先回去第十章瞧瞧再回来吧!
这个su的用法当中,有没有加上那个减号『 - 』差很多喔!
因为涉及login-shell与non-login shell的变数读取方法。这里让我们以一个小例子来说明吧!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

范例一:假设你原本是dmtsai的身份,想要使用non-login shell的方式变成root
[ dmtsai @study ~]$ su <==注意提示字元,是dmtsai的身份喔!
Password: <==这里输入root的密码喔!
[root@study dmtsai ]# id <==提示字元的目录是dmtsai喔!
uid=0(root) gid=0(root) groups=0(root) context=unconf.... <==确实是root的身份!
[root@study dmtsai]# env | grep 'dmtsai'
USER=dmtsai <==竟然还是dmtsai这家伙!
PATH=...:/home/dmtsai/.local/bin:/home/dmtsai/bin <==这个影响最大!
MAIL=/var/spool/mail/dmtsai <==收到的mailbox是vbird1
PWD=/home/dmtsai <==并非root的家目录
LOGNAME=dmtsai
# 虽然你的UID 已经是具有root 的身份,但是看到上面的输出讯息吗?
#还是有一堆变数为原本dmtsai的身份,所以很多资料还是无法直接利用。
[root@study dmtsai]# exit <==这样可以离开su的环境!

单纯使用『 su 』切换成为root的身份,读取的变数设定方式为non-login shell的方式,
这种方式很多原本的变数不会被改变,尤其是我们之前谈过很多次的PATH这个变数,由于没有改变成为root的环境,
因此很多root惯用的指令就只能使用绝对路径来执行咯。其他的还有MAIL这个变数,你输入mail时,收到的邮件竟然还是dmtsai的,而不是root本身的邮件!
是否觉得很奇怪啊!所以切换身份时,请务必使用如下的范例二:

1
2
3
4
5
6
7
8
9
10
11
12
13
范例二:使用login shell的方式切换为root的身份并观察变数
[dmtsai@study ~]$ su -
Password: <==这里输入root的密码喔!

[root@study ~]# env | grep root
USER=root
MAIL=/var/spool/mail/root
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
PWD=/root
HOME=/root
LOGNAME=root
#了解差异了吧?下次变换成为root时,记得最好使用su -喔!
[root@study ~]# exit <==这样可以离开su的环境!

上述的作法是让使用者的身份变成root 并开始操作系统,如果想要离开root 的身份则得要利用exit 离开才行。
那我如果只是想要执行『一个只有root 才能进行的指令,且执行完毕就恢复原本的身份』呢?
那就可以加上-c 这个选项啰!请参考底下范例三!

1
2
3
4
5
6
7
8
9
10
11
范例三:dmtsai想要执行『 head -n 3 /etc/shadow 』一次,且已知root密码
[dmtsai@study ~]$ head -n 3 /etc/shadow
head: cannot open `/etc/shadow' for reading: Permission denied

[dmtsai@study ~]$ su - -c "head -n 3 /etc/shadow"
Password: <==这里输入root的密码喔!
root:$6$wtbCCce/PxMeE5wm$KE2IfSJr.YLP7Rcai6oa/T7KFhOYO62vDnqfLw85...:16559:0:99999:7:::
bin:*:16372:0:99999:7:::
daemon:*:16372:0:99999:7:::

[dmtsai@study ~]$ <==注意看,身份还是dmtsai喔!继续使用旧的身份进行系统操作!

由于/etc/shadow 权限的关系,该档案仅有root 可以查阅。
为了查阅该档案,所以我们必须要使用root 的身份工作。但我只想要进行一次该指令而已,此时就使用类似上面的语法吧!
好,那接下来,如果我是root 或者是其他人, 想要变更成为某些特殊帐号,可以使用如下的方法来切换喔!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
范例四:原本是dmtsai这个使用者,想要变换身份成为vbird1时?
[dmtsai@study ~]$ su -l vbird1
Password: <==这里输入vbird1的密码喔!
[ vbird1 @study ~]$ su -
Password: <==这里输入root的密码喔!
[ root @study ~]# id sshd
uid=74(sshd) gid=74(sshd) groups=74(sshd) ... <==确实有存在此人
[root@study ~]# su -l sshd
This account is currently not available. <==竟然说此人无法切换?
[root@study ~]# finger sshd
Login: sshd Name: Privilege-separated SSH
Directory: /var/empty/sshd Shell: /sbin/nologin
[root@study ~]# exit <==离开第二次的su
[vbird1@study ~]$ exit <==离开第一次的su
[dmtsai @study ~]$ exit <==这才是最初的环境!

su 就这样简单的介绍完毕,总结一下他的用法是这样的:
1、若要完整的切换到新使用者的环境,必须要使用『 su - username 』或『 su -l username 』, 才会连同PATH/USER/MAIL 等变数都转成新使用者的环境;
2、如果仅想要执行一次root 的指令,可以利用『 su - -c “指令串” 』的方式来处理;
3、使用root 切换成为任何使用者时,并不需要输入新使用者的密码;

虽然使用su很方便啦,不过缺点是,当我的主机是多人共管的环境时,如果大家都要使用su来切换成为root的身份,那么不就每个人都得要知道root的密码,这样密码太多人知道可能会流出去,很不妥当呢!怎办?透过sudo来处理即可!

sudo:root执行

除非是信任用户,否则一般用户预设是不能操作sudo 的喔!
由于一开始系统预设仅有root可以执行sudo,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@study ~]# sudo [-b] [-u新使用者帐号]
选项与参数:
-b :将后续的指令放到背景中让系统自行执行,而不与目前的shell 产生影响
-u :后面可以接欲切换的使用者,若无此项则代表切换身份为root 。

范例一:你想要以sshd的身份在/tmp底下建立一个名为mysshd的档案
[root@study ~]# sudo -u sshd touch /tmp/mysshd
[root@study ~]# ll /tmp/mysshd
- rw-r--r--. 1 sshd sshd 0 Jul 21 23:37 /tmp/mysshd
#特别留意,这个档案的权限是由sshd所建立的情况喔!

范例二:你想要以vbird1的身份建立~vbird1/www并于其中建立index.html档案
[root@study ~]# sudo -u vbird1 sh -c "mkdir ~vbird1/www; cd ~vbird1/www; \
> echo 'This is index.html file' > index.html"
[root@study ~]# ll -a ~vbird1/www
drwxr-xr-x. 2 vbird1 vbird1 23 Jul 21 23:38 .
drwx------. 6 vbird1 vbird1 4096 Jul 21 23:38 ..
-rw-r--r--. 1 vbird1 vbird1 24 Jul 21 23:38 index.html
# 要注意,建立者的身份是vbird1 ,且我们使用sh -c "一串指令" 来执行的!

sudo 可以让你切换身份来进行某项任务,例如上面的两个范例。范例一中,我们的root 使用sshd 的权限去进行某项任务!
要注意,因为我们无法使用『 su - sshd 』去切换系统帐号(因为系统帐号的shell 是/sbin/nologin), 这个时候sudo 真是他X 的好用了!
立刻以sshd 的权限在/tmp 底下建立档案!查阅一下档案权限你就了解意义啦!
至于范例二则更使用多重指令串(透过分号; 来延续指令进行),使用sh -c 的方法来执行一连串的指令, 如此真是好方便!

但是sudo 预设仅有root 能使用啊!为什么呢?因为sudo 的执行是这样的流程:
1、当使用者执行sudo 时,系统于/etc/sudoers 档案中搜寻该使用者是否有执行sudo 的权限;
2、若使用者具有可执行sudo 的权限后,便让使用者『输入使用者自己的密码』来确认;
3、若密码输入成功,便开始进行sudo 后续接的指令(但root 执行sudo 时,不需要输入密码);
4、若欲切换的身份与执行者身份相同,那也不需要输入密码。

所以说,sudo执行的重点是:『能否使用sudo必须要看/etc/sudoers的设定值,而可使用sudo者是透过输入使用者自己的密码来执行后续的指令串』喔!
由于能否使用与/etc/sudoers有关,所以我们当然要去编辑sudoers档案啦!
不过,因为该档案的内容是有一定的规范的,因此直接使用vi去编辑是不好的。此时,我们得要透过visudo去修改这个档案喔!

visudo 与/etc/sudoers
使用 visudo 来对 /etc/sudoers 文件编辑

1、添加用户,使之可使用 sudo 命令

1
2
3
4
5
[root@study ~]# visudo 
....(前面省略)....
root ALL=(ALL) ALL <==找到这一行,大约在98行左右
taopanfeng ALL=(ALL) ALL <==这一行是你要新增的! 添加用户 taopanfeng
....(底下省略)....

修改之后,taopanfeng 用户就可以使用 sudo 命令了。

1
2
3
4
5
6
7
[root@localhost ~]# su taopanfeng
[taopanfeng@localhost root]$ sudo head -n 3 /etc/shadow
[sudo] password for taopanfeng: #===> 输入 taopanfeng 的密码,而不是 root
root:$6$2YkSvs.5fQRTXxX7$5/boPraToML3eeZy7FxDe3vbwShbyIRgutHsmFw9qFKpYaHdRxbtgX1.lRC33EguAvypuwI9k.WyfdiAjOPSx/::0:99999:7:::
bin:*:16659:0:99999:7:::
daemon:*:16659:0:99999:7:::
[taopanfeng@localhost root]$
1
root ALL=(ALL) ALL    <==这是预设值

上面这一行的四个值分别是什么意思?:
1、『使用者帐号』:系统的哪个帐号可以使用sudo 这个指令的意思;
2、『登入者的来源主机名称』:当这个帐号由哪部主机连线到本Linux 主机,意思是这个帐号可能是由哪一部网路主机连线过来的, 这个设定值可以指定用户端电脑(信任的来源的意思)。预设值root 可来自任何一部网路主机
3、『(可切换的身份)』:这个帐号可以切换成什么身份来下达后续的指令,预设root 可以切换成任何人;
4、『可下达的指令』:可用该身份下达什么指令?这个指令请务必使用绝对路径撰写。预设root可以切换任何身份且进行任何指令之意。

注:那个ALL 是特殊的关键字,代表任何身份、主机或指令的意思。

2、利用 wheel 群组以及 NOPASSWD 免密码的功能处理visudo

》》》免密登录

1
2
3
4
5
6
7
8
9
     98 root    ALL=(ALL)       ALL
99 taopanfeng ALL=(ALL) NOPASSWD:ALL

[root@localhost ~]# su taopanfeng
[taopanfeng@localhost root]$ sudo head -n 3 /etc/shadow #===> 输入命令之后,无需输入密码
root:$6$2YkSvs.5fQRTXxX7$5/boPraToML3eeZy7FxDe3vbwShbyIRgutHsmFw9qFKpYaHdRxbtgX1.lRC33EguAvypuwI9k.WyfdiAjOPSx/::0:99999:7:::
bin:*:16659:0:99999:7:::
daemon:*:16659:0:99999:7:::
[taopanfeng@localhost root]$

》》》利用 wheel 群组,免密登录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
     98 root    ALL=(ALL)       ALL
99 taopanfeng ALL=(ALL) ALL
106 %wheel ALL=(ALL) NOPASSWD:ALL
#在最左边加上% ,代表后面接的是一个『群组』之意!

[root@localhost ~]# id taopanfeng
uid=1000(taopanfeng) gid=1000(taopanfeng) 组=1000(taopanfeng) #===> 一个组
[root@localhost ~]#
[root@localhost ~]# usermod -a -G wheel taopanfeng #===> 给用户 taopanfeng 添加组 wheel
[root@localhost ~]#
[root@localhost ~]# id taopanfeng
uid=1000(taopanfeng) gid=1000(taopanfeng) 组=1000(taopanfeng),10(wheel) #===> 两个组
[root@localhost ~]#
[root@localhost ~]# su taopanfeng
[taopanfeng@localhost root]$ sudo head -n 3 /etc/shadow #===> 无需输入密码
root:$6$2YkSvs.5fQRTXxX7$5/boPraToML3eeZy7FxDe3vbwShbyIRgutHsmFw9qFKpYaHdRxbtgX1.lRC33EguAvypuwI9k.WyfdiAjOPSx/::0:99999:7:::
bin:*:16659:0:99999:7:::
daemon:*:16659:0:99999:7:::
[taopanfeng@localhost root]$

3、有限制的命令操作

1
2
[root@study ~]# visudo   <==注意是root身份
myuser1 ALL=(root) /usr/bin/passwd <==之前已经说过了,最后指令务必用绝对路径

上面的设定值指的是『myuser1可以切换成为root使用passwd这个指令』的意思。
其中要注意的是: 指令栏位必须要填写绝对路径才行!否则visudo会出现语法错误的状况发生!

1
2
3
4
5
6
7
8
9
10

[myuser1@study ~]$ sudo passwd myuser3 <==注意,身份是myuser1
[sudo] password for myuser1: <==输入myuser1的密码
Changing password for user myuser3. <==底下改的是myuser3的密码喔!这样是正确的
New password:
Retype new password:
passwd: all authentication tokens updated successfully.

[myuser1@study ~]$ sudo passwd
Changing password for user root. <==见鬼!怎么会去改root的密码?

恐怖啊!我们竟然让root 的密码被myuser1 给改变了!

1
2
3

[root@study ~]# visudo <==注意是root身份
myuser1 ALL=(root) !/usr/bin/passwd, /usr/bin/passwd [A-Za-z]*, !/usr/bin/ passwd root

在设定值中加上惊叹号『 ! 』代表『不可执行』的意思。
因此上面这一行会变成:可以执行『 passwd 任意字元』,但是『 passwd 』与『 passwd root 』这两个指令例外!如此一来myuser1 就无法改变root 的密码了!
这样这位使用者可以具有root 的能力帮助你修改其他用户的密码, 而且也不能随意改变root 的密码!很有用处的!

4、透过别名建置visudo
省略学习

w,who,whoami,last,lastlog

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
---------------w #===> 谁在线上
# 目前的时间、开机(up) 多久,几个使用者,在系统上平均负载
10:41:39 up 1 day, 6:08, 2 users, load average: 0.00, 0.01, 0.05
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 192.168.1.8 09:37 59:39 0.05s 0.01s bash
root pts/2 192.168.1.8 06:50 3.00s 0.30s 0.00s w
---------------who:谁在线上
root pts/0 2020-11-27 09:37 (192.168.1.8)
root pts/2 2020-11-27 06:50 (192.168.1.8)
---------------whoami
root
---------------last #===> 每个帐号的最近登入的时间
root pts/0 192.168.1.8 Fri Nov 27 09:37 still logged in
root pts/0 192.168.1.8 Fri Nov 27 09:14 - 09:36 (00:22)
...省略
root pts/1 192.168.1.8 Tue Nov 3 20:01 - 02:03 (06:02)
root pts/0 192.168.1.8 Tue Nov 3 19:41 - 21:53 (02:11)
root pts/0 192.168.1.8 Tue Nov 3 19:41 - 19:41 (00:00)
root pts/0 192.168.1.8 Tue Nov 3 19:12 - 19:41 (00:28)
root tty1 Tue Nov 3 18:58 - crash (5+21:42)
root tty1 Tue Nov 3 18:58 - crash (5+21:42)
reboot system boot 3.10.0-327.el7.x Tue Nov 3 18:58 - 10:41 (23+15:43)

wtmp begins Tue Nov 3 18:58:23 2020
---------------lastlog #===> lastlog 会去读取/var/log/lastlog 档案
用户名 端口 来自 最后登陆时间
root pts/0 192.168.1.8 五 11月 27 09:37:04 +0800 2020
bin **从未登录过**
...省略
sshd **从未登录过**
taopanfeng pts/0 五 11月 27 09:41:59 +0800 2020

quota:磁碟配额

Quota 是什么?

在Linux 系统中,由于是多人多工的环境,所以会有多人共同使用一个硬碟空间的情况发生, 如果其中有少数几个使用者大量的占掉了硬碟空间的话,那势必压缩其他使用者的使用权力!因此管理员应该适当的限制硬碟的容量给使用者,以妥善的分配系统资源!避免有人抗议呀!

举例来说,我们使用者的预设家目录都是在/home 底下,如果/home 是个独立的partition , 假设这个分割槽有10G 好了,而/home 底下共有30 个帐号,也就是说,每个使用者平均应该会有333MB 的空间才对。偏偏有个使用者在他的家目录底下塞了好多只影片,占掉了8GB 的​​空间,想想看,是否造成其他正常使用者的不便呢?如果想要让磁碟的容量公平的分配,这个时候就得要靠quota 的帮忙啰!

Quota 的一般用途

quota 比较常使用的几个情况是:
、针对WWW server ,例如:每个人的网页空间的容量限制!
、针对mail server,例如:每个人的邮件空间限制。
、针对file server,例如:每个人最大的可用网路硬碟空间(教学环境中最常见!)

上头讲的是针对网路服务的设计,如果是针对Linux 系统主机上面的设定那么使用的方向有底下这一些:
1、限制某一群组所能使用的最大磁碟配额(使用群组限制):
你可以将你的主机上的使用者分门别类,有点像是目前很流行的付费与免付费会员制的情况,你比较喜好的那一群的使用配额就可以给高一些!呵呵!^_^…

2、限制某一使用者的最大磁碟配额(使用使用者限制):
在限制了群组之后,你也可以再继续针对个人来进行限制,使得同一群组之下还可以有更公平的分配!

3、限制某一目录(directory, project)的最大磁碟配额:
在旧版的CentOS当中,使用的预设档案系统为EXT家族,这种档案系统的磁碟配额主要是针对整个档案系统来处理,所以大多针对『挂载点』进行设计。新的xfs可以使用project这种模式,就能够针对个别的目录(非档案系统喔)来设计磁碟配额耶!超棒的!

基本上,quota 就是在回报管理员磁碟使用率以及让管理员管理磁碟使用情况的一个工具就是了!
比较特别的是,XFS 的quota 是整合到档案系统内,并不是其他外挂的程式来管理的。
因此,透过quota 来直接回报磁碟使用率,要比unix 工具来的快速!
举例来说, du 这东西会重新计算目录下的磁碟使用率,但xfs 可以透过xfs_quota 来直接回报各目录使用率,速度上是快非常多!

Quota 的使用限制

1、在EXT档案系统家族仅能针对整个filesystem:
EXT档案系统家族在进行quota限制的时候,它仅能针对整个档案系统来进行设计,无法针对某个单一的目录来设计它的磁碟配额。
因此,如果你想要使用不同的档案系统进行quota时,请先搞清楚该档案系统支援的情况喔!因为XFS已经可以使用project模式来设计不同目录的磁碟配额。

2、核心必须支援quota :
Linux核心必须有支援quota这个功能才行:如果你是使用CentOS 7.x的预设核心,嘿嘿!那恭喜你了,你的系统已经预设有支援quota这个功能啰!
如果你是自行编译核心的,那么请特别留意你是否已经『真的』开启了quota这个功能?否则底下的功夫将全部都视为『白工』。

3、只对一般身份使用者有效:
这就有趣了!并不是所有在Linux上面的帐号都可以设定quota呢,例如root就不能设定quota ,因为整个系统所有的资料几乎都是他的啊!^_^

4、若启用SELinux,非所有目录均可设定quota :
新版的CentOS预设都有启用SELinux这个核心功能,该功能会加强某些细部的权限控制!由于担心管理员不小心设定错误,因此预设的情况下, quota似乎仅能针对/home进行设定而已~
因此,如果你要针对其他不同的目录进行设定,请参考到后续章节查阅解开SELinux限制的方法喔!这就不是quota的问题了…

新版的CentOS使用的xfs确实比较有趣!不但无须额外的quota纪录档,也能够针对档案系统内的不同目录进行配置!
相当有趣!只是不同的档案系统在quota的处理情况上不太相同,因此这里要特别强调,进行quota前,先确认你的档案系统吧!

Quota 的规范设定项目

quota 这玩意儿针对XFS filesystem 的限制项目主要分为底下几个部分:

1、分别针对使用者、群组或个别目录(user, group & project):
XFS 档案系统的quota 限制中,主要是针对群组、个人或单独的目录进行磁碟使用率的限制!

2、容量限制或档案数量限制(block 或inode):
我们在第七章谈到档案系统中,说到档案系统主要规划为存放属性的inode与实际档案资料的block区块,Quota既然是管理档案系统,所以当然也可以管理inode或block啰!
这两个管理的功能为:
、、、限制inode 用量:可以管理使用者可以建立的『档案数量』;
、、、限制block 用量:管理使用者磁碟容量的限制,较常见为这种方式。

3、柔性劝导与硬性规定(soft/hard):
既然是规范,当然就有限制值。不管是inode/block ,限制值都有两个,分别是soft 与hard。通常hard 限制值要比soft 还要高。
举例来说,若限制项目为block ,可以限制hard 为500MBytes 而soft 为400MBytes。这两个限值的意义为:
、、、hard:表示使用者的用量绝对不会超过这个限制值,以上面的设定为例, 使用者所能使用的磁碟容量绝对不会超过500Mbytes ,若超过这个值则系统会锁住该用户的磁碟使用权;
、、、soft:表示使用者在低于soft 限值时(此例中为400Mbytes),可以正常使用磁碟,但若超过soft 且低于hard 的限值(介于400~500Mbytes 之间时),每次使用者登入系统时,系统会主动发出磁碟即将爆满的警告讯息, 且会给予一个宽限时间(grace time)。不过,若使用者在宽限时间倒数期间就将容量再次降低于soft 限值之下, 则宽限时间会停止。

4、会倒数计时的宽限时间(grace time):
刚刚上面就谈到宽限时间了!这个宽限时间只有在使用者的磁碟用量介于soft到hard之间时,才会出现且会倒数的一个咚咚!
由于达到hard限值时,使用者的磁碟使用权可能会被锁住。为了担心使用者没有注意到这个磁碟配额的问题,因此设计了soft 。
当你的磁碟用量即将到达hard且超过soft时,系统会给予警告,但也会给一段时间让使用者自行管理磁碟。
一般预设的宽限时间为七天,如果七天内你都不进行任何磁碟管理,那么soft限制值会即刻取代hard限值来作为quota的限制。

以上面设定的例子来说,假设你的容量高达450MBytes 了,那七天的宽限时间就会开始倒数, 若七天内你都不进行任何删除档案的动作来替你的磁碟用量瘦身, 那么七天后你的磁碟最大用量将变成400MBytes (那个soft 的限制值),此时你的磁碟使用权就会被锁住而无法新增档案了。

整个soft, hard, grace time 的相关性我们可以用底下的图示来说明:
图14.1.1、soft, hard, grace time 的相关性

图中的长条图为使用者的磁碟容量,soft/hard 分别是限制值。只要小于400M 就一切OK , 若高于soft 就出现grace time 并倒数且等待使用者自行处理,若到达hard 的限制值, 那我们就搬张小板凳等着看好戏啦!嘿嘿!^_^!这样图示有清楚一点了吗?

RAID:磁碟阵列

什么是 RAID?

磁碟阵列全名是『 Redundant Arrays of Independent Disks, RAID 』,英翻中的意思为:独立容错式磁碟阵列,旧称为容错式廉价磁碟阵列,反正就称为磁碟阵列即可!RAID可以透过一个技术(软体或硬体),将多个较小的磁碟整合成为一个较大的磁碟装置;而这个较大的磁碟功能可不止是储存而已,他还具有资料保护的功能呢。
整个RAID由于选择的等级(level)不同,而使得整合后的磁碟具有不同的功能,基本常见的 level 有下面这几种:

RAID-0 (等量模式, stripe):效能最佳

(陶攀峰)个人理解:相当于多线程的理解,不然是单线程,使用了这个之后是多线程处理(所以速度快),但是其中一个线程不行了,那么整体的数据就不正确了,就会有丢失或损坏。

这种模式如果使用相同型号与容量的磁碟来组成时,效果较佳。
这种模式的RAID 会将磁碟先切出等量的区块(名为chunk,一般可设定4K~1M 之间), 然后当一个档案要写入RAID 时,该档案会依据chunk 的大小切割好,之后再依序放到各个磁碟里面去。
由于每个磁碟会交错的存放资料, 因此当你的资料要写入RAID 时,资料会被等量的放置在各个磁碟上面。
举例来说,你有两颗磁碟组成RAID-0 , 当你有100MB 的资料要写入时,每个磁碟会各被分配到50MB 的储存量。
RAID-0 的示意图如下所示:
图14.2.1、RAID-0 的磁碟写入示意图

上图的意思是,在组成RAID-0时,每颗磁碟(Disk A与Disk B)都会先被区隔成为小区块(chunk)。当有资料要写入RAID时,资料会先被切割成符合小区块的大小,然后再依序一个一个的放置到不同的磁碟去。由于资料已经先被切割并且依序放置到不同的磁碟上面,因此每颗磁碟所负责的资料量都降低了!照这样的情况来看, 越多颗磁碟组成的RAID-0效能会越好,因为每颗负责的资料量就更低了!这表示我的资料可以分散让多颗磁碟来储存,当然效能会变的更好啊!此外,磁碟总容量也变大了!因为每颗磁碟的容量最终会加总成为RAID-0的总容量喔!

只是使用此等级你必须要自行负担资料损毁的风险,由上图我们知道档案是被切割成为适合每颗磁碟分割区块的大小,然后再依序放置到各个磁碟中。想一想,如果某一颗磁碟损毁了,那么档案资料将缺一块,此时这个档案就损毁了。由于每个档案都是这样存放的,因此RAID-0只要有任何一颗磁碟损毁,在RAID上面的所有资料都会遗失而无法读取。

另外,如果使用不同容量的磁碟来组成RAID-0 时,由于资料是一直等量的依序放置到不同磁碟中,当小容量磁碟的区块被用完了, 那么所有的资料都将被写入到最大的那颗磁碟去。举例来说,我用200G 与500G 组成RAID-0 , 那么最初的400GB 资料可同时写入两颗磁碟(各消耗200G 的容量),后来再加入的资料就只能写入500G 的那颗磁碟中了。此时的效能就变差了,因为只剩下一颗可以存放资料嘛!

RAID-1 (映射模式, mirror):完整备份

(陶攀峰)个人理解:相当于分布式的主从模式,但是它只有一个副本,如果自己丢失了,还是副本可以工作,如果副本丢失了,那么“恭喜你”,数据就彻底的丢失了。因为要拷贝一份同样的,所以容量减少一半。
这种模式也是需要相同的磁碟容量的,最好是一模一样的磁碟啦!如果是不同容量的磁碟组成RAID-1时,那么总容量将以最小的那一颗磁碟为主!这种模式主要是『让同一份资料,完整的保存在两颗磁碟上头』

举例来说,如果我有一个100MB的档案,且我仅有两颗磁碟组成RAID-1时,那么这两颗磁碟将会同步写入100MB到他们的储存空间去。因此,整体RAID的容量几乎少了50%。由于两颗硬碟内容一模一样,好像镜子映照出来一样,所以我们也称他为mirror模式啰~
在这里插入图片描述

如上图所示,一份资料传送到RAID-1 之后会被分为两股,并分别写入到各个磁碟里头去。由于同一份资料会被分别写入到其他不同磁碟,因此如果要写入100MB 时,资料传送到I/O 汇流排后会被复制多份到各个磁碟, 结果就是资料量感觉变大了!因此在大量写入RAID-1 的情况下,写入的效能可能会变的非常差(因为我们只有一个南桥啊!)。好在如果你使用的是硬体RAID (磁碟阵列卡) 时,磁碟阵列卡会主动的复制一份而不使用系统的I/O 汇流排,效能方面则还可以。如果使用软体磁碟阵列,可能效能就不好了。

由于两颗磁碟内的资料一模一样,所以任何一颗硬碟损毁时,你的资料还是可以完整的保留下来的!所以我们可以说,RAID-1最大的优点大概就在于资料的备份吧!不过由于磁碟容量有一半用在备份,因此总容量会是全部磁碟容量的一半而已。虽然RAID-1的写入效能不佳,不过读取的效能则还可以啦!这是因为资料有两份在不同的磁碟上面,如果多个processes在读取同一笔资料时, RAID会自行取得最佳的读取平衡。

RAID 1+0,RAID 0+1:组合模式

(陶攀峰)个人理解:上面0和1的综合体。多个线程进行工作,每个线程再建立一份副本。
RAID-0 的效能佳但是资料不安全,RAID-1 的资料安全但是效能不佳,那么能不能将这两者整合起来设定RAID 呢?可以啊!那就是RAID 1+0 或RAID 0+1。

所谓的RAID 1+0 就是: (1)先让两颗磁碟组成RAID 1,并且这样的设定共有两组; (2)将这两组RAID 1 再组成一组RAID 0。这就是RAID 1+0 啰!反过来说,RAID 0+1 就是先组成RAID-0 再组成RAID-1 的意思。

在这里插入图片描述

如上图所示,Disk A + Disk B 组成第一组RAID 1,Disk C + Disk D 组成第二组RAID 1, 然后这两组再整合成为一组RAID 0。如果我有100MB 的资料要写入,则由于RAID 0 的关系, 两组RAID 1 都会写入50MB,又由于RAID 1 的关系,因此每颗磁碟就会写入50MB 而已。如此一来不论哪一组RAID 1 的磁碟损毁,由于是RAID 1 的映像资料,因此就不会有任何问题发生了!这也是目前储存设备厂商最推荐的方法!

为何会推荐RAID 1+0 呢?想像你有20 颗磁碟组成的系统,每两颗组成一个RAID1,因此你就有总共10组可以自己复原的系统了!然后这10组再组成一个新的RAID0,速度立刻拉升10倍了!同时要注意,因为每组RAID1 是个别独立存在的,因此任何一颗磁碟损毁, 资料都是从另一颗磁碟直接复制过来重建,并不像RAID5/RAID6 必须要整组RAID 的磁碟共同重建一颗独立的磁碟系统!效能上差非常多!而且RAID 1 与RAID 0 是不需要经过计算的(striping) !读写效能也比其他的RAID 等级好太多了!

RAID 5:效能与资料备份的均衡考量

(陶攀峰)个人理解:因为至少三个,所以其中一个当做 parity 使用,所以真正使用的有两个。但是其中一个坏了,另外一个还可以恢复,但是这两个都坏了,就彻底丢失数据了。
RAID-5 至少需要三颗以上的磁碟才能够组成这种类型的磁碟阵列。

这种磁碟阵列的资料写入有点类似RAID-0 , 不过每个循环的写入过程中(striping),在每颗磁碟还加入一个同位检查资料(Parity) ,这个资料会记录其他磁碟的备份资料, 用于当有磁碟损毁时的救援。RAID-5 读写的情况有点像底下这样:

图14.2.4、RAID-5 的磁碟写入示意图
横向看,一层一层看。图中一层有三个位置。每层有一个 parity,其他两个为副本。

如上图所示,每个循环写入时,都会有部分的同位检查码(parity)被记录起来,并且记录的同位检查码每次都记录在不同的磁碟,因此,任何一个磁碟损毁时都能够藉由其他磁碟的检查码来重建原本磁碟内的资料喔!不过需要注意的是,由于有同位检查码,因此RAID 5的总容量会是整体磁碟数量减一颗。以上图为例,原本的3颗磁碟只会剩下(3-1)=2颗磁碟的容量。而且当损毁的磁碟数量大于等于两颗时,这整组RAID 5的资料就损毁了。因为RAID 5预设仅能支援一颗磁碟的损毁情况

在读写效能的比较上,读取的效能还不赖!与RAID-0 有的比!不过写的效能就不见得能够增加很多!这是因为要写入RAID 5 的资料还得要经过计算同位检查码(parity) 的关系。由于加上这个计算的动作, 所以写入的效能与系统的硬体关系较大!尤其当使用软体磁碟阵列时,同位检查码是透过CPU 去计算而非专职的磁碟阵列卡, 因此效能方面还需要评估。

另外,由于RAID 5 仅能支援一颗磁碟的损毁,因此近来还有发展出另外一种等级,就是RAID 6 ,这个RAID 6 则使用两颗磁碟的容量作为parity 的储存,因此整体的磁碟容量就会少两颗,但是允许出错的磁碟数量就可以达到两颗了!也就是在RAID 6 的情况下,同时两颗磁碟损毁时,资料还是可以救回来!

Spare Disk:预备磁碟的功能

(陶攀峰)个人理解:相当于一个不断开,可以继续工作
当磁碟阵列的磁碟损毁时,就得要将坏掉的磁碟拔除,然后换一颗新的磁碟。换成新磁碟并且顺利启动磁碟阵列后, 磁碟阵列就会开始主动的重建(rebuild) 原本坏掉的那颗磁碟资料到新的磁碟上!然后你磁碟阵列上面的资料就复原了!这就是磁碟阵列的优点。不过,我们还是得要动手拔插硬碟,除非你的系统有支援热拔插,否则通常得要关机才能这么做。

为了让系统可以即时的在坏掉硬碟时主动的重建,因此就需要预备磁碟(spare disk) 的辅助。所谓的spare disk 就是一颗或多颗没有包含在原本磁碟阵列等级中的磁碟,这颗磁碟平时并不会被磁碟阵列所使用, 当磁碟阵列有任何磁碟损毁时,则这颗spare disk 会被主动的拉进磁碟阵列中,并将坏掉的那颗硬碟移出磁碟阵列!然后立即重建资料系统。如此你的系统则可以永保安康啊!若你的磁碟阵列有支援热拔插那就更完美了!直接将坏掉的那颗磁碟拔除换一颗新的,再将那颗新的设定成为spare disk ,就完成了!

举例来说,鸟哥之前所待的研究室有一个磁碟阵列可允许16 颗磁碟的数量,不过我们只安装了10 颗磁碟作为RAID 5。每颗磁碟的容量为250GB,我们用了一颗磁碟作为spare disk ,并将其他的9 颗设定为一个RAID 5, 因此这个磁碟阵列的总容量为: (9-1) x 250G= 2000G。运作了一两年后真的有一颗磁碟坏掉了,我们后来看灯号才发现!不过对系统没有影响呢!因为spare disk 主动的加入支援,坏掉的那颗拔掉换颗新的,并重新设定成为spare 后, 系统内的资料还是完整无缺的!嘿嘿!真不错!

磁碟阵列的总结

说的口沫横飞,重点在哪里呢?其实你的系统如果需要磁碟阵列的话,其实重点在于:
1、资料安全与可靠性:指的并非网路资讯安全,而是当硬体(指磁碟) 损毁时,资料是否还能够安全的救援或使用之意;
2、读写效能:例如RAID 0 可以加强读写效能,让你的系统I/O 部分得以改善;
3、容量:可以让多颗磁碟组合起来,故单一档案系统可以有相当大的容量。

尤其资料的可靠性与完整性更是使用RAID 的考量重点!毕竟硬体坏掉换掉就好了,软体资料损毁那可不是闹着玩的!所以企业界为何需要大量的RAID 来做为档案系统的硬体基准,现在您有点了解了吧?那依据这三个重点,我们来列表看看上面几个重要的RAID 等级各有哪些优点吧!

假设有n 颗磁碟组成的RAID 设定喔!

在这里插入图片描述

注:因为RAID5, RAID6 读写都需要经过parity 的计算机制,因此读/写效能都不会刚好满足于使用的磁碟数量喔!

另外,根据使用的情况不同,一般推荐的磁碟阵列等级也不太一样。以鸟哥为例,在鸟哥的跑空气品质模式之后的输出资料,动辄几百GB 的单一大档案资料, 这些情况鸟哥会选择放在RAID6 的阵列环境下,这是考量到资料保全与总容量的应用,因为RAID 6 的效能已经足以应付模式读入所需的环境。

近年来鸟哥也比较积极在作一些云程式环境的设计,在云环境下,确保每个虚拟机器能够快速的反应以及提供资料保全是最重要的部份!因此效能方面比较弱的RAID5/RAID6 是不考虑的,总结来说,大概就剩下RAID10 能够满足云环境的效能需求了。在某些更特别的环境下, 如果搭配SSD 那才更具有效能上的优势哩!

software, hardware RAID

为何磁碟阵列又分为硬体与软体呢?所谓的硬体磁碟阵列(hardware RAID) 是透过磁碟阵列卡来达成阵列的目的。磁碟阵列卡上面有一块专门的晶片在处理RAID 的任务,因此在效能方面会比较好。在很多任务(例如RAID 5 的同位检查码计算) 磁碟阵列并不会重复消耗原本系统的I/O 汇流排,理论上效能会较佳。此外目前一般的中高阶磁碟阵列卡都支援热拔插, 亦即在不关机的情况下抽换损坏的磁碟,对于系统的复原与资料的可靠性方面非常的好用。

不过一块好的磁碟阵列卡动不动就上万元台币,便宜的在主机板上面『附赠』的磁碟阵列功能可能又不支援某些高阶功能, 例如低阶主机板若有磁碟阵列晶片,通常仅支援到RAID0 与RAID1 ,鸟哥喜欢的RAID6 并没有支援。此外,作业系统也必须要拥有磁碟阵列卡的驱动程式,才能够正确的捉到磁碟阵列所产生的磁碟机!

由于磁碟阵列有很多优秀的功能,然而硬体磁碟阵列卡偏偏又贵的很~因此就有发展出利用软体来模拟磁碟阵列的功能, 这就是所谓的软体磁碟阵列(software RAID) 。软体磁碟阵列主要是透过软体来模拟阵列的任务, 因此会损耗较多的系统资源,比如说CPU 的运算与I/O 汇流排的资源等。不过目前我们的个人电脑实在已经非常快速了, 因此以前的速度限制现在已经不存在!所以我们可以来玩一玩软体磁碟阵列!

我们的CentOS提供的软体磁碟阵列为mdadm这套软体,这套软体会以partition或disk为磁碟的单位,也就是说,你不需要两颗以上的磁碟,只要有两个以上的分割槽(partition)就能够设计你的磁碟阵列了。此外, mdadm支援刚刚我们前面提到的RAID0/RAID1/RAID5/spare disk等!而且提供的管理机制还可以达到类似热拔插的功能,可以线上(档案系统正常使用)进行分割槽的抽换!使用上也非常的方便呢!

另外你必须要知道的是,硬体磁碟阵列在Linux底下看起来就是一颗实际的大磁碟,因此硬体磁碟阵列的装置档名为 /dev/sd[ap] ,因为使用到SCSI的模组之故。 至于软体磁碟阵列则是系统模拟的,因此使用的装置档名是系统的装置档,档名为 /dev/md0 , /dev/md1,两者的装置档名并不相同!不要搞混了喔!因为很多朋友常常觉得奇怪,怎么他的RAID档名跟我们这里测试的软体RAID档名不同,所以这里特别强调说明喔!

Intel 的南桥附赠的磁碟阵列功能,在windows 底下似乎是完整的磁碟阵列,但是在Linux 底下则被视为是软体磁碟阵列的一种!因此如果你有设定过Intel 的南桥晶片磁碟阵列,那在Linux 底下反而还会是/dev/md126, /dev/md127 等等装置档名, 而他的分割槽竟然是/dev/md126p1, / dev/md126p2… 之类的喔!比较特别,所以这里加强说明!

LVM:逻辑卷轴管理员

(Logical Volume Manager)

想像一个情况,你在当初规划主机的时候将/home 只给他50G ,等到使用者众多之后导致这个filesystem 不够大, 此时你能怎么作?多数的朋友都是这样:再加一颗新硬碟,然后重新分割、格式化,将/home 的资料完整的复制过来, 然后将原本的partition 卸载重新挂载新的partition 。啊!好忙碌啊!若是第二次分割却给的容量太多!导致很多磁碟容量被浪费了!你想要将这个partition 缩小时,又该如何作?将上述的流程再搞一遍!唉~烦死了,尤其复制很花时间ㄟ~有没有更简单的方法呢?有的!那就是我们这个小节要介绍的LVM 这玩意儿!

LVM的重点在于『可以弹性的调整filesystem的容量!』而并非在于效能与资料保全上面。需要档案的读写效能或者是资料的可靠性,请参考前面的RAID小节。 LVM可以整合多个实体partition在一起,让这些partitions看起来就像是一个磁碟一样!而且,还可以在未来新增或移除其他的实体partition到这个LVM管理的磁碟当中。如此一来,整个磁碟空间的使用上,实在是相当的具有弹性啊!既然LVM这么好用,那就让我们来瞧瞧这玩意吧!

什么是LVM: PV, PE, VG, LV 的意义

LVM 的全名是Logical Volume Manager,中文可以翻译作逻辑卷轴管理员。之所以称为『卷轴』可能是因为可以将 filesystem 像卷轴一样伸长或缩短之故吧!

LVM 的作法是将几个实体的partitions (或disk) 透过软体组合成为一块看起来是独立的大磁碟(VG) ,然后将这块大磁碟再经过分割成为可使用分割槽(LV),最终就能够挂载使用了。但是为什么这样的系统可以进行filesystem 的扩充或缩小呢?其实与一个称为PE 的项目有关!底下我们就得要针对这几个项目来好好聊聊!

Physical Volume, PV, 实体卷轴
我们实际的partition (或Disk)需要调整系统识别码(system ID)成为8e (LVM的识别码),然后再经过pvcreate的指令将他转成LVM最底层的实体卷轴(PV) ,之后才能够将这些PV加以利用!调整system ID的方是就是透过gdisk啦!

Volume Group, VG, 卷轴群组
所谓的LVM 大磁碟就是将许多PV 整合成这个VG 的东西就是啦!所以VG 就是LVM 组合起来的大磁碟!这么想就好了。那么这个大磁碟最大可以到多少容量呢?这与底下要说明的PE 以及LVM 的格式版本有关喔~在预设的情况下, 使用32位元的Linux 系统时,基本上LV 最大仅能支援到65534 个PE 而已,若使用预设的PE为4MB 的情况下, 最大容量则仅能达到约256GB 而已~不过,这个问题在64位元的Linux 系统上面已经不存在了!LV 几乎没有啥容量限制了!

Physical Extent, PE, 实体范围区块
LVM预设使用4MB的PE区块,而LVM的LV在32位元系统上最多仅能含有65534个PE (lvm1的格式),因此预设的LVM的LV会有4M*65534/(1024M/G )=256G。这个PE很有趣喔!他是整个LVM最小的储存区块,也就是说,其实我们的档案资料都是藉由写入PE来处理的。简单的说,这个PE就有点像档案系统里面的block大小啦。这样说应该就比较好理解了吧?所以调整PE会影响到LVM的最大容量喔!不过,在CentOS 6.x以后,由于直接使用lvm2的各项格式功能,以及系统转为64位元,因此这个限制已经不存在了。

Logical Volume, LV, 逻辑卷轴
最终的VG还会被切成LV,这个LV就是最后可以被格式化使用的类似分割槽的咚咚了!那么LV是否可以随意指定大小呢?当然不可以!既然PE是整个LVM的最小储存单位,那么LV的大小就与在此LV内的PE总数有关。为了方便使用者利用LVM来管理其系统,因此LV的装置档名通常指定为『 /dev/vgname/lvname』的样式!

此外,我们刚刚有谈到LVM 可弹性的变更filesystem 的容量,那是如何办到的?其实他就是透过『交换PE 』来进行资料转换, 将原本LV 内的PE 移转到其他装置中以降低LV 容量,或将其他装置的PE 加到此LV 中以加大容量!VG、LV 与PE 的关系有点像下图:

图14.3.1、PE 与VG 的相关性图示

如上图所示,VG 内的PE 会分给虚线部分的LV,如果未来这个VG 要扩充的话,加上其他的PV 即可。而最重要的LV 如果要扩充的话,也是透过加入VG 内没有使用到的PE 来扩充的!

(陶攀峰)以下本人理解:
多个磁碟搞成 LVM 流程
、多个磁碟,一个磁碟转成一个 PV(Physical Volume, PV, 实体卷轴)
、多个 PV 转成一个 VG(Volume Group, 卷轴群组)
、一个 VG 里面有很多 PE(Physical Extent, 实体范围区块)
、一个 VG 也会划分多个 LV(Logical Volume, 逻辑卷轴)
、划分的 LV 里面是包含 PE

大话:
多个磁碟(班级)搞成 LVM(校长=学校管理员) 流程
、多个磁碟(班级),一个磁碟(班级)转成一个 PV(班级)(Physical Volume, PV, 实体卷轴)
、多个 PV (班级)转成一个 VG(学校)(Volume Group, 卷轴群组)
、一个 VG(学校) 里面有很多 PE(学生)(Physical Extent, 实体范围区块)
、一个 VG(学校) 也会划分多个 LV(年级)(Logical Volume, 逻辑卷轴)
、划分的 LV (年级)里面是包含 PE(学生

实操流程

透过PV, VG, LV的规划之后,再利用mkfs 就可以将你的LV格式化成为可以利用的档案系统了!而且这个档案系统的容量在未来还能够进行扩充或减少,而且里面的资料还不会被影响!实在是很『福气啦!』

那实作方面要如何进行呢?很简单呢!整个流程由基础到最终的结果可以这样看:

图14.3.2、LVM 各元件的实现流程图示

如此一来,我们就可以利用LV这个玩意儿来进行系统的挂载了。不过,你应该要觉得奇怪的是, 那么我的资料写入这个LV时,到底他是怎么写入硬碟当中的? 呵呵!好问题~其实,依据写入机制的不同,而有两种方式:

线性模式(linear):假如我将/dev/vda1, /dev/vdb1 这两个partition 加入到VG 当中,并且整个VG 只有一个LV 时,那么所谓的线性模式就是:当/dev/vda1 的容量用完之后,/dev/vdb1 的硬碟才会被使用到, 这也是我们所建议的模式。

交错模式(triped):那什么是交错模式?很简单啊,就是我将一笔资料拆成两部分,分别写入/dev/vda1 与/dev/vdb1 的意思,感觉上有点像RAID 0 啦!如此一来,一份资料用两颗硬碟来写入,理论上,读写的效能会比较好。

基本上,LVM最主要的用处是在实现一个可以弹性调整容量的档案系统上,而不是在建立一个效能为主的磁碟上,所以,我们应该利用的是LVM可以弹性管理整个partition大小的用途上,而不是著眼在效能上的。因此, LVM预设的读写模式是线性模式啦!如果你使用triped模式,要注意,当任何一个partition 『归天』时,所有的资料都会『损毁』的!所以啦,不是很适合使用这种模式啦!如果要强调效能与备份,那么就直接使用RAID即可,不需要用到LVM啊!

使用LVM thin Volume 让LVM 动态自动调整磁碟使用率

(陶攀峰)个人理解:这个池子相当于一个大棚,里面种的都是草莓,你带着篮子去摘草莓,实际装的只是你篮子的容量,而不是真实的大棚容量,这个大棚就是:LVM thin Volume
想像一个情况,你有个目录未来会使用到大约5T 的容量,但是目前你的磁碟仅有3T,问题是,接下来的两个月你的系统都还不会超过3T 的容量, 不过你想要让用户知道,就是他最多有5T 可以使用就是了!而且在一个月内你确实可以将系统提升到5T 以上的容量啊!你又不想要在提升容量后才放大到5T!那可以怎么办?呵呵!这时可以考虑『实际用多少才分配多少容量给LV 的LVM Thin Volume 』功能!

另外,再想像一个环境,如果你需要有3 个10GB 的磁碟来进行某些测试,问题是你的环境仅有5GB 的剩余容量,再传统的LVM 环境下, LV 的容量是一开始就分配好的,因此你当然没有办法在这样的环境中产生出3 个10GB 的装置啊!而且更呕的是,那个10GB 的装置其实每个实际使用率都没有超过10%, 也就是总用量目前仅会到3GB 而已!但…我实际就有5GB 的容量啊!为何不给我做出3 个只用1GB 的10GB 装置呢?有啊!就还是LVM thin Volume 啊!

什么是LVM thin Volume 呢?这东西其实挺好玩的,他的概念是:先建立一个可以实支实付、用多少容量才分配实际写入多少容量的磁碟容量储存池(thin pool), 然后再由这个thin pool 去产生一个『指定要固定容量大小的LV 装置』,这个LV 就有趣了!虽然你会看到『宣告上,他的容量可能有10GB ,但实际上, 该装置用到多少容量时,才会从thin pool 去实际取得所需要的容量』!就如同上面的环境说的,可能我们的thin pool 仅有1GB 的容量, 但是可以分配给一个10GB 的LV 装置!而该装置实际使用到500M 时,整个thin pool 才分配500M 给该LV 的意思!当然啦!在所有由thin pool 所分配出来的LV 装置中,总实际使用量绝不能超过thin pool 的最大实际容量啊!如这个案例说的, thin pool 仅有1GB, 那所有的由这个thin pool 建置出来的LV 装置内的实际用量,就绝不能超过1GB 啊!

LVM 的 LV 磁碟快照

现在你知道LVM的好处咯,未来如果你有想要增加某个LVM的容量时,就可以透过这个放大的功能来处理。那么LVM除了这些功能之外,还有什么能力呢?其实他还有一个重要的能力,那就是LV磁碟的快照(snapshot)。什么是LV磁碟快照啊?快照就是将当时的系统资讯记录下来,就好像照相记录一般!未来若有任何资料更动了,则原始资料会被搬移到快照区,没有被更动的区域则由快照区与档案系统共享。 用讲的好像很难懂,我们用图解说明一下好了:

图14.3.3、LVM 快照区域的备份示意图

左图为最初建置LV磁碟快照区的状况,LVM会预留一个区域(左图的左侧三个PE区块)作为资料存放处。此时快照区内并没有任何资料,而快照区与系统区共享所有的PE资料,因此你会看到快照区的内容与档案系统是一模一样的。等到系统运作一阵子后,假设A区域的资料被更动了(上面右图所示),则更动前系统会将该区域的资料移动到快照区,所以在右图的快照区被占用了一块PE成为A,而其他B到I的区块则还是与档案系统共用!

照这样的情况来看,LVM 的磁碟快照是非常棒的『备份工具』,因为他只有备份有被更动到的资料, 档案系统内没有被变更的资料依旧保持在原本的区块内,但是LVM 快照功能会知道那些资料放置在哪里, 因此『快照』当时的档案系统就得以『备份』下来,且快照所占用的容量又非常小!所以您说,这不是很棒的工具又是什么?

那么快照区要如何建立与使用呢?首先,由于快照区与原本的LV共用很多PE区块,因此快照区与被快照的LV必须要在同一个VG上头

另外,或许你跟鸟哥一样,会想到说:『咦!我们能不能使用thin pool 的功能来制作快照』呢?老实说,是可以的!不过使用上面的限制非常的多!包括最好要在同一个thin pool 内的原始LV 磁碟, 如果为非thin pool 内的原始LV 磁碟快照,则该磁碟快照『不可以写入』,亦即LV 磁碟要设定成唯读才行!同时, 使用thin pool 做出来的快照,通常都是不可启动(inactive) 的预设情况,启动又有点麻烦~所以,至少目前(CentOS 7.x) 的环境下, 鸟哥还不是很建议你使用thin pool 快照喔!

CentOS Linux 系统上常见的循环性工作

1、进行登录档的轮替(log rotate):
Linux会主动的将系统所发生的各种资讯都记录下来,这就是登录档(第十八章)。由于系统会一直记录登录资讯,所以登录档将会越来越大!我们知道大型档案不但占容量还会造成读写效能的困扰,因此适时的将登录档资料挪一挪,让旧的资料与新的资料分别存放,则比较可以有效的记录登录资讯。这就是log rotate的任务!这也是系统必要的例行任务;

2、登录档分析logwatch的任务:
如果系统发生了软体问题、硬体错误、资安问题等,绝大部分的错误资讯都会被记录到登录档中,因此系统管理员的重要任务之一就是分析登录档。但你不可能手动透过vim等软体去检视登录档,因为资料太复杂了!我们的CentOS提供了一只程式『 logwatch 』来主动分析登录资讯,所以你会发现,你的root老是会收到标题为logwatch的信件,那是正常的!你最好也能够看看该信件的内容喔!

3、建立locate的资料库:
在第六章我们谈到的locate指令时,我们知道该指令是透过已经存在的档名资料库来进行系统上档名的查询。我们的档名资料库是放置到/var/lib/mlocate/中。问题是,这个资料库怎么会自动更新啊?嘿嘿!这就是系统的例行性工作所产生的效果啦!系统会主动的进行updatedb喔!

4、man page查询资料库的建立:
与locate资料库类似的,可提供快速查询的man page db也是个资料库,但如果要使用man page资料库时,就得要执行mandb才能够建立好啊!而这个man page资料库也是透过系统的例行性工作排程来自动执行的哩!

5、RPM软体登录档的建立:
RPM (第二十二章)是一种软体管理的机制。由于系统可能会常常变更软体,包括软体的新安装、非经常性更新等,都会造成软体档名的差异。为了方便未来追踪,系统也帮我们将档名作个排序的记录呢!有时候系统也会透过排程来帮忙RPM资料库的重新建置喔!

6、移除暂存档:
某些软体在运作中会产生一些暂存档,但是当这个软体关闭时,这些暂存档可能并不会主动的被移除。有些暂存档则有时间性,如果超过一段时间后,这个暂存档就没有效用了,此时移除这些暂存档就是一件重要的工作!否则磁碟容量会被耗光。系统透过例行性工作排程执行名为tmpwatch的指令来删除这些暂存档呢!

7、与网路服务有关的分析行为:
如果你有安装类似WWW伺服器软体(一个名为apache的软体),那么你的Linux系统通常就会主动的分析该软体的登录档。同时某些凭证与认证的网路资讯是否过期的问题,我们的Linux系统也会很亲和的帮你进行自动检查!

at:定时,一次性

at是个可以处理仅执行一次就结束排程的指令,不过要执行at时,必须要有atd这个服务(第十七章)的支援才行。
在某些新版的distributions中,atd可能预设并没有启动,那么at这个指令就会失效呢!

atd 是服务,at 是命令。

atd 服务

先安装命令:yum -y install at

1
2
3
4
5
6
systemctl status atd #===> 查看状态
systemctl start atd #===> 启动
systemctl stop atd #===> 停止
systemctl restart atd #===> 重启
systemctl enable atd #===> 开启【开机自启动】
systemctl disable atd #===> 关闭【开机自启动】

at 工作流程

at 的工作情况其实是这样的:

1、先找寻/etc/at.allow这个档案,写在这个档案中的使用者才能使用at ,没有在这个档案中的使用者则不能使用at (即使没有写在at.deny当中);

2、如果/etc/at.allow不存在,就寻找/etc/at.deny这个档案,若写在这个at.deny的使用者则不能使用at ,而没有在这个at.deny档案中的使用者,就可以使用at咯;

3、如果两个档案都不存在,那么只有root 可以使用at 这个指令。

at 使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

[root@study ~]# at [-mldv] TIME
[root@study ~]# at -c工作号码
选项与参数:
-m :当at 的工作完成后,即使没有输出讯息,亦以email 通知使用者该工作已完成。
-l :at -l 相当于atq,列出目前系统上面的所有该使用者的at 排程;
-d :at -d 相当于atrm ,可以取消一个在at 排程中的工作;
-v :可以使用较明显的时间格式列出at 排程中的工作列表;
-c :可以列出后面接的该项工作的实际指令内容。

TIME:时间格式,这里可以定义出『什么时候要进行at 这项工作』的时间,格式有:
HH:MM ex> 04:00
在今日的HH:MM 时刻进行,若该时刻已超过,则明天的HH:MM 进行此工作。
HH:MM YYYY-MM-DD ex> 04:00 2015-07-30
强制规定在某年某月的某一天的特殊时刻进行该工作!
HH:MM[am|pm] [Month] [Date] ex> 04pm July 30
也是一样,强制在某年某月某日的某时刻进行!
HH:MM[am|pm] + number [minutes|hours|days|weeks]
ex> now + 5 minutes ex> 04pm + 3 days
就是说,在某个时间点『再加几个时间后』才进行。

at 使用案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
范例一:再过五分钟后,将/root/.bashrc寄给root自己
[root@study ~]# at now + 5 minutes <==记得单位要加s喔!
at> /bin/mail -s "testing at job" root < /root/.bashrc
at> <EOT> <==这里输入[ctrl] + d就会出现<EOF>的字样!代表结束!
job 2 at Thu Jul 30 19:35:00 2015
# 上面这行资讯在说明,第2 个at 工作将在2015/07/30 的19:35 进行!
# 而执行at 会进入所谓的at shell 环境,让你下达多重指令等待运作!

范例二:将上述的第2项工作内容列出来查阅
[root@study ~]# at -c 2
#!/bin/sh <==就是透过bash shell的啦!
# atrun uid=0 gid=0
# mail root 0
umask 22
....(中间省略许多的环境变数项目)....
cd /etc/cron\.d || {
echo 'Execution directory inaccessible' >&2
exit 1
}
${SHELL:-/bin/sh} << 'marcinDELIMITER410efc26'
/bin/mail -s "testing at job" root < /root/.bashrc #这一行最重要!
marcinDELIMITER410efc26
#你可以看到指令执行的目录(/root),还有多个环境变数与实际的指令内容啦!

范例三:由于机房预计于2015/08/05停电,我想要在2015/08/04 23:00关机?
[root@study ~]# at 23:00 2015-08-04
at> /bin/sync
at> /bin/sync
at> /sbin/shutdown -h now
at> <EOT>
job 3 at Tue Aug 4 23:00:00 2015
# 您瞧瞧!at 还可以在一个工作内输入多个指令呢!不错吧!

事实上,当我们使用at时会进入一个at shell的环境来让使用者下达工作指令,此时,建议你最好使用绝对路径来下达你的指令,比较不会有问题喔!由于指令的下达与PATH变数有关,同时与当时的工作目录也有关连(如果有牵涉到档案的话),因此使用绝对路径来下达指令,会是比较一劳永逸的方法。为什么呢?举例来说,你在/tmp下达『 at now 』然后输入『 mail -s “test” root < .bashrc 』,问一下,那个.bashrc的档案会是在哪里?答案是『 /tmp/.bashrc 』!

因为 at在运作时,会跑到当时下达at指令的那个工作目录的缘故啊!

有些朋友会希望『我要在某某时刻,在我的终端机显示出Hello的字样』,然后就在at里面下达这样的资讯『 echo “Hello” 』。等到时间到了,却发现没有任何讯息在萤幕上显示,这是啥原因啊?这是因为at的执行与终端机环境无关,而所有standard output/standard error output都会传送到执行者的mailbox去啦!所以在终端机当然看不到任何资讯。那怎办?没关系,可以透过终端机的装置来处理!假如你在tty1登入,则可以使用『 echo “Hello” > /dev/tty1 』来取代。

由于at工作排程的使用上,系统会将该项at工作独立出你的bash环境中,直接交给系统的atd程式来接管,因此,当你下达了at的工作之后就可以立刻离线了,剩下的工作就完全交给Linux管理即可!

atq,atrm:at 工作管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14

[root@study ~]# atq
[root@study ~]# atrm (jobnumber)

范例一:查询目前主机上面有多少的at工作排程?
[root@study ~]# atq
3 Tue Aug 4 23:00:00 2015 a root
# 上面说的是:『在2015/08/04 的23:00 有一项工作,该项工作指令下达者为
# root』而且,该项工作的工作号码(jobnumber) 为3 号喔!

范例二:将上述的第3个工作移除!
[root@study ~]# atrm 3
[root@study ~]# atq
#没有任何资讯,表示该工作被移除了!

batch:系统有空时才进行背景任务

其实batch是利用at来进行指令的下达啦!只是加入一些控制参数而已。

这个batch神奇的地方在于:他会在CPU的工作负载小于0.8(80%)的时候,才进行你所下达的工作任务啦!

因为CPU仅负责一个工作嘛!如果同时执行这样的程式两支呢?CPU的使用率还是100% ,但是工作负载则变成2了!了解乎?

1
2
3
4
5
6
7
[root@study ~]# uptime 
19:56:45 up 2 days, 19:54, 2 users, load average: 3.93, 2.23, 0.96

[root@study ~]# batch
at> /usr/bin/updatedb
at> <EOT>
job 4 at Thu Jul 30 19:57:00 2015

使用uptime 可以观察到1, 5, 15 分钟的『平均工作负载』量,因为是平均值

crontab:定时,循环

crontab这个指令所设定的工作将会循环的一直进行下去!可循环的时间为分钟、小时、每周、每月或每年等。
crontab除了可以使用指令执行外,亦可编辑/etc/crontab来支援。至于让crontab可以生效的服务则是crond这个服务喔!

crond 是服务,crontab 是命令。

ctond 服务

1
2
3
4
5
6
systemctl status crond #===> 查看状态
systemctl start crond #===> 启动
systemctl stop crond #===> 停止
systemctl restart crond #===> 重启
systemctl enable crond #===> 开启【开机自启动】
systemctl disable crond #===> 关闭【开机自启动】

crontab 工作流程

为了安全性的问题, 与at 同样的,我们可以限制使用crontab 的使用者帐号喔!使用的限制资料有:
/etc/cron.allow
将可以使用crontab的帐号写入其中,若不在这个档案内的使用者则不可使用crontab;

/etc/cron.deny
将不可以使用crontab的帐号写入其中,若未记录到这个档案当中的使用者,就可以使用crontab 。

与at 很像吧!同样的,以优先顺序来说, /etc/cron.allow 比/etc/cron.deny 要优先, 而判断上面,这两个档案只选择一个来限制而已。
因此,建议你只要保留一个即可,免得影响自己在设定上面的判断!一般来说,系统预设是保留/etc/cron.deny , 你可以将不想让他执行crontab 的那个使用者写入/etc/cron.deny 当中,一个帐号一行!

当使用者使用crontab这个指令来建立工作排程之后,该项工作就会被纪录到/var/spool/cron/里面去了,而且是以帐号来作为判别的喔!
举例来说, root 使用crontab后,他的工作会被纪录到/var/spool/cron/root 这个文本文件里头去!但请注意,不要使用vi直接编辑该档案,因为可能由于输入语法错误,会导致无法执行cron喔!另外, cron执行的每一项工作都会被纪录到/var/log/cron这个登录档中,所以啰,如果你的Linux不知道有否被植入木马时,也可以搜寻一下/var/log/ cron这个登录档呢!

crontab 使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13

[root@study ~]# crontab [-u username] [-l|-e|-r]
选项与参数:
-u :只有root 才能进行这个任务,亦即帮其他使用者建立/移除crontab 工作排程;
-e :编辑crontab 的工作内容
-l :查看crontab 的工作内容
-r :移除所有的crontab 的工作内容【若仅要移除一项,请用-e 去编辑。】

范例一:用dmtsai的身份在每天的12:00发信给自己
[dmtsai@study ~]$ crontab -e
#此时会进入vi的编辑画面让您编辑工作!注意到,每项工作都是一行。
0 12 * * * mail -s "at 12:00" dmtsai < /home/dmtsai/.bashrc
#分时日月周|<==============指令串=== =====================>|

预设情况下,任何使用者只要不被列入/etc/cron.deny 当中,那么他就可以直接下达『 crontab -e 』去编辑自己的例行性命令了!整个过程就如同上面提到的,会进入vi 的编辑画面, 然后以一个工作一行来编辑,编辑完毕之后输入『 :wq 』储存后离开vi 就可以了!

而每项工作(每行) 的格式都是具有六个栏位,这六个栏位的意义为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#项目           含义                范围
第一个* 一小时的第几分钟 0~59
第二个* 一天的第几个小时 0~23
第三个* 一月的第几天 1~31
第四个* 一年的第几月 1~12
第五个* 一周的星期几 0~7(0和7代表星期日)




#时间 含义
* * * * * 命令 每一分钟执行一次命令
10 * * * * 命令 每小时的第10分钟执行一次命令
45 22 * * * 命令 每天的22:45执行一次命令
0 17 * * 1 命令 每周一的17:00执行一次命令
0 5 1,15 * * 命令 每月的1号和15号的05:00执行一次命令
40 4 * * 1-5 命令 每周一到周五的04:40执行一次命令
*/10 4 * * * 命令 每天的04:00每隔10分钟执行一次
0 0 1,15 * 1 命令 每个月的1号和15号的00:00或每周一的00:00都会执行一次命令

注意:周与日月不可同时并存
30 12 11 9 5 root echo "just test" <==这是错误的写法
本来你以为九月十一号且为星期五才会进行这项工作,无奈的是,系统可能会判定每个星期五作一次,
或每年的9 月11 号分别进行,如此一来与你当初的规划就不一样了~所以啰,得要注意这个地方!

# 特殊符号 含义
*星号 任何时间,
例如第一个*表示一个小时每分钟执行一次
,逗号 不连续时间,
例如0 8,12,16 * * * 命令表示每天08:00和12:00和16:00都会执行一次命令
-减号 连续的时间范围,
例如0 5 * * 1-6 命令表示每周1到周6的05:00都会执行一次命令
*/n星号 每隔多久执行一次,
例如*/10 * * * * 命令代表每隔10分钟执行一次命令
1
2
3
4
5
6
7
8
9
10
---------------------------crontab -e #===> 编辑,等同于【vim /var/spool/cron/$(whoami)】
crontab: no changes made to crontab
---------------------------crontab -l #===> 查看,等同于【cat /var/spool/cron/$(whoami)】
* * * * * /app/crontab/mkdir.sh
---------------------------
---------------------------crontab -r #===> 删除全部,等同于【echo > /var/spool/cron/$(whoami)】
---------------------------
---------------------------
---------------------------crontab -l #===> 查看,无内容
no crontab for root

/etc/crontab

基本上,cron这个服务的最低侦测限制是『分钟』,所以『 cron会每分钟去读取一次/etc/crontab与/var/spool/cron里面的资料内容 』,因此,只要你编辑完/ etc/crontab这个档案,并且将他储存之后,那么cron的设定就自动的会来执行了!

注意:
在Linux 底下的crontab 会自动的帮我们每分钟重新读取一次/etc/crontab 的例行工作事项,但是某些原因或者是其他的Unix 系统中,由于crontab 是读到记忆体当中的,所以在你修改完/etc/crontab 之后,可能并不会马上执行, 这个时候请重新启动crond 这个服务吧!『systemctl restart crond』

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@study ~]# cat /etc/crontab 
SHELL=/bin/bash <==使用哪种shell介面
PATH=/sbin:/bin:/usr/sbin:/usr/bin <==执行档搜寻路径
MAILTO=root <==若有额外STDOUT,以email将资料送给谁

# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed

MAILTO=root:
这个项目是说,当/etc/crontab这个档案中的例行性工作的指令发生错误时,或者是该工作的执行结果有STDOUT/STDERR时,会将错误讯息或者是萤幕显示的讯息传给谁?预设当然是由系统直接寄发一封mail给root啦!不过,由于root并无法在用户端中以POP3之类的软体收信,因此,鸟哥通常都将这个e-mail改成自己的帐号,好让我随时了解系统的状况!例如: MAILTO=dmtsai@my.host.name

PATH=….:
还记得我们在第十章的BASH当中一直提到的执行档路径问题吧!没错啦!这里就是输入执行档的搜寻路径!使用预设的路径设定就已经很足够了!

『分,时,日,月,周,身份,指令』七个栏位的设定(crontab -e 命令是 6 个栏位喔~!!)
这个/etc/crontab里面可以设定的基本语法与crontab -e不太相同喔!前面同样是分、时、日、月、周五个栏位,但是在五个栏位后面接的并不是指令,而是一个新的栏位,那就是『执行后面那串指令的身份』为何!这与使用者的crontab -e不相同。由于使用者自己的crontab并不需要指定身份,但/etc/crontab里面当然要指定身份啦!以上表的内容来说,系统预设的例行性工作是以root的身份来进行的。

crond 服务读取设定档的位置

一般来说,crond 预设有三个地方会有执行脚本设定档,他们分别是:

1
2
3
4
5
/etc/crontab #===> 系统的运作

/etc/cron.d/* #===> 系统的运作

/var/spool/cron/* #===> 用户工作

anacron:可唤醒停机期间的工作任务

长时间没执行的任务,可以再次执行。

什么是anacron?

anacron 并不是用来取代crontab 的,anacron 存在的目的就在于我们上头提到的,在处理非24 小时一直启动的Linux 系统的crontab 的执行!以及因为某些原因导致的超过时间而没有被执行的排程工作。

其实anacron 也是每个小时被crond 执行一次,然后anacron 再去检测相关的排程任务有没有被执行,如果有超过期限的工作在, 就执行该排程任务,执行完毕或无须执行任何排程时,anacron 就停止了。

由于anacron 预设会以一天、七天、一个月为期去侦测系统未进行的crontab 任务,因此对于某些特殊的使用环境非常有帮助。举例来说,如果你的Linux 主机是放在公司给同仁使用的,因为周末假日大家都不在所以也没有必要开启, 因此你的Linux 是周末都会关机两天的。但是crontab 大多在每天的凌晨以及周日的早上进行各项任务, 偏偏你又关机了,此时系统很多crontab 的任务就无法进行。anacron 刚好可以解决这个问题!

那么anacron 又是怎么知道我们的系统啥时关机的呢?这就得要使用anacron 读取的时间记录档(timestamps) 了!anacron 会去分析现在的时间与时间记录档所记载的上次执行anacron 的时间,两者比较后若发现有差异, 那就是在某些时刻没有进行crontab 啰!此时anacron 就会开始执行未进行的crontab 任务了!

anacron 与/etc/anacrontab

anacron 其实是一支程式并非一个服务!这支程式在CentOS 当中已经进入crontab 的排程喔!

同时anacron 会每个小时被主动执行一次喔!咦!每个小时?所以anacron 的设定档应该放置在/etc/cron.hourly 吗?嘿嘿!您真内行~赶紧来瞧一瞧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

[root@study ~]# cat /etc/cron.hourly/0anacron
#!/bin/sh
# Check whether 0anacron was run today already
if test -r /var/spool/anacron/cron.daily; then
day=`cat /var/spool/anacron/cron.daily`
fi
if [ `date +%Y%m%d` = "$day" ]; then
exit 0;
fi
# 上面的语法在检验前一次执行anacron 时的时间戳记!

# Do not run jobs when on battery power
if test -x /usr/bin/on_ac_power; then
/usr/bin/on_ac_power >/dev/null 2>&1
if test $? -eq 1; then
exit 0
fi
fi
/usr/sbin/anacron -s
# 所以其实也仅是执行anacron -s 的指令!因此我们得来谈谈这支程式!

》》》anacron 使用语法

1
2
3
4
5
6
7
8
[root@study ~]# anacron [-sfn] [job].. 
[root@study ~]# anacron -u [job]..
选项与参数:
-s :开始一连续的执行各项工作(job),会依据时间记录档的资料判断是否进行;
-f :强制进行,而不去判断时间记录档的时间戳记;
-n :立刻进行未进行的任务,而不延迟(delay) 等待时间;
-u :仅更新时间记录档的时间戳记,不进行任何工作。
job :由/etc/anacrontab 定义的各项工作名称。

在我们的CentOS 中,anacron 的进行其实是在每个小时都会被抓出来执行一次, 但是为了担心anacron 误判时间参数,因此/etc/cron.hourly/ 里面的anacron 才会在档名之前加个0 (0anacron),让anacron 最先进行!就是为了让时间戳记先更新!以避免anacron 误判crontab 尚未进行任何工作的意思。

接下来我们看一下anacron 的设定档: /etc/anacrontab 的内容好了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

[root@study ~]# cat /etc/anacrontab
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
RANDOM_DELAY=45 #随机给予最大延迟时间,单位是分钟
START_HOURS_RANGE=3-22 #延迟多少个小时内应该要执行的任务时间

1 5 cron.daily nice run-parts /etc/cron.daily
7 25 cron.weekly nice run-parts /etc/cron.weekly
@monthly 45 cron.monthly nice run-parts /etc/cron.monthly
天数延迟时间工作名称定义实际要进行的指令串
# 天数单位为天;延迟时间单位为分钟;工作名称定义可自订,指令串则通常与crontab 的设定相同!

[root@study ~]# more /var/spool/anacron/*
::::::::::::::
/var/spool/anacron/cron.daily
::::::::::::::
20150731
::::::::::::::
/var/spool/anacron/cron.monthly
::::::::::::::
20150703
::::::::::::::
/var/spool/anacron/cron.weekly
::::::::::::::
20150727
# 上面则是三个工作名称的时间记录档以及记录的时间戳记

我们拿/etc/cron.daily/ 那一行的设定来说明好了。那四个栏位的意义分别是:

  • 天数:anacron 执行当下与时间戳记(/var/spool/anacron/ 内的时间纪录档) 相差的天数,若超过此天数,就准备开始执行,若没有超过此天数,则不予执行后续的指令。
  • 延迟时间:若确定超过天数导致要执行排程工作了,那么请延迟执行的时间,因为担心立即启动会有其他资源冲突的问题吧!
  • 工作名称定义:这个没啥意义,就只是会在/var/log/cron 里头记载该项任务的名称这样!通常与后续的目录资源名称相同即可。
  • 实际要进行的指令串:有没有跟0hourly 很像啊!没错!相同的作法啊!透过run-parts 来处理的!

根据上面的设定档内容,我们大概知道anacron 的执行流程应该是这样的(以cron.daily 为例):

1、由/etc/anacrontab 分析到cron.daily 这项工作名称的天数为1 天;
2、由/var/spool/anacron/cron.daily 取出最近一次执行anacron 的时间戳记;
3、由上个步骤与目前的时间比较,若差异天数为1 天以上(含1 天),就准备进行指令;
4、若准备进行指令,根据/etc/anacrontab 的设定,将延迟5 分钟+ 3 小时(看START_HOURS_RANGE 的设定);
5、延迟时间过后,开始执行后续指令,亦即『 run-parts /etc/cron.daily 』这串指令;
6、执行完毕后, anacron 程式结束。

如此一来,放置在/etc/cron.daily/内的任务就会在一天后一定会被执行的!因为anacron是每个小时被执行一次嘛!所以,现在你知道为什么隔了一阵子才将CentOS开机,开机过后约1小时左右系统会有一小段时间的忙碌!而且硬碟会跑个不停!那就是因为anacron正在执行过去/etc/cron.daily/, /etc/cron.weekly/, /etc/cron.monthly/里头的未进行的各项工作排程啦!这样对anacron有没有概念了呢?^_^

最后,我们来总结一下本章谈到的许多设定档与目录的关系吧!这样我们才能了解crond 与anacron 的关系:

1、crond 会主动去读取/etc/crontab, /var/spool/cron/, /etc/cron.d/ 等设定档,并依据『分、时、日、月、周』的时间设定去各项工作排程;
2、根据/etc/cron.d/0hourly 的设定,主动去/etc/cron.hourly/ 目录下,执行所有在该目录下的执行档;
3、因为/etc/cron.hourly/0anacron 这个指令档的缘故,主动的每小时执行anacron ,并呼叫/etc/anacrontab 的设定档;
4、根据/etc/anacrontab 的设定,依据每天、每周、每月去分析/etc/cron.daily/, /etc/cron.weekly/, /etc/cron.monthly/ 内的执行档,以进行固定周期需要执行的指令。

也就是说,如果你每个周日的需要执行的动作是放置于/etc/crontab 的话,那么该动作只要过期了就过期了,并不会被抓回来重新执行。但如果是放置在/etc/cron.weekly/ 目录下,那么该工作就会定期,几乎一定会在一周内执行一次~如果你关机超过一周,那么一开机后的数个小时内,该工作就会主动的被执行喔!真的吗?对啦!因为/etc/anacrontab 的定义啦!

基本上,crontab 与at 都是『定时』去执行,过了时间就过了!不会重新来一遍~那anacron 则是『定期』去执行,某一段周期的执行~ 因此,两者可以并行,并不会互相冲突啦!

&:后台执行

1
2
3
4
5
6
7
8
------------------------ls
sh
------------------------tar -zcf sh.tar.gz sh &
[4] 32658 #===> 4 表示作业号,32658 表示 PID
------------------------
[4] 完成 tar -zcf sh.tar.gz sh #===> 后台工作 4 完成
------------------------ls
sh sh.tar.gz

jobs:后台列表

1
2
3
4
5
6
7
8
9
10
11
12
------------------------jobs #===> 列出所有后台程序
[1] 运行中 java -jar junit.jar &
[2]- 已停止 vim /etc/bashrc
[3]+ 已停止 vim /root/.bashrc
------------------------jobs -l #===> 显示 PID
[1] 31619 运行中 java -jar junit.jar &
[2]- 32256 停止 vim /etc/bashrc
[3]+ 32454 停止 vim /root/.bashrc

[n] 这个 n 代表作业号,用于恢复。作业号会越来越大
+ 加号 表示最后一个后台程序,bgfg 会直接恢复这个
- 减号 表示倒数第二个后台程序,bf,fg恢复之后,这个-会变成+成为倒数第一个程序

fg,bg:工作恢复

1
2
3
4
5
6
7
8
fg(foreground 前台) bf(background)后台


直接执行 fg/bg 会把 jobs 的 + 在前台/后台继续执行

fg %n/bg %n 把工作号 n 恢复前台/后台运行(% 百分号可以省略)

注意:例如 top 这种前台进程,不可以使用 bg 来恢复。

kill:杀死进程

1
一般使用 kill -9 PID 来杀死进程,也可以使用 kill -9 %n 来杀死后台任务

ps:所有 PID

1
2
3
4
5
6
7
8
------------------------ps aux | head | cat -n | sed '2d'
1 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
3 root 2 0.0 0.0 0 0 ? S 11月26 0:00 [kthreadd]
...
10 root 12 0.0 0.0 0 0 ? S 11月26 0:00 [rcuob/3]
------------------------ps aux | grep java #===> 常用
root 31619 0.7 6.4 2511760 121348 pts/0 Sl 09:58 0:10 java -jar junit.jar
root 32893 0.0 0.0 112660 964 pts/0 R+ 10:21 0:00 grep --color=auto java

top:动态观察程序变化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
------------------------top
top - 10:24:07 up 3 days, 5:50, 1 user, load average: 0.00, 0.01, 0.05
Tasks: 354 total, 2 running, 350 sleeping, 2 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.3 si, 0.0 st
KiB Mem : 1868660 total, 794816 free, 332484 used, 741360 buff/cache
KiB Swap: 3145724 total, 3145724 free, 0 used. 1286764 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
33033 root 20 0 148432 2300 1436 R 0.7 0.1 0:00.02 top
1436 root 20 0 561784 18572 5864 S 0.3 1.0 0:42.15 tuned
1444 root 20 0 495520 44800 15676 S 0.3 2.4 1:47.47 containerd
31619 root 20 0 2511760 121348 13432 S 0.3 6.5 0:11.32 java
1 root 20 0 54564 6856 4088 S 0.0 0.4 0:43.82 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.14 kthreadd
...
  • 监控指定 pid:top -p pid
    进程不会占用CPU,只有进程只是占用资源,线程才会占用CPU,进入之后输入 H 即可查看动单个进程下的所有线程。

pstree:程序树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

[root@study ~]# pstree [-A|U] [-up]
选项与参数:
-A :各程序树之间的连接以ASCII字元来连接;
-U :各程序树之间的连接以万国码的字元来连接。在某些终端介面下可能会有错误;
-p :并同时列出每个process的PID;
-u :并同时列出每个process的所属帐号名称。



------------------------pstree
systemd─┬─NetworkManager───2*[{NetworkManager}]
├─agetty
├─atd
├─auditd───{auditd}
├─containerd───8*[{containerd}]
├─crond
├─dbus-daemon───{dbus-daemon}
├─dockerd───7*[{dockerd}]
├─firewalld───{firewalld}
├─lvmetad
├─master─┬─pickup
│ └─qmgr
├─polkitd───5*[{polkitd}]
├─rsyslogd───2*[{rsyslogd}]
├─sshd───sshd───bash─┬─java───25*[{java}]
│ ├─pstree
│ └─2*[vim]
├─systemd-journal
├─systemd-logind
├─systemd-udevd
├─tuned───4*[{tuned}]
└─wpa_supplicant
------------------------pstree -p | grep java #===> 显示 PID
| |-java(31619)-+-{java}(31620)
| | |-{java}(31621)
...
| | |-{java}(31647)
| | |-{java}(31648)
| | `-{java}(31649)

nohup:非bash后台执行

1
2
3
4
5
6
nohup 命令   #===> 前台执行
nohup 命令 & #===> 后台运行

例如:nohup java -jar app.jar >> log &

命令可以重定向文件,否则自动会同步到 ”~/nohup.out“

uname:系统与核心信息

1
2
3
4
5
6
7
8
9
10
11
[root@study ~]# uname [-asrmpi]
选项与参数:
-a :所有系统相关的资讯,包括底下的资料都会被列出来;
-s :系统核心名称
-r :核心的版本
-m :本系统的硬体名称,例如i686或x86_64等;
-p :CPU的类型,与-m类似,只是显示的是CPU的类型!
-i :硬体的平台(ix86)

------------------------uname -r #===> 常用,查看核心版本
3.10.0-327.el7.x86_64

uptime:显示工作负载

和 top 上面显示差不多

1
2
------------------------uptime
10:31:57 up 3 days, 5:58, 1 user, load average: 0.00, 0.01, 0.05

netstat:显示网络,端口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@study ~]# netstat -[atunlp]
选项与参数:
-a :将目前系统上所有的连线、监听、Socket资料都列出来
-t :列出tcp网路封包的资料
-u :列出udp网路封包的资料
-n :不以程序的服务名称,以埠号(port number)来显示;
-l :列出目前正在网路监听(listen)的服务;
-p :列出该网路服务的程序PID


------------------------netstat -atlunp #===> 常用
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1438/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1918/master
tcp 0 0 192.168.1.3:22 192.168.1.8:57836 ESTABLISHED 29999/sshd: root@pt
tcp6 0 0 :::8080 :::* LISTEN 31619/java
tcp6 0 0 :::22 :::* LISTEN 1438/sshd
tcp6 0 0 ::1:25 :::* LISTEN 1918/master
tcp6 0 0 :::2375 :::* LISTEN 2066/dockerd

fuser:根据文件查进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[root@study ~]# fuser [-umv] [-k [i] [-signal]] file/dir
选项与参数:
-u :除了程序的PID之外,同时列出该程序的拥有者;
-m :后面接的那个档名会主动的上提到该档案系统的最顶层,对umount不成功很有效!
-v :可以列出每个档案与程序还有指令的完整相关性!
-k :找出使用该档案/目录的PID ,并试图以SIGKILL这个讯号给予该PID;
-i :必须与-k配合,在删除PID之前会先询问使用者意愿!
-signal:例如-1 -15等等,若不加的话,预设是SIGKILL (-9)啰!



------------------------fuser /linux/junit.jar #===> 默认显示 PID
/linux/junit.jar: 31619m
------------------------fuser -uv /linux/junit.jar #===> 显示PID,用户
用户 进程号 权限 命令
/linux/junit.jar: root 31619 f...m (root)java
------------------------jobs -l
[1]+ 31619 运行中 java -jar junit.jar & #===> 就是该 PID


c :此程序在当前的目录下(非次目录);
e :可被触发为执行状态;
f :是一个被开启的档案;
r :代表顶层目录(root directory);
F :该档案被开启了,不过在等待回应中;
m :可能为分享的动态函式库;

lsof:显示程序所用档案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@study ~]# lsof [-aUu] [+d]
选项与参数:
-a :多项资料需要『同时成立』才显示出结果时!
-U :仅列出Unix like系统的socket档案类型;
-u :后面接username,列出该使用者相关程序所开启的档案;
+d :后面接目录,亦即找出某个目录底下已经被开启的档案!

------------------------lsof | grep java | head
java 31619 root cwd DIR 253,0 34 17409854 /linux
java 31619 root rtd DIR 253,0 4096 128 /
java 31619 root txt REG 253,0 8984 34516930 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64/jre/bin/java
java 31619 root mem REG 253,0 52496 383950 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64/jre/lib/amd64/libmanagement.so
java 31619 root mem REG 253,0 106065056 33621379 /usr/lib/locale/locale-archive
java 31619 root mem REG 253,0 4003855 34516943 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64/jre/lib/ext/cldrdata.jar
java 31619 root mem REG 253,0 81139 16863787 /usr/share/locale/zh_CN/LC_MESSAGES/libc.mo
java 31619 root mem REG 253,0 123176 383952 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64/jre/lib/amd64/libnet.so
java 31619 root mem REG 253,0 100064 383953 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64/jre/lib/amd64/libnio.so
java 31619 root mem REG 253,0 73861866 50506762 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64/jre/lib/rt.jar

pidof:根据程序找出 PID

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@study ~]# pidof [-sx] program_name
选项与参数:
-s :仅列出一个PID而不列出所有的PID
-x :同时列出该program name可能的PPID那个程序的PID


------------------------jobs -l
[1]- 31619 运行中 java -jar junit.jar &
[2]+ 34086 运行中 java -jar -Dserver.port=8081 junit.jar &
------------------------
------------------------pidof java #===> 显示多个 PID
34086 31619
------------------------fuser /linux/junit.jar
/linux/junit.jar: 31619m 34086m
------------------------
------------------------fuser -uv /linux/junit.jar
用户 进程号 权限 命令
/linux/junit.jar: root 31619 f...m (root)java
root 34086 f...m (root)java
------------------------ps aux | grep java #===> 常用 grep 行搜索
root 31619 0.4 6.5 2511760 121548 pts/0 Sl 09:58 0:14 java -jar junit.jar
root 34086 5.4 5.9 2509712 111780 pts/0 Sl 10:44 0:07 java -jar -Dserver.port=8081 junit.jar
root 34236 0.0 0.0 112660 964 pts/0 R+ 10:46 0:00 grep --color=auto java

SELinux:增强Linux

(Security Enhanced Linux 安全增强Linux)
因为 root 可以为所欲为。所用让某些文件不能访问,即使是 root 也行。

省略。。。详情可以参考SELinux 初探