鸟哥的 Linux 私房菜阅读笔记

写在前面:

本文是阅读 《鸟哥的 Linux 私房菜:基础学习篇》 一书的笔记,记录了个人认为的一些重点内容,供本人后续查阅参考。对于偏向于系统运维方面的内容,则未记录。

《鸟哥的 Linux 私房菜:基础学习篇》无疑是入门 Linux 命令行的一本好书,但是在个人阅读过程中,发现本书部分内容更偏向于系统运维人员,而不是大多数的普通程序员。当然,技多不压身,不过,对于仅想了解与日常编程开发紧密相关的命令行内容的读者,不妨选择性地跳跃阅读本书,或者找一些更符合您需求的资料。

1. Linux 是什么与如何学习

1.1 Linux 是什么

  • 1991 年 Linus 编写
  • 操作系统发展历程
    1. 1969 年,批处理操作系统
    2. 分时操作系统, “Multics” 系统
    3. 1973 年,UNIX 正式诞生; 1977 年,BSD 诞生; 1979 年,System V,引发商业纠纷
    4. 1984 年,Minix 系统开始编写 ; GNU (GNU's Not Unix)计划(斯托曼发起)与 FSF(Free Software Foundation)基金会成立
    5. 1988 年,图形用户界面模式 XFree86 计划
    6. 1991 年,芬兰 Linus Torvalds 发布 Linux 内核
  • 常见的开放源代码的授权(详细介绍
    1. Apache Lisense 2.0
    2. BSD
    3. GPL
    4. MIT License
    5. ……

1.2 托瓦兹的 Linux 的发展

  • 1994 年,Linux 内核正式版, Version 1.0

  • 1996 年, 2.0 版

  • 2011 年,3.0 版

  • 2015 年 4 月,4.0 版

  • 查看内核版本命令: uname -r

  • Linux 发行版分类(按照包管理工具)

    1. RPM 方式管理:Red Hat、Fedora、SUSE 等
    2. dpkg 方式管理:Debian、Ubuntu、B2D 等
    3. 其他:Gentoo

1.3 Linux 该如何学习

  1. Linux 的安装与命令
  2. Linux 操作系统的基础技能:用户用户组、权限的概念等
  3. vi 文本编辑器
  4. Shell 与 Shell 脚本的学习
  5. 软件管理
  6. 网络基础

核心:实践 动手

2. 主机规划与磁盘划分

2.1 Linux 与硬件的搭配

2.2 磁盘分区

P69 - P82 略

2.3 安装 Linux 前的规划

确定用途 -> 磁盘分区

3. 安装 CentOS7.x

略 见第 3 章

4. 首次登录与在线求助

4.1 首次登录系统

4.2 命令行模式下命令的执行

4.2.1 开始执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 命令一般格式
$ command [-options] parameter1 parameter2 ...
# 命令 选项 参数

# 示例:查看home目录下所有文件(包括隐藏文件 -a)及其属性(-l)
$ ls -al ~
$ ls -al ~
$ ls -a -l ~

# 查看日期和时间
$ date

# 查看语言语系
$ locale

番外:

如何使用Windows ssh 连接远程CentOS主机?

CentOS 端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 查看本机是否安装SSH软件包
$ rpm -qa | grep ssh

# 如果没有安装SSH
$ yum install openssh-server

# 开启SSH服务
$ service sshd start

# 查看22号端口是否开启
$ netstat -ntpl | grep 22

# 修改服务器端断开连接时长
$ vi /etc/ssh/sshd_config
找到
#ClientAliveInterval 0
#ClientAliveCountMax 3
修改为
ClientAliveInterval 60
ClientAliveCountMax 5
然后重启sshd服务
$ systemctl restart sshd

Windows 端:

1
2
3
$ ssh username@hostname(hostIP) 
# 命令 用户名@主机名或者IP地址
# 然后输入密码即可连接远程主机

4.2.2 基础命令的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ date		# 查看时间日期
$ cal # 查看日历(calender)
$ bc # 计算器

# date 具体
$ date +%Y/%m/%d # 以 year/month/day 格式输出 如“2021/03/22”
$ date +%H:%M # 以 hour:minute 格式输出 如“23:49”

# cal 具体
$ cal year # 显示 year 年的日历
$ cal [month] [year] # 显示某年某月日历

# bc 简单计算器
$ bc # 支持 + - * / ^ % quit 退出计算器
# bc 默认输出整数 scale = n 使输出小数点后 n 位

4.2.3 重要的几个热键 [Tab]、[Ctrl]-c、[Ctrl]-d

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 键
# 命令补全
$ ca[Tab][Tab] # 输入 ca 后按两次 Tab 键,终端输出以 ca 为前缀的命令
# 输出如下:
cacertdir_rehash cache_restore ca-legacy cancel cat
cache_check cache_writeback calibrate_ppa cancel.cups catchsegv
cache_dump cagent_tools caller capsh catman
cache_metadata_size cairo-sphinx canberra-boot captoinfo
cache_repair cal canberra-gtk-play case

# 文件补齐
$ ls -al .bash[Tab][Tab] # 自动补齐文件名
# 输出如下
.bash_history .bash_logout .bash_profile .bashrc

# 选项/参数补齐
$ date --[Tab][Tab] # 自动补齐参数
# 输出如下
--date --help --reference= --rfc-3339= --universal
--date= --iso-8601 --rfc-2822 --set= --version

# [Ctrl]-c
作用:终端正在运行中的命令

# [Ctrl]-d
作用:
1、相当于 exit 命令
2、代表键盘输入结束(End Of File)

# [Shift] + {[Page UP][Page Down]}
作用:用于命令行输出的翻页

4.3 Linux 系统的在线求助 man page 和 info page

4.3.1 命令的 --help 求助说明

1
2
$ date --help
# 查询已经使用过的命令选项

4.3.2 man page

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ man date
# man 为 manual(操作说明)的缩写
# 按下 空格键 翻页;q 退出
# 向下查找词语: 输入 “/word” ,然后按 Enter 键,即可查找 “word”
# 向上查找词语: 输入 “?word” ,然后按 Enter 键,即可查找 “word”

# 查看 man 的帮助
$ man man

# 查看系统中和 man 命令有关的说明文件
$ man -f man
$ whatis [命令或文件] # 等价于上面 man -f ...
man (7) - 格式化手册页的宏
man (1) - 格式化并显示在线帮助手册页
man (1p) - display system documentation
$ man 1 man # 根据上面输出查看指定说明文件

# 找到系统中说明文件,只有有 man 关键字就将该说明列出来
$ man -k man
$ apropos [命令或文件] # 相当于 man -k ...

4.3.3 info page

1
2
$ info info
# 按 N 、 P 、 U 到下一个、上一个、上一层的节点

4.4 超简单的文本编辑器:nano

1
2
$ nano file.txt			# 打开 或者 创建 文件
# 打开 nano 之后下面的 "^X" 代表 Ctrl + X 键,完成一定功能

4.5 正确的关机方法

  1. 观察系统的使用状态

    1
    2
    3
    $ who  				# 查看当前谁在线
    $ netstat -a # 查看网络的联机状态
    $ ps -aux # 查看后台进程
  2. 通知在线用户关机的时刻

  3. 正确使用关机命令

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $ sync			# 将数据同步写入硬盘中mingl
    $ shutdown # 常用的关机命令

    # 重新启动、关机
    $ reboot
    $ halt
    $ poweroff

    # 以 root 身份登录
    $ su -
  4. shutdown 使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    $ shutdown [-krhc]	[时间] [警告信息]
    -k : 不关机,只发送警告信息
    -r : 将系统服务停掉之后就重新启动
    -h : 将系统服务停掉之后,立即关机
    -c : 取消已经在进行的 shutdown 命令内容
    时间 : 指定系统关机时间。默认一分钟后自动进行

    # 常用示例
    $ shutdown -h now # 立刻关机
    $ shutdown -h 20:25 # 今天 20:25 关机 (或次日20:25关机)
    $ shutdown -h +10 # 系统 10 分钟后自动关机
    $ shutdown -r now # 立刻重新启动
  5. reboot、 halt、 poweroff 使用

    1
    2
    $ halt			# 系统停止,屏幕可能会保留系统已经停止的信息
    $ poweroff # 系统关机,屏幕空白
  6. 实际使用管理工具 systemctl 关机

    1
    2
    3
    $ systemctl [命令]
    $ systemctl reboot # 系统重新启动
    $ systemctl poweroff # 系统关机

5. Linux 的文件权限与目录配置

5.1 用户与用户组

  1. 文件拥有者的三个层次:

    1. 文件所有者( User ):
    2. 用户组( Group ):每个用户可以有多个用户组的支持
    3. 其他人( Others ):
  2. 系统上所有账号相关信息记录在文件: /etc/passwd (可用 cat /etc/passwd 命令查看文件内容)

  3. 密码记录在: /etc/shadow 文件内

  4. 组名记录在: /etc/group 文件内

5.2 Linux 文件权限概念

5.2.1 Linux 文件属性

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
$ su -		# 切换到 root 权限
$ ls -al # list 查看文件名及相关属性
总用量 104
dr-xr-x---. 16 root root 4096 3月 22 23:14 .
dr-xr-xr-x. 20 root root 4096 3月 31 20:50 ..
-rw------- 1 root root 1955 3月 31 20:50 .bash_history
-rw-r--r--. 1 root root 18 12月 29 2013 .bash_logout
-rw-r--r--. 1 root root 176 12月 29 2013 .bash_profile
-rw-r--r--. 1 root root 176 12月 29 2013 .bashrc
drwxr-xr-x 9 root root 4096 3月 22 23:14 .cache
drwxr-xr-x 13 root root 4096 3月 22 23:14 .config
-rw-r--r--. 1 root root 100 12月 29 2013 .cshrc
drwx------ 3 root root 4096 3月 22 23:13 .dbus
-rw------- 1 root root 16 3月 22 23:13 .esd_auth
-rw------- 1 root root 310 3月 22 23:13 .ICEauthority
drwx------ 3 root root 4096 3月 22 23:13 .local
drwxr-xr-x 2 root root 4096 3月 8 22:34 .pip
-rw-r--r-- 1 root root 73 3月 8 22:34 .pydistutils.cfg
drwx------ 2 root root 4096 1月 21 2019 .ssh
-rw-r--r--. 1 root root 129 12月 29 2013 .tcshrc
-rw------- 1 root root 508 3月 8 22:37 .viminfo
-rw------- 1 root root 0 3月 22 23:14 .Xauthority

各字段解释如下:
-rw------- 1 root root 0 3月 22 23:14 .Xauthority
[1] [2] [3] [4] [5] [6] [7]

各字段含义解释如下:

  1. 文件类型权限:共有10个字符,第一个字符代表该文件是目录文件,或 链接文件

    第一个字符含义说明:

    d :目录(directory)

    - :文件

    l :链接文件(link file)

    b :设备文件里面的可供存储的周边设备(可按块随机读写的设备)

    c :设备文件里面的串行端口设备,如键盘、鼠标(一次性读写的设备)

    剩余字符,3个一组,均为 [rwx] 的组合,r 代表可读(read),w 代表可写(write),x 代表可执行(execute)。这三个权限的位置不会改变,若无权限,则为减号 [-] 。

    第一组为:文件拥有者 可具备的权限

    第二组为:加入此用户组之账号的权限

    第三组为:非本人且没有加入本用户组的其他账户的权限

  2. 链接数:表示有多少个文件名链接到此节点( inode ),即记录有多少个文件名链接到相同的 inode 号码

  3. 文件拥有者:表示文件(或目录)拥有者的账号

  4. 文件所属用户组:表示文件的所属用户组

  5. 文件大小:文件的容量大小,默认单位为字节 Bytes

  6. 文件最后被修改的时间:文件的创建时间或者最近修改时间(若修改时间据现在太久,则只显示年份)。如果要显示完整的时间格式,则需要使用 ls -l --full-time 命令

  7. 文件名:这个文件名,若文件名之前多一个 . ,则代表该文件为隐藏文件

5.2.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
# 修改文件所属用户组 (change group)
$ chgrp
$ chgrp [-R] dirName/fileName
-R : 进行递归修改,即将目录下子目录及文件都更新成为这个用户组

# 修改文件拥有者 (change own)
$ chown
$ chown [-R] 账号名称 dirName/fileName
$ chown [-R] 账号名称:用户组名称 dirName/fileName
# 复制文件后修改文件所有者示例:
$ cp 源文件 目标文件
$ chown 账号名称 目标文件名

# 修改文件的权限
$ chmod
# 1.数字类型修改文件权限
# 各权限对应数字如下:
# r : 4
# w : 2
# x : 1
# 则 rwx = 4 + 2 + 1 = 4 rw- = 4 + 2 + 0 = 6
$ chmod [-R] xyz 文件或目录
# 其中 xyz 为数字,例如:666 、 777、 640 等
# 示例:将一个 shell 文件修改为可执行,非拥有者用户可以执行、查看但不可以修改
$ chmod 755 fileName.sh

# 2.符号类型修改文件权限
# 格式
$ chmod [u | g | o | a] [+ | - | =] [r | w | x] [dirName | fileName]
# 含义:
# u :user g : group o : others a : all
# + :加入 - : 移除 = : 设置
# rwx : 读写执行

# 例:
$ chmod u=rwx,go=rx .bashrc
$ chmod u=rwx,g=rx,o=r fileName
$ chmod a+w fileName # 令 fileName 对所有人都可以写
$ chmod a-x fileName # 去除所有人执行权限
# 给某文件添加执行权限
$ chmod a+x fileName

5.2.3 目录与文件的权限意义

文件

r : 读取文件实际内容

w: 编辑、修改、新增文件内容(不能删除该文件)

e : 文件可以被系统执行 (与文件名无关)

目录

r : 读取目录结构列表 (可以使用 ls 命令查看目录内容列表)

w : 可以改动目录结构列表,即建立新文件与目录、删除已经存在文件和目录、重命名文件和目录、移动该目录内文件和目录位置 (与该目录下文件名的变动有关)

x : 代表用户能否进入该目录成为工作目录,工作目录及当前所在的目录

能不能进入目录,只与该目录执行权限有关

要开放目录给任何人浏览时,应该开放 r 和 x 权限, w 权限不可随便给

要读一个文件时,至少应具备该文件所在目录 r 和 x 权限。

5.2.4 Linux 文件种类与扩展名

  1. 文件种类:

    1. 常规文件(regular file):ls -al 命令输出的第一个字符 - ,如 -rwxrwxrwx
      • 纯文本文件(ASCII):
      • 二进制文件(binary):可执行文件等
      • 数据文件(date):有些程序在运行时会读取某些特定格式的文件,这些文件被称为数据文件 。
    2. 目录(directory):第一个属性为 d
    3. 链接文件(link):第一个属性为 l ,如 lrwxrwxrwx ,类似于 Windows 下快捷方式 。
    4. 设备与设备文件(device):与系统周边及存储等相关的一些文件,通常集中在 /dev 目录下,又可分为:
      • 区块(block)设备文件:第一个属性为 d ,可供系统随机存取的接口设备 。
      • 字符(character)设备文件:一些串行端口的接口设备,如键盘、鼠标等,特色:一次性读取,不能够截断输出,第一个属性为 c
    5. 数据接口文件(sockets):用于网络上的数据交换,第一个属性为 s ,通常可在 /run/tmp 这些目录中可看到这种文件类型 。
    6. 数据输送文件(FIFO,pipe):主要目的:用于解决多个进程同时读写一个文件所造成的的错误问题,即管道,第一个属性为 p
  2. Linux 文件扩展名:Linux 下文件无所谓文件扩展名,扩展名只能用来大概了解文件是什么类型。

    常用文件扩展名

    1. *.sh :脚本或批处理文件
    2. *.Z、*.tar、、*.tar.gz、*.zip、*.tgz :压缩文件
    3. 、*.html、*.php :网页相关文件
  3. Linux 文件名长度限制:单一文件或目录的最大允许文件名为 255 字节

  4. Linux 文件名的限制:

    不能包含以下字符:

    * ? > < ; & ! [ ] | \ ' " ` ( ) { } - +

5.3 Linux 目录配置

5.3.1 Linux 目录配置的依据 —— FHS

FHS(Filesystem Hierarchy Standard):规定每个特定目录下应该放置什么数据


可分享 不可分享
不变(static) /usr (软件存放处) /etc (配置文件)
/opt (第三方辅助软件) /boot (启动与内核文件)
可变动(variable) /var/mail (用户邮箱) /var/run (程序相关)
/var/spool/news (新闻组) /var/lock (程序相关)

FHS 仅针对三个目录规定应该放置什么数据:

/ (root ,根目录):与启动系统有关

/usr (Unix software resource):与软件安装 / 执行有关

/var (variable):与系统运行过程有关

FHS 具体要求:图片来源:维基百科:文件系统层次结构标准

FHS

5.3.2 目录树

5.3.3 绝对路径与相对路径

5.3.4 CentOS 的观察

Linux Standard Base(LSB)

1
2
3
4
5
6
7
8
9
10
11
$ uname -r			# 查看内核版本
$ uname -m # 查看操作系统架构版本

# 查看 LSB 标准
$ yum install redhat-lsb # 安装软件
$ lsb_relase -a # 查看 LSB 标准
LSB Version: :core-4.1-amd64:core-4.1-noarch:cxx-4.1-amd64:cxx-4.1-noarch:desktop-4.1-amd64:desktop-4.1-noarch:languages-4.1-amd64:languages-4.1-noarch:printing-4.1-amd64:printing-4.1-noarch
Distributor ID: CentOS
Description: CentOS Linux release 7.5.1804 (Core)
Release: 7.5.1804
Codename: Core

6. Linux 文件与目录管理

6.1 目录与路径

1
2
3
4
$ cd
$ pwd
$ mkdir [-p]
$ rmdir [-p]

显示环境变量:

1
2
3
4
$ echo $PATH

# 给 PATH 环境变量添加路径:
$ PATH="${PATH}:/root"

6.2 文件与目录管理

ls 命令常用选项:

1
2
3
4
5
-a :全部的文件,连同隐藏文件(开头为 . 的文件)一起列出来
-d :仅列出目录本身,而不是列出目录内的文件数据
-h :将文件大小以人类较易读的方式(例如 GB, KB等等)列出来
-l :长数据串行出,包含文件的属性与权限等等数据
-i :列出 inode 号码

其他命令:

1
2
3
4
5
$ cp
$ mv
$ rm
$ basename # 获取路径的文件名
$ dirname # 获取路径的目录名

6.3 文件内容查看

1
2
3
4
5
6
7
8
$ cat
$ tac # 从最后一行开始显示文件内容
$ nl # 同时输出行号
$ more # 一页一页显示文件内容
$ less # 与 more 类似,但可以向前翻页
$ head
$ tail
$ od # 以二进制形式读取文件内容

6.4 文件与目录的默认权限与隐藏权限

umask:指定目前用户在建立文件或目录时的默认权限,具体指要被拿掉的权限。

1
2
3
4
$ umask
$ umask -S
# 设置 umask
$ umask 002

文件的隐藏属性:

1
2
$ chattr		# 配置文件隐藏属性
$ lsattr # 显示文件隐藏属性

查看文件类型命令:file

6.5 命令与文件的查找

1
2
3
4
5
6
7
$ which [-a] 命令名

$ wnereis [-bmsu] 文件或目录名
$ locate [-ir] keyword
$ updatedb # 更新数据库

$ find [PATH] [option] [action]

7. Linux 磁盘与文件系统管理

7.1 文件系统的简单操作

1
2
3
4
5
6
7
$ df	# 列出文件系统的整体磁盘使用量
-h : 以人们较易阅读的 GBytes, MBytes, KBytes 等格式自行显示
-i : 不用磁盘容量,而以 inode 的数量来显示

$ du # 查看文件系统的磁盘使用量
-h : 同上
-s : 仅列出总量,而不列出每个各别的目录占用容量

链接:

1
2
3
$ ln [-sf] 源文件 目标文件
-s :有 -s 选项为符号链接;否则为硬链接
-f : 目标文件存在时,就主动的将目标文件直接删除后再创建

7.2 磁盘的分区、格式化、检验与挂载

1
2
3
4
5
$ gdisk 设备名称


$ mount [-t 文件系统] UUID='' 挂载点
$ umount [-fn] 设备文件名或挂载点

8. 文件与文件系统的压缩

8.1 Linux 系统常见的压缩命令

.Z compress 程序压缩的文件; .zip zip 程序压缩的文件; .gz gzip 程序压缩的文件; .bz2 bzip2 程序压缩的文件; .xz xz 程序压缩的文件; .tar tar 程序打包的数据,并没有压缩过; .tar.gz tar 程序打包的文件,其中并且经过 gzip 的压缩 .tar.bz2 tar 程序打包的文件,其中并且经过 bzip2 的压缩 *.tar.xz tar 程序打包的文件,其中并且经过 xz 的压缩

gzip 命令:

1
2
3
4
5
6
$ gzip	[-cdtv#] 文件名
-c :将压缩的数据输出到屏幕上,可通过数据流重导向来处理;
-d :解压缩的参数;
-t :可以用来检验一个压缩文件的一致性,看看文件有无错误;
-v :可以显示出原文件/压缩文件的压缩比等信息;
-## 为数字的意思,代表压缩等级,-1 最快,但是压缩比最差、-9 最慢,但是压缩比最好!默认是 -6

8.2 打包命令:tar

最简单的使用 tar 就只要记忆下面的方式即可:

  • 压 缩:tar -jcv -f filename.tar.bz2 要被压缩的文件或目录名称
  • 查 询:tar -jtv -f filename.tar.bz2
  • 解压缩:tar -jxv -f filename.tar.bz2 -C 欲解压缩的目录

9. vim 程序编辑器

9.1 vi 的使用

三种模式:

  • 一般命令模式
  • 编辑模式
  • 命令行模式

9.2 vim 的额外功能

  • 可视区块:以列为单位编辑
  • 多文件编辑
  • 多窗口功能

10. 认识与学习 BASH

10.1 认识 BASH 这个 SHELL

快捷键:

组合键 功能与示范
[ctrl]+u/[ctrl]+k 分别是从游标处向前删除指令串([ctrl]+u) 及向后删除指令串([ctrl]+k)。
[ctrl]+a/[ctrl]+e 分别是让游标移动到整个指令串的最前面([ctrl]+a) 或最后面([ctrl]+e)。

10.2 Shell 的变量功能

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ echo $PATH	# PATH 即为变量,使用时签名需加上 $
# 或者:
$ echo ${PATH} # recommend

$ echo ${MAIL}
$ echo ${HOME}

# 设置变量值
$ var_name=value
# 取消设置变量
$ unset var_name

$ export var_name # 以 export 来使变量变成环境变量


$ read [-pt] var_name # 读取键盘输入的变量值

$ declare [-aixr] var_name
选项与参数:
-a :将后面名为variable 的变数定义成为阵列(array) 类型
-i :将后面名为variable 的变数定义成为整数数字(integer) 类型
-x :用法与export 一样,就是将后面的variable 变成环境变数;
-r :将变数设定成为readonly 类型,该变数不可被更改内容,也不能unset

10.3 命令别名与历史

1
2
3
4
5
6
7
8
9
10
11
12
13
$ alias lm='ls -al | more'
$ unalias lm

$ history [n]
$ history [-c]
$ history [-raw] histfiles
选项与参数:
n :数字,意思是『要列出最近的 n 条命令列表』的意思!
-c :将目前的shell 中的所有history 内容全部消除
-a :将目前新增的history 指令新增入histfiles 中,若没有加histfiles ,
则预设写入~/.bash_history
-r :将histfiles 的内容读到目前这个shell 的history 记忆中;
-w :将目前的history 记忆内容写入histfiles 中

10.4 Bash shell 的操作环境

命令查找顺序:

  1. 以相对/绝对路径执行指令,例如『 /bin/ls 』或『 ./ls 』;
  2. 由alias 找到该指令来执行;
  3. 由bash 内建的(builtin) 指令来执行;
  4. 透过$PATH 这个变量的顺序搜寻到的第一个指令来执行。
1
$ source ~/.bashrc		# 读入环境配置文件

10.5 数据流重定向

  1. 标准输入(stdin) :代码为0 ,使用< 或<< ;
  2. 标准输出(stdout):代码为1 ,使用> 或>> ;
  3. 标准错误输出(stderr):代码为2 ,使用2> 或2>> ;
1
2
3
4
5
6
$ find /home -name .bashrc 2> /dev/null		# dev/null 垃圾桶黑洞装置与特殊写法


$ command1; command2; command3 # ; 依次连续执行多条命令
$ cmd1 && cmd2
$ cmd1 || cmd2
指令下达情况 说明
cmd1 && cmd2 1. 若cmd1 执行完毕且正确执行($?=0),则开始执行cmd2。 2. 若cmd1 执行完毕且为错误($?≠0),则cmd2 不执行。
cmd1 || cmd2 1. 若cmd1 执行完毕且正确执行($?=0),则cmd2 不执行。 2. 若cmd1 执行完毕且为错误($?≠0),则开始执行cmd2。

10.6 管道命令

  • 管道命令仅会处理standard output,对于standard error output 会予以忽略
  • 管道命令必须要能够接受来自前一个指令的资料成为standard input 继续处理才行。
1
2
3
4
5
$ split [-bl] file PREFIX
选项与参数:
-b :后面可接欲分割成的档案大小,可加单位,例如b, k, m 等;
-l :以行数来进行分割。
PREFIX :代表前置字元的意思,可作为分割档案的前导文字。

11. 正则表达式与文件格式化处理

11.1 基础正则表达式

特殊符号 代表意义
[:alnum:] 代表英文大小写字元及数字,亦即0-9, A-Z, a-z
[:alpha:] 代表任何英文大小写字元,亦即A-Z, a-z
[:blank:] 代表空白键与[Tab] 按键两者
[:cntrl:] 代表键盘上面的控制按键,亦即包括CR, LF, Tab, Del 等等
[:digit:] 代表数字而已,亦即0-9
[:graph:] 除了空白字元(空白键与[Tab] 按键) 外的其他所有按键
[:lower:] 代表小写字元,亦即a-z
[:print:] 代表任何可以被列印出来的字元
[:punct:] 代表标点符号(punctuation symbol),亦即:" ' ? ! ; : # $
[:upper:] 代表大写字元,亦即A-Z
[:space:] 任何会产生空白的字元,包括空白键, [Tab], CR 等等
[:xdigit:] 代表16 进位的数字类型,因此包括: 0-9, A-F, a-f 的数字与字元
  • 查找特定字符串
  • 利用中括号 [] 来查找集合字符
  • 行首和行尾字符 ^$
  • 任意一个字符 . 与重复字符 *
  • 限定连续 RE 字符范围 {} (需使用转义字符\

基础的正规表示法特殊字符汇整如下:

E 字符 意义与范例
^word 意义:待搜寻的字串(word)在行首!
范例:搜寻行首为# 开始的那一行,并列出行号
grep -n '^#' regular_express.txt
word$ 意义:待搜寻的字串(word)在行尾!
范例:将行尾为! 的那一行列印出来,并列出行号
grep -n '!$' regular_express.txt
. 意义:代表『一定有一个任意字元』的字符!
范例:搜寻的字串可以是(eve) (eae) (eee) (ee), 但不能仅有(ee) !亦即e 与e 中间『一定』仅有一个字元,而空白字元也是字元!
grep -n 'ee' regular_express.txt
\ 意义:透过shell 的跳脱字符,将特殊符号的特殊意义去除!
范例:搜寻含有单引号' 的那一行!
grep -n ' regular_express.txt
* 意义:重复零个到无穷多个的前一个RE 字符
范例:找出含有(es) (ess) (esss) 等等的字串,注意,因为* 可以是0 个,所以es 也是符合带搜寻字串。另外,因为* 为重复『前一个RE 字符』的符号, 因此,在* 之前必须要紧接着一个RE 字符喔!例如任意字元则为『.*』 !
grep -n 'ess*' regular_express.txt
[list] 意义:字元集合的RE 字符,里面列出想要撷取的字元!
范例:搜寻含有(gl) 或(gd) 的那一行,需要特别留意的是,在[] 当中『谨代表一个待搜寻的字元』, 例如『 a[afl]y 』代表搜寻的字串可以是aay, afy, aly 即[afl] 代表a 或f 或l 的意思!
grep -n 'g[ld]' regular_express.txt
[n1-n2] 意义:字元集合的RE 字符,里面列出想要撷取的字元范围!
范例:搜寻含有任意数字的那一行!需特别留意,在字元集合[] 中的减号- 是有特殊意义的,他代表两个字元之间的所有连续字元!但这个连续与否与ASCII 编码有关,因此,你的编码需要设定正确(在 bash 当中,需要确定LANG 与LANGUAGE 的变数是否正确!) 例如所有大写字元则为[AZ]
grep -n '[AZ]' regular_express.txt
[^list] 意义:字元集合的RE 字符,里面列出不要的字串或范围!
范例:搜寻的字串可以是(oog) (ood) 但不能是(oot) ,那个^ 在[] 内时,代表的意义是『反向选择』的意思。例如,我不要大写字元,则为[^AZ]。但是,需要特别注意的是,如果以grep -n [^AZ] regular_express.txt 来搜寻,却发现该档案内的所有行都被列出,为什么?因为这个[^AZ] 是『非大写字元』的意思, 因为每一行均有非大写字元,例如第一行的"Open Source" 就有p,e,n,o.... 等等的小写字
grep -n 'oo[^t]' regular_express.txt
{n,m} 意义:连续n 到m 个的『前一个RE 字符』
意义:若为{n} 则是连续n 个的前一个RE 字符,
意义:若是{n,} 则是连续n 个以上的前一个RE 字符!
范例:在g 与g 之间有2 个到3 个的o 存在的字串,亦即(goog)(gooog)
grep -n 'go\{2,3\}g' regular_express.txt

11.2 扩展正则表达式

RE 字符 意义与范例
+ 意义:重复『一个或一个以上』的前一个RE 字符
范例:搜寻(god) (good) (goood)... 等等的字串。那个o+ 代表『一个以上的o 』所以,底下的执行成果会将第1, 9, 13 行列出来。
egrep -n 'go+d' regular_express.txt
? 意义:『零个或一个』的前一个RE 字符
范例:搜寻(gd) (god) 这两个字串。那个o? 代表『空的或1 个o 』所以,上面的执行成果会将第13, 14 行列出来。有没有发现到,这两个案例( 'go+d' 与'go?d' )的结果集合与'go*d' 相同?想想看,这是为什么喔!
egrep -n 'go?d' regular_express.txt
| 意义:用或( or )的方式找出数个字串
范例:搜寻gd 或good 这两个字串,注意,是『或』!所以,第1,9,14 这三行都可以被列印出来喔!那如果还想要找出dog 呢?
egrep -n 'gd|good' regular_express.txt egrep -n 'gd|good|dog' regular_express.txt
() 意义:找出『群组』字串 范
例:搜寻(glad) 或(good) 这两个字串,因为g 与d 是重复的,所以, 我就可以将la 与oo 列于( ) 当中,并以| 来分隔开来,就可以啦!
egrep -n 'g(la|oo)d' regular_express.txt
()+ 意义:多个重复群组的判别
范例:将『AxyzxyzxyzxyzC』用echo 叫出,然后再使用如下的方法搜寻一下!
echo 'AxyzxyzxyzxyzC' | egrep 'A(xyz)+C'
上面的例子意思是说,我要找开头是A 结尾是C ,中间有一个以上的"xyz" 字串的意思

12. 学习 shell 脚本

12.1 什么是 shell 脚本

1
2
3
4
5
6
7
8
9
#!/bin/bash
# Program:
# This program shows "Hello World!" in your screen.
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Hello World! \a \n"
exit 0

良好的script 撰写习惯,在每个script 的档头处记录好:

  • script 的功能;
  • script 的版本资讯;
  • script 的作者与联络方式;
  • script 的版权宣告方式;
  • script 的History (历史纪录);
  • script 内较特殊的指令,使用『绝对路径』的方式来下达;
  • script 运作时需要的环境变量预先声明与设置。

12.2 简单的 shell 脚本练习

处理用户输入:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
# Program:
# User inputs his first name and last name. Program shows his full name.
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

read -p "Please input your first name: " firstname # 提示使用者输入
read -p "Please input your last name: " lastname # 提示使用者输入
echo -e "\nYour full name is: ${firstname} ${lastname}" # 结果由屏幕输出

随日期变化:

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
#!/bin/bash
# Program:
# Program creates three files, which named by user's input and date command.
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 1. 让使用者输入档案名称,并取得fileuser 这个变数;
echo -e "I will use 'touch' command to create 3 files." # 纯粹显示资讯
read -p "Please input your filename: " fileuser # 提示使用者输入

# 2. 为了避免使用者随意按Enter ,利用变数功能分析档名是否有设定?
filename=${fileuser:-"filename"} # 开始判断有否设定档名

# 3. 开始利用date 指令来取得所需要的档名了;
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}

# 4. 将档名建立吧!
touch "${file1}" # 底下三行在建立档案
touch "${file2}"
touch "${file3}"

数值运算:简单的加减乘除

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
# Program:
# User inputs 2 integer numbers; program will cross these two numbers.
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "You SHOULD input 2 numbers, I will multiplying them! \n"
read -p "first number: " firstnu
read -p "second number: " secnu
total=$((${firstnu}*${secnu}))
echo -e "\nThe result of ${firstnu} x ${secnu} is ==> ${total}"

计算含小数点数:

1
$ echo "123.123*55.9" | bc

计算 Pi

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
# Program:
# User input a scale number to calculate pi number.
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "This program will calculate pi value. \n"
echo -e "You should input a float number to calculate pi value.\n"
read -p "The scale number (10~10000) ? " checking
num=${checking:-"10"} # 开始判断有否有输入数值
echo -e "Starting calculate pi value. Be patient."
time echo "scale=${num}; 4*a(1)" | bc -lq

12.3 善用判断式

测试的标志 代表意义
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 判定字串是否为0 ?若string 为空字串,则为true
test -n string 判定字串是否非为0 ?若string 为空字串,则为false。 注: -n 亦可省略
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
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash
# Program:
# User input a filename, program will check the flowing:
# 1.) exist? 2.) file/directory? 3.) file permissions
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 1. 让使用者输入档名,并且判断使用者是否真的有输入字串?
echo -e "Please input a filename, I will check the filename's type and permission. \n\n"
read -p "Input a filename : " filename
test -z ${filename} && echo "You MUST input a filename." && exit 0
# 2. 判断档案是否存在?若不存在则显示讯息并结束脚本
test ! -e ${filename} && echo "The filename '${filename}' DO NOT exist" && exit 0
# 3. 开始判断档案类型与属性
test -f ${filename} && filetype="regulare file"
test -d ${filename} && filetype="directory"
test -r ${filename} && perm="readable"
test -w ${filename} && perm="${perm} writable"
test -x ${filename} && perm="${perm} executable"
# 4. 开始输出资讯!
echo "The filename: ${filename} is a ${filetype}"
echo "And the permissions for you are : ${perm}"

使用中括号 []

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
# Program:
# This program shows the user's choice
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

read -p "Please input (Y/N): " yn
[ "${yn}" == "Y" -o "${yn}" == "y" ] && echo "OK, continue" && exit 0
[ "${yn}" == "N" -o "${yn}" == "n" ] && echo "Oh, interrupt!" && exit 0
echo "I don't know what your choice is" && exit 0

shell 脚本的默认变量($0 $1 ...):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/bash
# Program:
# Program shows the script name, parameters...
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

echo "The script name is ==> ${0}"
echo "Total parameter number is ==> $#"
[ "$#" -lt 2 ] && echo "The number of parameter is less than 2. Stop here." && exit 0
echo "Your whole parameter is ==> '$@'"
echo "The 1st parameter ==> ${1}"
echo "The 2nd parameter ==> ${2}"

12.4 条件判断式

12.4.1 if...then

单层、简单条件判断式:

1
2
3
if [条件判断式]; then
当条件判断式成立时,可以进行的指令工作内容;
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# Program:
# This program shows the user's choice
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

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

if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]; then
echo "OK, continue"
exit 0
fi
if [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; then
echo "Oh, interrupt!"
exit 0
fi
echo "I don't know what your choice is" && exit 0

多重、复杂条件判断式:

1
2
3
4
5
6
# 一个条件判断,分成功进行与失败进行(else) 
if [条件判断式]; then
当条件判断式成立时,可以进行的指令工作内容;
else
当条件判断式不成立时,可以进行的指令工作内容;
fi
1
2
3
4
5
6
7
8
# 多个条件判断(if ... elif ... elif ... else) 分多种不同情况执行
if [条件判断式一]; then
当条件判断式一成立时,可以进行的指令工作内容;
elif [条件判断式二]; then
当条件判断式二成立时,可以进行的指令工作内容;
else
当条件判断式一与二均不成立时,可以进行的指令工作内容;
fi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash
# Program:
# This program shows the user's choice
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

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

if [ "${yn}" == "Y" ] || [ "${yn}" == "y" ]; then
echo "OK, continue"
elif [ "${yn}" == "N" ] || [ "${yn}" == "n" ]; then
echo "Oh, interrupt!"
else
echo "I don't know what your choice is"
fi

从命令行获取用户输入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash
# Program:
# Check $1 is equal to "hello"
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

if [ "${1}" == "hello" ]; then
echo "Hello, how are you ?"
elif [ "${1}" == "" ]; then
echo "You MUST input parameters, ex> {${0} someword}"
else
echo "The only parameter is 'hello', ex> {${0} hello}"
fi

检测服务端口:

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
#!/bin/bash
# Program:
# Using netstat and grep to detect WWW,SSH,FTP and Mail services.
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 1. 先作一些告知的动作而已~
echo "Now, I will detect your Linux server's services!"
echo -e "The www, ftp, ssh, and mail(smtp) will be detected! \n"

# 2. 开始进行一些测试的工作,并且也输出一些资讯啰!
testfile=/dev/shm/netstat_checking.txt
netstat -tuln > ${testfile} # 先转存资料到记忆体当中!不用一直执行netstat
testing=$(grep ":80 " ${testfile}) # 侦测看port 80 在否?
if [ "${testing}" != "" ]; then
echo "WWW is running in your system."
fi
testing=$(grep ":22 " ${testfile}) # 侦测看port 22 在否?
if [ "${testing}" != "" ]; then
echo "SSH is running in your system."
fi
testing=$(grep ":21 " ${testfile}) # 侦测看port 21 在否?
if [ "${testing}" != "" ]; then
echo "FTP is running in your system."
fi
testing=$(grep ":25 " ${testfile}) # 侦测看port 25 在否?
if [ "${testing}" != "" ]; then
echo "Mail is running in your system."
fi

计算距离指定日期剩余天数:

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
#!/bin/bash
# Program:
# You input your demobilization date, I calculate how many days before you demobilize.
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 1. 告知使用者这支程式的用途,并且告知应该如何输入日期格式?
echo "This program will try to calculate :"
echo "How many days before your demobilization date..."
read -p "Please input your demobilization date (YYYYMMDD ex>20150716): " date2

# 2. 测试一下,这个输入的内容是否正确?利用正规表示法啰~
date_d=$(echo ${date2} |grep '[0-9]\{8\}') # 看看是否有八个数字
if [ "${date_d}" == "" ]; then
echo "You input the wrong date format...."
exit 1
fi

# 3. 开始计算日期啰~
declare -i date_dem=$(date --date="${date2}" +%s) # 退伍日期秒数
declare -i date_now=$(date +%s) # 现在日期秒数
declare -i date_total_s=$((${date_dem}-${date_now})) # 剩余秒数统计
declare -i date_d=$((${date_total_s}/60/60/24)) # 转为日数
if [ "${date_total_s}" -lt "0" ]; then # 判断是否已退伍
echo "You had been demobilization before: " $((-1*${date_d})) " ago"
else
declare -i date_h=$(($((${date_total_s}-${date_d}*60*60*24))/60/60))
echo "You will demobilize after ${date_d} days and ${date_h} hours."
fi

12.4.2 case...esac

1
2
3
4
5
6
7
8
9
10
11
12
case $变数名称 in    <==关键字为case ,还有变数前有钱字号
"第一个变数内容" ) <==每个变数内容建议用双引号括起来,关键字则为小括号)
程式段
;; <==每个类别结尾使用两个连续的分号来处理!
"第二个变数内容" )
程式段
;;
* ) <==最后一个变数内容都会用* 来代表所有其他值
不包含第一个变数内容与第二个变数内容的其他程式执行段
exit 1
;;
esac

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# Program:
# Show "Hello" from $1.... by using case .... esac
# History:
# 2015/07/16 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

case ${1} in
"hello")
echo "Hello, how are you ?"
;;
"")
echo "You MUST input parameters, ex> {${0} someword}"
;;
*) # 其实就相当于万用字元,0~无穷多个任意字元之意!
echo "Usage ${0} {hello}"
;;
esac

12.4.3 function

语法:

1
2
3
function fname () {
程序段
}

例子:

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
#!/bin/bash
# Program:
# Use function to repeat information.
# History:
# 2015/07/17 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

function printit(){
echo -n "Your choice is " # 加上-n 可以不断行继续在同一行显示
}

echo "This program will print your selection !"
case ${1} in
"one")
printit ; echo ${1} | tr 'az' 'AZ' # 将参数做大小写转换!
;;
"two")
printit ; echo ${1} | tr 'az' 'AZ'
;;
"three")
printit ; echo ${1} | tr 'az' 'AZ'
;;
*)
echo "Usage ${0} {one|two|three}"
;;
esac

例子:

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
#!/bin/bash
# Program:
# Use function to repeat information.
# History:
# 2015/07/17 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

function printit(){
echo "Your choice is ${1}" # 这个$1 必须要参考底下指令的下达
}

echo "This program will print your selection !"
case ${1} in
"one")
printit 1 # 请注意, printit 指令后面还有接参数!
;;
"two")
printit 2
;;
"three")
printit 3
;;
*)
echo "Usage ${0} {one|two|three}"
;;
esac

12.5 循环

12.5.1 while do done、until do done(不定循环)

语法:

1
2
3
4
while [ condition ]   <==中括号内的状态就是判断式
do <==do 是循环的开始!
程式段落
done
1
2
3
4
until [ condition ]
do
程式段落
done

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
# Program:
# Repeat question until user input correct answer.
# History:
# 2015/07/17 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

while [ "${yn}" != "yes" -a "${yn}" != "YES" ]
do
read -p "Please input yes/YES to stop this program: " yn
done
echo "OK! you input the correct answer."
1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
# Program:
# Repeat question until user input correct answer.
# History:
# 2015/07/17 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

until [ "${yn}" == "yes" -o "${yn}" == "YES" ]
do
read -p "Please input yes/YES to stop this program: " yn
done
echo "OK! you input the correct answer."

示例:(计算1+2+3+....+100)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
# Program:
# Use loop to calculate "1+2+3+...+100" result.
# History:
# 2015/07/17 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

s=0 # 这是加总的数值变数
i=0 # 这是累计的数值,亦即是1, 2, 3....
while [ "${i}" != "100" ]
do
i=$(($i+1)) # 每次i 都会增加1
s=$(($s+$i)) # 每次都会求和一次
done
echo "The result of '1+2+3+...+100' is ==> $s"

12.5.2 for...do...done(固定循环)

语法:

1
2
3
4
for var in con1 con2 con3 ...
do
程式段
done

示例:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
# Program:
# Using for .... loop to print 3 animals
# History:
# 2015/07/17 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

for animal in dog cat elephant
do
echo "There are ${animal}s.... "
done

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash
# Program
# Use ping command to check the network's PC state.
# History
# 2015/07/17 VBird first release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
network="192.168.1" # 先定义一个网域的前面部分!
for sitenu in $(seq 1 100) # seq 为sequence(连续) 的缩写之意
do
# 底下的程式在取得ping 的回传值是正确的还是失败的!
ping -c 1 -w 1 ${network}.${sitenu} &> /dev/null && result=0 || result=1
# 开始显示结果是正确的启动(UP) 还是错误的没有连通(DOWN)
if [ "${result}" == 0 ]; then
echo "Server ${network}.${sitenu} is UP."
else
echo "Server ${network}.${sitenu} is DOWN."
fi
done

示例:

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
#!/bin/bash
# Program:
# User input dir name, I find the permission of files.
# History:
# 2015/07/17 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# 1. 先看看这个目录是否存在啊?
read -p "Please input a directory: " dir
if [ "${dir}" == "" -o ! -d "${dir}" ]; then
echo "The ${dir} is NOT exist in your system."
exit 1
fi

# 2. 开始测试档案啰~
filelist=$(ls ${dir}) # 列出所有在该目录下的档案名称
for filename in ${filelist}
do
perm=""
test -r "${dir}/${filename}" && perm="${perm} readable"
test -w "${dir}/${filename}" && perm="${perm} writable"
test -x "${dir}/${filename}" && perm="${perm} executable"
echo "The file ${dir}/${filename}'s permission is ${perm} "
done

12.5.3 for...do...done 的数值处理

语法:

1
2
3
4
for ((初始值;限制值;赋值运算))
do
程式段
done

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
# Program:
# Try do calculate 1+2+....+${your_input}
# History:
# 2015/07/17 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

read -p "Please input a number, I will count for 1+2+...+your_input: " nu

s=0
for (( i=1; i<=${nu}; i=i+1 ))
do
s=$((${s}+${i}))
done
echo "The result of '1+2+3+...+${nu}' is ==> ${s}"

12.5.4 搭配随机数和数组的实验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
# Program:
# Try do tell you what you may eat.
# History:
# 2015/07/17 VBird First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

eat[1]="卖当当汉堡" # 写下你所收集到的店家!
eat[2]="肯爷爷炸鸡"
eat[3]="彩虹日式便当"
eat[4]="越油越好吃大雅"
eat[5]="想不出吃啥学餐"
eat[6]="太师父便当"
eat[7]="池上便当"
eat[8]="怀念火车便当"
eat[9]="一起吃泡面"
eatnum=9 # 需要输入有几个可用的餐厅数!

check=$(( ${RANDOM} * ${eatnum} / 32767 + 1 ))
echo "your may eat ${eat[${check}]}"

12.6 shell 脚本的跟踪与调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ sh [-nvx] scripts.sh
选项与参数:
-n :不要执行script,仅查询语法的问题;
-v :再执行script 前,先将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....

13. Linux 账号管理与 ACL 权限设置

13.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
# 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 为永远不失效(密码只会过期而强制于登入时重新设定而已。)

范例一:完全参考预设值建立一个使用者,名称为vbird1
[root@study ~]# useradd vbird1
[root@study ~]# ll -d /home/vbird1
drwx------. 3 vbird1 vbird1 74 Jul 20 21:50 /home/vbird1
# 预设会建立使用者家目录,且权限为700 !这是重点!

[root@study ~]# grep vbird1 /etc/passwd /etc/shadow /etc/group
/etc/passwd:vbird1:x:1003:1004::/home/vbird1:/bin/bash
/etc/shadow:vbird1:!!:16636:0:99999:7:::
/etc/group:vbird1:x:1004: <==预设会建立一个与帐号一模一样的群组名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# passwd [--stdin] [帐号名称]   <==所有人均可使用来改自己的密码
# 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 栏位,密码失效天数

范例一:请root 给予vbird2 密码
# passwd vbird2
Changing password for user vbird2.
New UNIX password: <==这里直接输入新的密码,萤幕不会有任何反应
BAD PASSWORD: The password is shorter than 8 characters <==密码太简单或过短的错误!
Retype new UNIX password: <==再输入一次同样的密码
passwd: all authentication tokens updated successfully . <==竟然还是成功修改了!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 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 密码栏的! 拿掉,解冻啦!
1
2
3
4
5
6
# userdel [-r] username
选项与参数:
-r :连同使用者的家目录也一起删除

范例一:删除vbird2 ,连同家目录一起删除
# userdel -r vbird2

用户组管理:

1
2
3
4
5
6
7
8
9
10
11
# groupadd [-g gid] [-r] 群组名称
选项与参数:
-g :后面接某个特定的GID ,用来直接给予某个GID ~
-r :建立系统群组啦!与/etc/login.defs 内的GID_MIN 有关。

范例一:新建一个群组,名称为group1
# groupadd group1
# grep group1 /etc/group /etc/gshadow
/etc/group:group1:x: 1503 :
/etc/gshadow:group1:!::
# 群组的GID 也是会由1000 以上最大GID+1 来决定!
1
2
3
4
5
6
7
8
9
10
# groupmod [-g gid] [-n group_name] 群组名
选项与参数:
-g :修改既有的GID 数字;
-n :修改既有的群组名称

范例一:将刚刚上个指令建立的group1 名称改为mygroup , GID 为201
# groupmod -g 201 -n mygroup group1
# grep mygroup /etc/group /etc/gshadow
/etc/group:mygroup:x: 201 :
/etc/gshadow:mygroup:!::
1
2
3
4
5
6
7
8
# groupdel [groupname]

范例一:将刚刚的mygroup 删除!
# groupdel mygroup

范例二:若要删除vbird1 这个群组的话?
# groupdel vbird1
groupdel: cannot remove the primary group of user 'vbird1'
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
# 关于系统管理员(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/group:testgroup:x:1503:
/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 ~]$ gpasswd -a vbird1 testgroup
[vbird1@study ~]$ gpasswd -a vbird3 testgroup
[vbird1@study ~]$ grep testgroup /etc/group
testgroup:x:1503: vbird1,vbird3

13.4 用户身份切换

1
2
3
4
5
6
7
# su [-lm] [-c 指令] [username]
选项与参数:
- :单纯使用- 如『 su - 』代表使用login-shell 的变数档案读取方式来登入系统;
若使用者名称没有加上去,则代表切换为root 的身份。
-l :与- 类似,但后面需要加欲切换的使用者帐号!也是login-shell 的方式。
-m :-m 与-p 是一样的,表示『使用目前的环境设定,而不读取新使用者的设定档』
-c :仅进行一次指令,所以-c 后面可以加上指令
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
范例一:假设你原本是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 的环境


范例二:使用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 的环境

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 "一串指令" 来执行的!

14. 磁盘配额(Quota)与高级文件系统管理

15. 计划任务(crontab)

15.1 什么是计划任务

  • at:at 是个可以处理仅执行一次就结束排程的指令,不过要执行at 时, 必须要有atd 这个服务的支援才行。在某些新版的distributions 中,atd 可能预设并没有启动,那么at 这个指令就会失效
  • crontab:crontab 这个指令所设定的工作将会循环的一直进行下去!可循环的时间为分钟、小时、每周、每月或每年等。crontab 除了可以使用指令执行外,亦可编辑/etc/crontab 来支援。至于让crontab 可以生效的服务则是crond 这个服务

15.2 仅执行一次的计划任务

需先启动 atd 服务:

1
2
3
4
5
6
7
8
9
10
11
12
[root@study ~]# systemctl restart atd   # 重新启动atd 这个服务
[root@study ~]# systemctl enable atd # 让这个服务开机就自动启动
[root@study ~]# systemctl status atd # 查阅一下atd 目前的状态
atd.service - Job spooling tools
Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled ) # 是否开机启动
Active: active (running) since Thu 2015-07-30 19:21:21 CST; 23s ago # 是否正在运作中
Main PID: 26503 (atd)
CGroup: /system.slice/atd.service
└─26503 /usr/sbin/atd -f

Jul 30 19:21:21 study.centos.vbird systemd[1]: Starting Job spooling tools...
Jul 30 19:21:21 study.centos.vbird systemd[1]: Started Job spooling tools.

使用 at 这个指令来产生所要运作的工作,并将这个工作以文本文件的方式写入 /var/spool/at/ 目录内,该工作便能等待atd 这个服务的取用与执行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[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
就是说,在某个时间点『再加几个时间后』才进行。

示例:

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 还可以在一个工作内输入多个指令呢!不错吧!
1
2
3
4
5
6
7
8
9
10
11
12
13
[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
# 没有任何资讯,表示该工作被移除了!

15.3 循环执行的计划任务

1
2
3
4
5
6
7
8
9
10
11
12
[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
#分时日月周|<==============指令串========================>|

每项工作(每行) 的格式都是具有六个栏位,这六个栏位的意义为:

代表意义 分钟 小时 日期 月份 命令
数字范围 0-59 0-23 1-31 1-12 0-7 需要执行的命令

特殊字符:

特殊字符 代表意义
*(星号) 代表任何时刻都接受的意思!举例来说,范例一内那个日、月、周都是* , 就代表着『不论何月、何日的礼拜几的12:00 都执行后续指令』的意思!
,(逗号) 代表分隔时段的意思。举例来说,如果要下达的工作是3:00 与6:00 时,就会是:0 3,6 * * * command时间参数还是有五栏,不过第二栏是3,6 ,代表3 与6 都适用!
-(减号) 代表一段时间范围内,举例来说, 8 点到12 点之间的每小时的20 分都进行一项工作:20 8-12 * * * command仔细看到第二栏变成8-12 喔!代表8,9,10,11,12 都适用的意思!
/n(斜线) 那个n 代表数字,亦即是『每隔n 单位间隔』的意思,例如每五分钟进行一次,则: /5 * * * command很简单吧!用* 与/5 来搭配,也可以写成0-59/5 ,相同意思!

注意:『如果只是要删除某个crontab 的工作项目,那么请使用crontab -e 来重新编辑即可!』如果使用-r 的参数,是会将所有的 crontab 资料内容都删掉的!

15.4 可唤醒停机期间的工作任务

语法:

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 定义的各项工作名称。

16. 进程管理与 SELinux 初探

kill:

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 ~]# kill -signal %jobnumber 
[root@study ~]# kill -l
选项与参数:
-l :这个是L 的小写,列出目前kill 能够使用的讯号(signal) 有哪些?
signal :代表给予后面接的那个工作什么样的指示啰!用man 7 signal 可知:
-1 :重新读取一次参数的设定档(类似reload);
-2 :代表与由键盘输入[ctrl]-c 同样的动作;
-9 :立刻强制删除一个工作;
-15:以正常的程序方式终止一项工作。与-9 是不一样的。

范例一:找出目前的bash 环境下的背景工作,并将该工作『强制删除』。
[root@study ~]# jobs
[1]+ Stopped vim ~/.bashrc
[2] Stopped find / -print
[root@study ~]# kill -9 %2; jobs
[1]+ Stopped vim ~/.bashrc
[2] Killed find / -print
# 再过几秒你再下达jobs 一次,就会发现2 号工作不见了!因为被移除了!

范例二:找出目前的bash 环境下的背景工作,并将该工作『正常终止』掉。
[root@study ~]# jobs
[1]+ Stopped vim ~/.bashrc
[root@study ~]# kill -SIGTERM %1
# -SIGTERM 与-15 是一样的!您可以使用kill -l 来查阅!
# 不过在这个案例中, vim 的工作无法被结束喔!因为他无法透过kill 正常终止的意思!
代号 名称 内容
1 SIGHUP 启动被终止的程序,可让该PID 重新读取自己的设定档,类似重新启动
2 SIGINT 相当于用键盘输入[ctrl]-c 来中断一个程序的进行
9 SIGKILL 代表强制中断一个程序的进行,如果该程序进行到一半, 那么尚未完成的部分可能会有『半产品』产生,类似vim会有.filename.swp 保留下来。
15 SIGTERM 以正常的结束程序来终止该程序。由于是正常的终止, 所以后续的动作会将他完成。不过,如果该程序已经发生问题,就是无法使用正常的方法终止时, 输入这个signal 也是没有用的。
19 SIGSTOP 相当于用键盘输入[ctrl]-z 来暂停一个程序的进行

killall:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@study ~]# killall [-iIe] [command name]
选项与参数:
-i :interactive 的意思,互动式的,若需要删除时,会出现提示字元给使用者;
-e :exact 的意思,表示『后面接的command name 要一致』,但整个完整的指令
不能超过15 个字元。
-I :指令名称(可能含参数)忽略大小写。

范例一:给予rsyslogd 这个指令启动的PID 一个SIGHUP 的讯号
[root@study ~]# killall -1 rsyslogd
# 如果用ps aux 仔细看一下,若包含所有参数,则/usr/sbin/rsyslogd -n 才是最完整的!

范例二:强制终止所有以httpd 启动的程序(其实并没有此程序在系统内)
[root@study ~]# killall -9 httpd

范例三:依次询问每个bash 程式是否需要被终止运作!
[root@study ~]# killall -i -9 bash
Signal bash(13888) ? (y/N) n <==这个不杀!
Signal bash(13928) ? (y/N) n <==这个不杀!
Signal bash(13970) ? (y/N) n <==这个不杀!
Signal bash(14836) ? (y/N) y <==这个杀掉!
# 具有互动的功能!可以询问你是否要删除bash 这个程式。要注意,若没有-i 的参数,
# 所有的bash 都会被这个root 给杀掉!包括root 自己的bash 喔!^_^

ps 命令:

1
2
3
4
5
6
7
8
9
10
11
12
[root@study ~]# ps aux  <==观察系统所有的程序资料
[root@study ~]# ps -lA <==也是能够观察所有系统的资料
[root@study ~]# ps axjf <==连同部分程序树状态
选项与参数:
-A :所有的process 均显示出来,与-e 具有同样的效用;
-a :不与terminal 有关的所有process ;
-u :有效使用者(effective user) 相关的process ;
x :通常与a 这个参数一起使用,可列出较完整资讯。
输出格式规划:
l :较长、较详细的将该PID 的的资讯列出;
j :工作的格式(jobs format)
-f :做一个更为完整的输出。

top 命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@study ~]# top [-d 数字] | top [-bnp]
选项与参数:
-d :后面可以接秒数,就是整个程序画面更新的秒数。预设是5 秒;
-b :以批次的方式执行top ,还有更多的参数可以使用喔!
通常会搭配资料流重导向来将批次的结果输出成为档案。
-n :与-b 搭配,意义是,需要进行几次top 的输出结果。
-p :指定某些个PID 来进行观察监测而已。
在top 执行过程当中可以使用的按键指令:
? :显示在top 当中可以输入的按键指令;
P :以CPU 的使用资源排序显示;
M :以Memory 的使用资源排序显示;
N :以PID 来排序喔!
T :由该Process 使用的CPU 时间累积(TIME+) 排序。
k :给予某个PID 一个讯号(signal)
r :给予某个PID 重新制订一个nice 值。
q :离开top 软体的按键。

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
[root@study ~]# pstree [-A|U] [-up]
选项与参数:
-A :各程序树之间的连接以ASCII 字元来连接;
-U :各程序树之间的连接以万国码的字元来连接。在某些终端介面下可能会有错误;
-p :并同时列出每个process 的PID;
-u :并同时列出每个process 的所属帐号名称。

范例一:列出目前系统上面所有的程序树的相关性:
[root@study ~]# pstree -A
systemd-+-ModemManager---2*[{ModemManager}] # 这行是ModenManager 与其子程序
|-NetworkManager---3*[{NetworkManager}] # 前面有数字,代表子程序的数量!
....(中间省略)....
|-sshd---sshd---sshd---bash---bash---sudo---su---bash---pstree <==我们指令执行的相依性
....(底下省略)....
# 注意一下,为了节省版面,所以鸟哥已经删去很多程序了!

范例二:承上题,同时秀出PID 与users
[root@study ~]# pstree -Aup
systemd(1)-+-ModemManager(745)-+-{ModemManager}(785)
| `-{ModemManager}(790)
|-NetworkManager(870)-+-{NetworkManager}(907)
| |-{NetworkManager}(911)
| `-{NetworkManager}(914)
....(中间省略)....
|-sshd(1326)---sshd(13923)---sshd(13927, dmtsai )---bash(13928)---bash(13970)---
....(底下省略)....
# 在括号() 内的即是PID 以及该程序的owner 喔!一般来说,如果该程序的所有人与父程序同,
# 就不会列出,但是如果与父程序不一样,那就会列出该程序的拥有者!看上面13927 就转变成dmtsai 了

free 命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
# free [-b|-k|-m|-g|-h] [-t] [-s N -c N]
选项与参数:
-b :直接输入free 时,显示的单位是Kbytes,我们可以使用b(bytes), m(Mbytes)
k(Kbytes), 及g(Gbytes) 来显示单位喔!也可以直接让系统自己指定单位(-h)
-t :在输出的最终结果,显示实体记忆体与swap 的总量。
-s :可以让系统每几秒钟输出一次,不间断的一直输出的意思!对于系统观察挺有效!
-c :与-s 同时处理~让free 列出几次的意思~

范例一:显示目前系统的记忆体容量
[root@study ~]# free -m
total used free shared buff/cache available
Mem: 2848 346 1794 8 706 2263
Swap: 1023 0 1023

netstat 命令:

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 ~]# netstat -[atunlp]
选项与参数:
-a :将目前系统上所有的连线、监听、Socket 资料都列出来
-t :列出tcp 网路封包的资料
-u :列出udp 网路封包的资料
-n :不以程序的服务名称,以埠号(port number) 来显示;
-l :列出目前正在网路监听(listen) 的服务;
-p :列出该网路服务的程序PID

范例一:列出目前系统已经建立的网路连线与unix socket 状态
[root@study ~]# netstat
Active Internet connections (w/o servers) <==与网路较相关的部分
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 172.16.15.100:ssh 172.16.220.234:48300 ESTABLISHED
Active UNIX domain sockets (w/o servers) <==与本机的程序自己的相关性(非网路)
Proto RefCnt Flags Type State I-Node Path
unix 2 [ ] DGRAM 1902 @/org/freedesktop/systemd1/notify
unix 2 [ ] DGRAM 1944 /run/systemd/shutdownd
....(中间省略)....
unix 3 [ ] STREAM CONNECTED 25425 @/tmp/.X11-unix/X0
unix 3 [ ] STREAM CONNECTED 28893
unix 3 [ ] STREAM CONNECTED 21262

范例二:找出目前系统上已在监听的网路连线及其PID
[root@study ~]# netstat -tulnp
Active Internet connections (only servers)
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 1326/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 2349/master
tcp6 0 0 :::22 :::* LISTEN 1326/sshd
tcp6 0 0 ::1:25 :::* LISTEN 2349/master
udp 0 0 0.0.0.0:123 0.0.0.0:* 751/chronyd
udp 0 0 127.0.0.1:323 0.0.0.0:* 751/chronyd
udp 0 0 0.0.0.0:57808 0.0.0.0:* 743/avahi-daemon: r
udp 0 0 0.0.0.0:5353 0.0.0.0:* 743/avahi-daemon: r
udp6 0 0 :::123 :::* 751/chronyd
udp6 0 0 ::1:323 :::* 751/chronyd
# 除了可以列出监听网路的介面与状态之外,最后一个栏位还能够显示此服务的
# PID 号码以及程序的指令名称喔!例如上头的1326 就是该PID

范例三:将上述的0.0.0.0:57808 那个网路服务关闭的话?
[root@study ~]# kill -9 743
[root@study ~]# killall -9 avahi-daemon

dmesg 命令:

1
2
3
4
5
6
7
8
范例一:输出所有的核心开机时的资讯
[root@study ~]# dmesg | more

范例二:搜寻开机的时候,硬碟的相关资讯为何?
[root@study ~]# dmesg | grep -i vda
[ 0.758551] vda: vda1 vda2 vda3 vda4 vda5 vda6 vda7 vda8 vda9
[ 3.964134] XFS (vda2): Mounting V4 Filesystem
....(底下省略)....

vmstat 命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@study ~]# vmstat [-a] [延迟[总计侦测次数]]  <==CPU/记忆体等资讯
[root@study ~]# vmstat [-fs] <==记忆体相关
[root@study ~]# vmstat [-S 单位] <==设定显示数据的单位
[root@study ~]# vmstat [-d] <==与磁碟有关
[root@study ~]# vmstat [-p 分割槽] <==与磁碟有关
选项与参数:
-a :使用inactive/active(活跃与否) 取代buffer/cache 的记忆体输出资讯;
-f :开机到目前为止,系统复制(fork) 的程序数;
-s :将一些事件(开机至目前为止) 导致的记忆体变化情况列表说明;
-S :后面可以接单位,让显示的资料有单位。例如K/M 取代bytes 的容量;
-d :列出磁碟的读写总量统计表
-p :后面列出分割槽,可显示该分割槽的读写总量统计表

范例一:统计目前主机CPU 状态,每秒一次,共计三次!
[root@study ~]# vmstat 1 3
procs ------------memory---------- ---swap-- -----io---- -system-- ------cpu-----
rb swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 1838092 1504 722216 0 0 4 1 6 9 0 0 100 0 0
0 0 0 1838092 1504 722200 0 0 0 0 13 23 0 0 100 0 0
0 0 0 1838092 1504 722200 0 0 0 0 25 46 0 0 100 0 0

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
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
[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) 啰!

范例一:找出目前所在目录的使用PID/所属帐号/权限为何?
[root@study ~]# fuser -uv .
USER PID ACCESS COMMAND
/root: root 13888 ..c.. (root)bash
root 31743 ..c.. (root)bash


范例二:找到所有使用到/proc 这个档案系统的程序吧!
[root@study ~]# fuser -uv /proc
/proc: root kernel mount (root)/proc
rtkit 768 .rc.. (rtkit)rtkit-daemon
# 资料量还不会很多,虽然这个目录很繁忙~没关系!我们可以继续这样作,看看其他的程序!

[root@study ~]# fuser -mvu /proc
USER PID ACCESS COMMAND
/proc: root kernel mount (root)/proc
root 1 f.... (root)systemd
root 2 ...e. (root)kthreadd
.....(底下省略).....
# 有这几支程序在进行/proc 档案系统的存取喔!这样清楚了吗?

范例三:找到所有使用到/home 这个档案系统的程序吧!
[root@study ~]# echo $$
31743 # 先确认一下,自己的bash PID 号码吧!
[root@study ~]# cd /home
[root@study home]# fuser -muv .
USER PID ACCESS COMMAND
/home: root kernel mount (root)/home
dmtsai 31535 ..c.. (dmtsai)bash
root 31571 ..c.. (root)passwd
root 31737 ..c.. (root)sudo
root 31743 ..c.. (root)bash # 果然,自己的PID 在啊!
[root@study home]# cd ~
[root@study ~]# umount /home
umount: /home: target is busy.
(In some cases useful info about processes that use
the device is found by lsof(8) or fuser(1))
# 从fuser 的结果可以知道,总共有五只process 在该目录下运作,那即使root 离开了/home,
# 当然还是无法umount 的!那要怎办?哈哈!可以透过如下方法一个一个删除~
[root@study ~]# fuser -mki /home
/home: 31535c 31571c 31737c # 你会发现, PID 跟上面查到的相同!
Kill process 31535 ? (y/N) # 这里会问你要不要删除!当然不要乱删除啦!通通取消!

lsof:列出被进程所使用的文件名称

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
[root@study ~]# lsof [-aUu] [+d]
选项与参数:
-a :多项资料需要『同时成立』才显示出结果时!
-U :仅列出Unix like 系统的socket 档案类型;
-u :后面接username,列出该使用者相关程序所开启的档案;
+d :后面接目录,亦即找出某个目录底下已经被开启的档案!

范例一:列出目前系统上面所有已经被开启的档案与装置:
[root@study ~]# lsof
COMMAND PID TID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root cwd DIR 253,0 4096 128 /
systemd 1 root rtd DIR 253,0 4096 128 /
systemd 1 root txt REG 253,0 1230920 967763 /usr/lib/systemd/systemd
....(底下省略)....
# 注意到了吗?是的,在预设的情况下, lsof 会将目前系统上面已经开启的
# 档案全部列出来~所以,画面多的吓人啊!您可以注意到,第一个档案systemd 执行的
# 地方就在根目录,而根目录,嘿嘿!所在的inode 也有显示出来喔!

范例二:仅列出关于root 的所有程序开启的socket 档案
[root@study ~]# lsof -u root -a -U
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root 3u unix 0xffff8800b7756580 0t0 13715 socket
systemd 1 root 7u unix 0xffff8800b7755a40 0t0 1902 @/org/freedesktop/systemd1/notify
systemd 1 root 9u unix 0xffff8800b7756d00 0t0 1903 /run/systemd/private
.....(中间省略).....
Xorg 4496 root 1u unix 0xffff8800ab107480 0t0 25981 @/tmp/.X11-unix/X0
Xorg 4496 root 3u unix 0xffff8800ab107840 0t0 25982 /tmp/.X11-unix/X0
Xorg 4496 root 16u unix 0xffff8800b7754f00 0t0 25174 @/tmp/.X11-unix/X0
.....(底下省略).....
# 注意到那个-a 吧!如果你分别输入lsof -u root 及lsof -U ,会有啥资讯?
# 使用lsof -u root -U 及lsof -u root -a -U ,呵呵!都不同啦!
# -a 的用途就是在解决同时需要两个项目都成立时啊!^_^

范例三:请列出目前系统上面所有的被启动的周边装置
[root@study ~]# lsof +d /dev
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
systemd 1 root 0u CHR 1,3 0t0 1028 /dev/null
systemd 1 root 1u CHR 1,3 0t0 1028 /dev/null
# 看吧!因为装置都在/dev 里面嘛!所以啰,使用搜寻目录即可啊!

范例四:秀出属于root 的bash 这支程式所开启的档案
[root@study ~]# lsof -u root | grep bash
ksmtuned 781 root txt REG 253,0 960384 33867220 /usr/bin/bash
bash 13888 root cwd DIR 253,0 4096 50331777 /root
bash 13888 root rtd DIR 253,0 4096 128 /
bash 13888 root txt REG 253,0 960384 33867220 /usr/bin/bash
bash 13888 root mem REG 253,0 106065056 17331169 /usr/lib/locale/locale-archive
....(底下省略)....

17. 认识系统服务(daemon)

17.1 什么是 daemon 与服务(service)

完成 service 的程序称为 daemon。

  • /usr/lib/systemd/system/:每个服务最主要的启动脚本设定,有点类似以前的/etc/init.d 底下的档案;
  • /run/systemd/system/:系统执行过程中所产生的服务脚本,这些脚本的优先序要比/usr/lib/systemd/system/ 高!
  • /etc/systemd/system/:管理员依据主机系统的需求所建立的执行脚本,其实这个目录有点像以前/etc/rc.d/rc5.d/Sxx 之类的功能!执行优先序又比/run/systemd/system/ 高喔!

常见的 systemd 服务类型:

扩展名 主要服务功能
.service 一般服务类型(service unit):主要是系统服务,包括服务器本身所需要的本机服务以及网路服务等,比较经常被使用到的服务大多是这种类型!所以,这也是最常见的类型了!
.socket 内部程序资料交换的插槽服务(socket unit):主要是IPC (Inter-process communication) 的传输讯息插槽档(socket file) 功能。这种类型的服务通常在监控讯息传递的插槽档,当有透过此插槽档传递讯息来说要连结服务时,就依据当时的状态将该用户的要求传送到对应的daemon, 若daemon 尚未启动,则启动该daemon 后再传送用户的要求。使用socket 类型的服务一般是比较不会被用到的服务,因此在开机时通常会稍微延迟启动的时间 (因为比较没有这么常用嘛!)。一般用于本机服务比较多,例如我们的图形界面很多的软体都是透过socket 来进行本机程序资料交换的行为。(这与早期的xinetd 这个super daemon 有部份的相似喔!)
.target 执行环境类型(target unit):其实是一群unit 的集合,例如上面表格中谈到的multi-user.target 其实就是一堆服务的集合~也就是说, 选择执行multi-user.target 就是执行一堆其他.service 或/及.socket 之类的服务就是了!
.mount .automount 档案系统挂载相关的服务(automount unit / mount unit):例如来自网路的自动挂载、NFS 档案系统挂载等与档案系统相关性较高的程序管理。
.path 侦测特定档案或目录类型(path unit):某些服务需要侦测某些特定的目录来提供伫列服务,例如最常见的列印服务,就是透过侦测列印伫列目录来启动列印功能!这时就得要.path 的服务类型支援了!
.timer 循环执行的服务(timer unit):这个东西有点类似anacrontab 喔!不过是由systemd 主动提供的,比anacrontab 更加有弹性!

17.2 通过 systemctl 管理服务

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 ~]# systemctl [command] [unit] 
command 主要有:
start :立刻启动后面接的unit
stop :立刻关闭后面接的unit
restart :立刻关闭后启动后面接的unit,亦即执行stop 再start 的意思
reload :不关闭后面接的unit 的情况下,重新载入设定档,让设定生效
enable :设定下次开机时,后面接的unit 会被启动
disable :设定下次开机时,后面接的unit 不会被启动
status :目前后面接的这个unit 的状态,会列出有没有正在执行、开机预设执行否、登录等资讯等!
is-active :目前有没有正在运作中
is-enabled:开机时有没有预设要启用这个unit

范例一:看看目前atd 这个服务的状态为何?
[root@study ~]# systemctl status atd.service
atd.service - Job spooling tools
Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled)
Active: active (running) since Mon 2015-08-10 19:17:09 CST; 5h 42min ago
Main PID: 1350 (atd)
CGroup: /system.slice/atd.service
└─1350 /usr/sbin/atd -f

Aug 10 19:17:09 study.centos.vbird systemd[1]: Started Job spooling tools.
# 重点在第二、三行喔~
# Loaded:这行在说明,开机的时候这个unit 会不会启动,enabled 为开机启动,disabled 开机不会启动
# Active:现在这个unit 的状态是正在执行(running) 或没有执行(dead)
# 后面几行则是说明这个unit 程序的PID 状态以及最后一行显示这个服务的登录档资讯!
# 登录档资讯格式为:『时间』 『讯息发送主机』 『哪一个服务的讯息』 『实际讯息内容』
# 所以上面的显示讯息是:这个atd 预设开机就启动,而且现在正在运作的意思!

范例二:正常关闭这个atd 服务
[root@study ~]# systemctl stop atd.service
[root@study ~]# systemctl status atd.service
atd.service - Job spooling tools
Loaded: loaded (/usr/lib/systemd/system/atd.service; enabled )
Active: inactive (dead) since Tue 2015-08-11 01:04:55 CST; 4s ago
Process: 1350 ExecStart=/usr/sbin/atd -f $OPTS (code=exited, status=0/SUCCESS)
Main PID: 1350 (code=exited, status=0/SUCCESS)

Aug 10 19:17:09 study.centos.vbird systemd[1]: Started Job spooling tools.
Aug 11 01:04:55 study.centos.vbird systemd[1]: Stopping Job spooling tools...
Aug 11 01:04:55 study.centos.vbird systemd[1]: Stopped Job spooling tools.
# 目前这个unit 下次开机还是会启动,但是现在是没在运作的状态中!同时,
# 最后两行为新增加的登录讯息,告诉我们目前的系统状态喔!

查看系统上所有服务:

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 ~]# systemctl [command] [--type=TYPE] [--all] 
command:
list-units :依据unit 列出目前有启动的unit。若加上--all 才会列出没启动的。
list-unit-files :依据/usr/lib/systemd/system/ 内的档案,将所有档案列表说明。
--type=TYPE:就是之前提到的unit type,主要有service, socket, target 等

范例一:列出系统上面有启动的unit
[root@study ~]# systemctl
UNIT LOAD ACTIVE SUB DESCRIPTION
proc-sys-fs-binfmt_mis... loaded active waiting Arbitrary Executable File Formats File System
sys-devices-pc...:0:1:... loaded active plugged QEMU_HARDDISK
sys-devices-pc...0:1-0... loaded active plugged QEMU_HARDDISK
sys-devices-pc...0:0-1... loaded active plugged QEMU_DVD-ROM
.....(中间省略).....
vsftpd.service loaded active running Vsftpd ftp daemon
.....(中间省略).....
cups.socket loaded failed failed CUPS Printing Service Sockets
.....(中间省略).....
LOAD = Reflects whether the unit definition was properly loaded.
ACTIVE = The high-level unit activation state, ie generalization of SUB.
SUB = The low-level unit activation state, values depend on unit type.

141 loaded units listed. Pass --all to see loaded but inactive units, too.
To show all installed unit files use 'systemctl list-unit-files'.
# 列出的项目中,主要的意义是:
# UNIT :项目的名称,包括各个unit 的类别(看副档名)
# LOAD :开机时是否会被载入,预设systemctl 显示的是有载入的项目而已喔!
# ACTIVE :目前的状态,须与后续的SUB 搭配!就是我们用systemctl status 观察时,active 的项目!
# DESCRIPTION :详细描述啰
# cups 比较有趣,因为刚刚被我们玩过,所以ACTIVE 竟然是failed 的喔!被玩死了!^_^
# 另外,systemctl 都不加参数,其实预设就是list-units 的意思!

范例二:列出所有已经安装的unit 有哪些?
[root@study ~]# systemctl list-unit-files
UNIT FILE STATE
proc-sys-fs-binfmt_misc.automount static
dev-hugepages.mount static
dev-mqueue.mount static
proc-fs-nfsd.mount static
.....(中间省略).....
systemd-tmpfiles-clean.timer static

336 unit files listed.

查看网络端口:

1
2
3
4
5
6
7
8
9
[root@study ~]# netstat -tlunp
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 1340/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 2387/master
tcp6 0 0 :::555 :::* LISTEN 29113/vsftpd
tcp6 0 0 :::22 :::* LISTEN 1340/sshd
tcp6 0 0 ::1:25 :::* LISTEN 2387/master
udp 0 0 0.0.0.0:5353 0.0.0.0:* 750/avahi-daemon: r
udp 0 0 0.0.0.0:36540 0.0.0.0:* 750/avahi-daemon: r

17.3 systemctl 针对 service 类型的配置文件

systemctl 配置文件设置项目简介:(以 sshd.service 文件为例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@study ~]# cat /usr/lib/systemd/system/sshd.service 
[Unit] # 这个项目与此unit 的解释、执行服务相依性有关
Description=OpenSSH server daemon
After=network.target sshd-keygen.service
Wants=sshd-keygen.service

[Service] # 这个项目与实际执行的指令参数有关
EnvironmentFile=/etc/sysconfig/sshd
ExecStart=/usr/sbin/sshd -D $OPTIONS
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=42s

[Install] # 这个项目说明此unit 要挂载哪个target 底下
WantedBy=multi-user.target

可分为三个部分:

  • [Unit]: unit 本身的说明,以及与其他相依daemon 的设定,包括在什么服务之后才启动此unit 之类的设定值;
  • [Service], [Socket], [Timer], [Mount], [Path]..:不同的unit type 就得要使用相对应的设定项目。我们拿的是sshd.service 来当范本,所以这边就使用[Service] 来设定。这个项目内主要在规范服务启动的脚本、环境设定档档名、重新启动的方式等等。
  • [Install]:这个项目就是将此unit 安装到哪个target 里面去的意思!

具体设置参数含义:

[Unit] 部分
设定参数 参数意义说明
Description 就是当我们使用systemctl list-units 时,会输出给管理员看的简易说明!当然,使用systemctl status 输出的此服务的说明,也是这个项目!
Documentation 这个项目在提供管理员能够进行进一步的文件查询的功能!提供的文件可以是如下的资料:
Documentation=http://www...
Documentation=man:sshd(8)
Documentation=file:/etc/ssh/sshd_config
After 说明此unit 是在哪个daemon 启动之后才启动的意思!基本上仅是说明服务启动的顺序而已,并没有强制要求里头的服务一定要启动后此unit 才能启动。以sshd.service 的内容为例,该档案提到After 后面有network.target 以及sshd-keygen.service,但是若这两个unit 没有启动而强制启动sshd.service 的话, 那么sshd.service 应该还是能够启动的!这与Requires 的设定是有差异的喔!
Before 与After 的意义相反,是在什么服务启动前最好启动这个服务的意思。不过这仅是规范服务启动的顺序,并非强制要求的意思。
Requires 明确的定义此unit 需要在哪个daemon 启动后才能够启动!就是设定相依服务啦!如果在此项设定的前导服务没有启动,那么此unit 就不会被启动!
Wants 与Requires 刚好相反,规范的是这个unit 之后最好还要启动什么服务比较好的意思!不过,并没有明确的规范就是了!主要的目的是希望建立让使用者比较好操作的环境。因此,这个Wants 后面接的服务如果没有启动,其实不会影响到这个unit 本身!
Conflicts 代表冲突的服务!亦即这个项目后面接的服务如果有启动,那么我们这个unit 本身就不能启动!我们unit 有启动,则此项目后的服务就不能启动!反正就是冲突性的检查啦!
[Service] 部分
设定参数 参数意义说明
Type 说明这个daemon 启动的方式,会影响到ExecStart 喔!一般来说,有底下几种类型
simple:预设值,这个daemon 主要由ExecStart 接的指令串来启动,启动后常驻于记忆体中。
forking:由ExecStart 启动的程序透过spawns 延伸出其他子程序来作为此daemon 的主要服务。原生的父程序在启动结束后就会终止运作。传统的unit 服务大多属于这种项目,例如httpd 这个WWW 服务,当httpd 的程序因为运作过久因此即将终结了,则systemd 会再重新生出另一个子程序持续运作后, 再将父程序删除。据说这样的效能比较好!!
oneshot:与simple 类似,不过这个程序在工作完毕后就结束了,不会常驻在记忆体中。
dbus:与simple 类似,但这个daemon 必须要在取得一个D-Bus 的名称后,才会继续运作!因此设定这个项目时,通常也要设定BusName= 才行!
idle:与simple 类似,意思是,要执行这个daemon 必须要所有的工作都顺利执行完毕后才会执行。这类的daemon 通常是开机到最后才执行即可的服务!比较重要的项目大概是simple, forking 与oneshot 了!毕竟很多服务需要子程序(forking),而有更多的动作只需要在开机的时候执行一次(oneshot),例如档案系统的检查与挂载啊等等的。
EnvironmentFile 可以指定启动脚本的环境设定档!例如sshd.service 的设定档写入到/etc/sysconfig/sshd 当中!你也可以使用Environment= 后面接多个不同的Shell 变数来给予设定!
ExecStart 就是实际执行此daemon 的指令或脚本程式。你也可以使用ExecStartPre (之前) 以及ExecStartPost (之后) 两个设定项目来在实际启动服务前,进行额外的指令行为。但是你得要特别注意的是,指令串仅接受『指令参数参数...』的格式,不能接受<, >, >>, |, & 等特殊字符,很多的bash 语法也不支援喔!所以,要使用这些特殊的字符时,最好直接写入到指令脚本里面去!不过,上述的语法也不是完全不能用,亦即,若要支援比较完整的bash 语法,那你得要使用Type=oneshot 才行喔!其他的Type 才不能支援这些字符。
ExecStop 与systemctl stop 的执行有关,关闭此服务时所进行的指令。
ExecReload 与systemctl reload 有关的指令行为
Restart 当设定Restart=1 时,则当此daemon 服务终止后,会再次的启动此服务。举例来说,如果你在tty2 使用文字界面登入,操作完毕后登出,基本上,这个时候tty2 就已经结束服务了。但是你会看到萤幕又立刻产生一个新的tty2 的登入画面等待你的登入!那就是Restart 的功能!除非使用systemctl 强制将此服务关闭,否则这个服务会源源不绝的一直重复产生!
RemainAfterExit 当设定为RemainAfterExit=1 时,则当这个daemon 所属的所有程序都终止之后,此服务会再尝试启动。这对于Type=oneshot 的服务很有帮助!
TimeoutSec 若这个服务在启动或者是关闭时,因为某些缘故导致无法顺利『正常启动或正常结束』的情况下,则我们要等多久才进入『强制结束』的状态!
KillMode 可以是process, control-group, none 的其中一种,如果是process 则daemon 终止时,只会终止主要的程序(ExecStart 接的后面那串指令),如果是control-group 时, 则由此daemon 所产生的其他control-group 的程序,也都会被关闭。如果是none 的话,则没有程序会被关闭喔!
RestartSec 与Restart 有点相关性,如果这个服务被关闭,然后需要重新启动时,大概要sleep 多少时间再重新启动的意思。预设是100ms (毫秒)。
[Install] 部分
设定参数 参数意义说明
WantedBy 这个设定后面接的大部分是*.target unit !意思是,这个unit 本身是附挂在哪一个target unit 底下的!一般来说,大多的服务性质的unit 都是附挂在multi-user.target 底下!
Also 当目前这个unit 本身被enable 时,Also 后面接的unit 也请enable 的意思!也就是具有相依性的服务可以写在这里呢!
Alias 进行一个连结的别名的意思!当systemctl enable 相关的服务时,则此服务会进行连结档的建立!以multi-user.target 为例,这个家伙是用来作为预设操作环境default.target 的规划, 因此当你设定用成default.target 时,这个/etc/systemd/system/default.target 就会连结到/usr/lib/systemd/system/multi-user.target 啰!

示例:建立自己的备份服务

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
[root@study ~]# vim /backups/backup.sh 
#!/bin/bash

source="/etc /home /root /var/lib /var/spool/{cron,at,mail}"
target="/backups/backup-system-$(date +%Y-%m-%d).tar.gz"
[ ! -d /backups ] && mkdir /backups
tar -zcvf ${target} ${source} &> /backups/backup.log

[root@study ~]# chmod a+x /backups/backup.sh
[root@study ~]# ll /backups/backup.sh
-rwxr-xr-x . 1 root root 220 Aug 13 01:57 /backups/backup.sh
# 记得要有可执行的权限才可以喔!


[root@study ~]# vim /etc/systemd/system/backup.service
[Unit]
Description=backup my server
Requires=atd.service

[Service]
Type=simple
ExecStart=/bin/bash -c " echo /backups/backup.sh | at now"

[Install]
WantedBy=multi-user.target
# 因为ExecStart 里面有用到at 这个指令,因此, atd.service 就是一定要的服务!

[root@study ~]# systemctl daemon-reload
[root@study ~]# systemctl start backup.service
[root@study ~]# systemctl status backup.service
backup.service - backup my server
Loaded: loaded (/etc/systemd/system/backup.service; disabled)
Active: inactive (dead)

Aug 13 07:50:31 study.centos.vbird systemd[1]: Starting backup my server...
Aug 13 07:50:31 study.centos.vbird bash[20490]: job 8 at Thu Aug 13 07:50:00 2015
Aug 13 07:50:31 study.centos.vbird systemd[1]: Started backup my server.
# 为什么Active 是inactive 呢?这是因为我们的服务仅是一个简单的script 啊!
# 因此执行完毕就完毕了,不会继续存在记忆体中喔!

18. 认识与分析日志文件

19. 启动流程、模块管理与 Loader

19.1 Linux 启动流程分析

  1. 载入BIOS 的硬体信息与进行自我检测(自检),并根据设置取得第一个可启动的设备;

  2. 读取并执行第一个启动设备内MBR 的启动引导程序(boot Loader,亦即是grub2, spfdisk 等程序);

  3. 依据boot loader 的设置载入Kernel ,Kernel 会开始检测硬体与加载驱动程序;

  4. 在硬体驱动成功后,Kernel 会主动调用systemd 程序,并以default.target 流程启动;

    • systemd 执行sysinit.target 初始化系统及basic.target 准备操作系统;

    • systemd 启动multi-user.target 下的本机与服务器服务;

    • systemd 执行multi-user.target 下的/etc/rc.d/rc.local 文件;

    • systemd 执行multi-user.target 下的getty.target 及登录服务;

    • systemd 执行graphical 需要的服务

20. 基础系统设置与备份策略

21. 软件安装:源代码与 Tarball

21.1 开放源代码的软件安装与升级简介

通过 configure 与 make 进行编译示意图:

21.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
# 1. 编辑主程式: 
[root@study ~]# vim thanks.c
#include <stdio.h>
int main(void)
{
printf("Hello World\n");
thanks_2();
}
# 上面的thanks_2(); 那一行就是呼叫副程式啦!

[root@study ~]# vim thanks_2.c
#include <stdio.h>
void thanks_2(void)
{
printf("Thank you!\n");
}

# 2. 开始将原始码编译成为可执行的binary file :
[root@study ~]# gcc -c thanks.c thanks_2.c
[root@study ~]# ll thanks*
-rw-r--r--. 1 root root 75 Sep 4 11:43 thanks_2.c
-rw-r--r--. 1 root root 1496 Sep 4 11:43 thanks_2.o <==编译产生的!
-rw-r--r--. 1 root root 91 Sep 4 11:42 thanks.c
-rw-r--r--. 1 root root 1560 Sep 4 11:43 thanks.o <==编译产生的!

[root@study ~]# gcc -o thanks thanks.o thanks_2.o
[root@study ~]# ll thanks*
-rwxr-xr-x. 1 root root 8572 Sep 4 11:44 thanks <==最终结果会产生这玩意儿

# 3. 执行一下这个档案:
[root@study ~]# ./thanks
Hello World
Thank you!

加入链接选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@study ~]# vim sin.c 
#include <stdio.h>
#include <math.h>
int main(void)
{
float value;
value = sin ( 3.14 / 2 );
printf("%f\n",value);
}

# 编译
[root@study ~]# gcc sin.c
# 新的GCC 会主动将函数抓进来给你用,所以只要加上include <math.h> 就好了!

[root@study ~]# gcc sin.c -lm -L/lib -L/lib64 <==重点在-lm
[root@study ~]# ./a.out <==尝试执行新档案!
1.000000

特别注意,使用gcc 编译时所加入的那个-lm 是有意义的,他可以拆开成两部份来看:

  • -l :是『加入某个函式库(library)』的意思,
  • m :则是libm.so 这个函式库,其中, lib 与副档名(.a 或.so)不需要写

所以-lm 表示使用libm.so (或libm.a) 这个函式库的意思~至于那个-L 后面接的路径呢?这表示: 『我要的函式库libm.so 请到/lib 或/lib64 里面搜寻!』

gcc 简易用法(编译、参数和链接):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 仅将原始码编译成为目标档,并不制作连结等功能: 
[root@study ~]# gcc -c hello.c
# 会自动的产生hello.o 这个档案,但是并不会产生binary 执行档。

# 在编译的时候,依据作业环境给予最佳化执行速度
[root@study ~]# gcc -O hello.c -c
# 会自动的产生hello.o 这个档案,并且进行最佳化喔!

# 在进行binary file 制作时,将连结的函式库与相关的路径填入
[root@study ~]# gcc sin.c -lm -L/lib -I/usr/include
# 这个指令较常下达在最终连结成binary file 的时候,
# -lm 指的是libm.so 或libm.a 这个函式库档案;
# -L 后面接的路径是刚刚上面那个函式库的搜寻目录;
# -I 后面接的是原始码内的include 档案之所在目录。

# 将编译的结果输出成某个特定档名
[root@study ~]# gcc -o hello hello.c
# -o 后面接的是要输出的binary file 档名

# 在编译的时候,输出较多的讯息说明
[root@study ~]# gcc -o hello hello.c -Wall
# 加入-Wall 之后,程式的编译会变的较为严谨一点,所以警告讯息也会显示出来!

我们通常称 -Wall 或者-O 这些非必要的参数为旗标(FLAGS),因为我们使用的是C 程式语言,所以有时候也会简称这些旗标为 CFLAGS

21.3 用 make 进行宏编译

有以下源文件:

  • main.c :主要的目的是让使用者输入角度资料与呼叫其他三支副程式;
  • haha.c :输出一堆有的没有的讯息而已;
  • sin_value.c :计算使用者输入的角度(360) sin 数值;
  • cos_value.c :计算使用者输入的角度(360) cos 数值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 1. 先进行目标档的编译,最终会有四个*.o 的档名出现: 
[root@study ~]# gcc -c main.c
[root@study ~]# gcc -c haha.c
[root@study ~]# gcc -c sin_value.c
[root@study ~]# gcc -c cos_value.c

# 2. 再进行连结成为执行档,并加入libm 的数学函式,以产生main 执行档:
[root@study ~]# gcc -o main main.o haha.o sin_value.o cos_value.o -lm

# 3. 本程式的执行结果,必须输入姓名、360 度角的角度值来计算:
[root@study ~]# ./main
Please input your name: VBird <==这里先输入名字
Please enter the degree angle (ex> 90): 30 <==输入以360 度角为主的角度
Hi, Dear VBird, nice to meet you. <==这三行为输出的结果喔!
The Sin is: 0.50
The Cos is: 0.87

使用 make 编译:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1. 先编辑makefile 这个规则档,内容只要作出main 这个执行档
[root@study ~]# vim makefile
main: main.o haha.o sin_value.o cos_value.o
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
# 注意:第二行的gcc 之前是<tab> 按键产生的空格喔!

# 2. 尝试使用makefile 制订的规则进行编译的行为:
[root@study ~]# rm -f main *.o <==先将之前的目标档去除
[root@study ~]# make
cc -c -o main.o main.c
cc -c -o haha.o haha.c
cc -c -o sin_value.o sin_value.c
cc -c -o cos_value.o cos_value.c
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
# 此时make 会去读取makefile 的内容,并根据内容直接去给他编译相关的档案啰!

# 3. 在不删除任何档案的情况下,重新执行一次编译的动作:
[root@study ~]# make
make: `main' is up to date.
# 看到了吧!是否很方便呢!只会进行更新(update) 的动作而已。

make 优点:

  • 简化编译时所需要下达的指令;
  • 若在编译完成之后,修改了某个原始码档案,则make 仅会针对被修改了的档案进行编译,其他的 object file 不会被更动;
  • 最后可以依照相依性来更新(update) 执行档。

基本的 makefile 规则:

1
2
目标(target): 目标文件1 目标文件2
<tab> gcc -o 欲建立的执行文件 目标文件1 目标文件2

命令行必须要以tab 按键作为开头 ,他的规则基本上是这样的:

  • 在makefile 当中的# 代表注解;
  • <tab> 需要在命令行(例如gcc 这个编译器指令) 的第一个字符;
  • 目标(target) 与依赖文件(就是目标文件)之间需以『:』隔开。

示例:

1
2
3
4
5
6
7
8
9
10
# 1. 先编辑makefile 来建立新的规则,此规则的标的名称为clean : 
[root@study ~]# vi makefile
main: main.o haha.o sin_value.o cos_value.o
gcc -o main main.o haha.o sin_value.o cos_value.o -lm
clean:
rm -f main main.o haha.o sin_value.o cos_value.o

# 2. 以新的目标(clean) 测试看看执行make 的结果:
[root@study ~]# make clean <==就是这里!透过make 以clean 为标的
rm -rf main main.o haha.o sin_value.o cos_value.o

如此一来,我们的makefile 里面就具有至少两个标的,分别是main 与clean ,如果我们想要建立main 的话,输入『make main』,如果想要清除有的没的,输入『make clean』即可。

使用变量:

1
2
3
4
5
6
7
[root@study ~]# vi makefile 
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
main: ${OBJS}
gcc -o main ${OBJS} ${LIBS}
clean:
rm -f main ${OBJS}

与 bash shell 脚本的语法有点不同,变量的基本语法为:

  1. 变量与变量内容以『=』隔开,同时两边可以具有空格;
  2. 变量左边不可以有<tab> ,例如上面范例的第一行LIBS 左边不可以是<tab>;
  3. 变量与变量内容在『=』两边不能具有『:』;
  4. 在习惯上,变量最好是以『大写字母』为主;
  5. 运用变量时,以${变量} 或$(变量) 使用;
  6. 在该shell 的环境变量是可以被套用的,例如提到的CFLAGS 这个变数!
  7. 在命令行模式也可以设置变量。

设置 CFLAGS 环境变量两种方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 方式 1
[root@study ~]# CFLAGS="-Wall" make clean main
# 这个动作在上make 进行编译时,会去取用CFLAGS 的变数内容!


# 方式 2
[root@study ~]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = -Wall
main: ${OBJS}
gcc -o main ${OBJS} ${LIBS}
clean:
rm -f main ${OBJS}

环境变量使用规则(优先级):

  1. make 命令行后面加上的环境变量为优先;
  2. makefile 里面指定的环境变量第二;
  3. shell 原本具有的环境变量第三。

$@:代表目前的目标(target) ,故 makefile 也可写成

1
2
3
4
5
6
7
8
[root@study ~]# vi makefile
LIBS = -lm
OBJS = main.o haha.o sin_value.o cos_value.o
CFLAGS = -Wall
main: ${OBJS}
gcc -o $@ ${OBJS} ${LIBS} <==那个$@ 就是main !
clean:
rm -f main ${OBJS}

22. 软件安装 RPM、SRPM 与 YUM

22.1 RPM 软件管理程序:rpm

rpm 安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@study ~]# rpm -ivh package_name
选项与参数:
-i :install 的意思
-v :察看更细部的安装资讯画面
-h :以安装资讯列显示安装进度

范例一:安装原版光碟上的rp-pppoe 软体
[root@study ~]# rpm -ivh /mnt/Packages/rp-pppoe-3.11-5.el7.x86_64.rpm
Preparing... ################################# [100%]
Updating / installing...
1:rp-pppoe-3.11-5.el7 ################################# [100%]

范例二、一口气安装两个以上的软体时:
[root@study ~]# rpm -ivh a.i386.rpm b.i386.rpm *.rpm
# 后面直接接上许多的软体档案!

范例三、直接由网路上面的某个档案安装,以网址来安装:
[root@study ~]# rpm -ivh http://website.name/path/pkgname.rpm

强制安装选项:

可下达的选项 代表意义
--nodeps 使用时机:当发生软体属性相依问题而无法安装,但你执意安装时 危险性: 软体会有相依性的原因是因为彼此会使用到对方的机制或功能,如果强制安装而不考虑软体的属性相依, 则可能会造成该软体的无法正常使用!
--replacefiles 使用时机: 如果在安装的过程当中出现了『某个档案已经被安装在你的系统上面』的资讯,又或许出现版本不合的讯息(confilcting files) 时,可以使用这个参数来直接覆盖档案。 危险性: 覆盖的动作是无法复原的!所以,你必须要很清楚的知道被覆盖的档案是真的可以被覆盖喔!否则会欲哭无泪!
--replacepkgs 使用时机: 重新安装某个已经安装过的软体!如果你要安装一堆RPM 软体档案时,可以使用rpm -ivh *.rpm ,但若某些软体已经安装过了, 此时系统会出现『某软体已安装』的资讯,导致无法继续安装。此时可使用这个选项来重复安装喔!
--force 使用时机:这个参数其实就是--replacefiles 与--replacepkgs 的综合体!
--test 使用时机: 想要测试一下该软体是否可以被安装到使用者的Linux 环境当中,可找出是否有属性相依的问题。范例为: rpm -ivh pkgname.i386.rpm --test
--justdb 使用时机: 由于RPM 资料库破损或者是某些缘故产生错误时,可使用这个选项来更新软体在资料库内的相关资讯。
--nosignature 使用时机: 想要略过数位签章的检查时,可以使用这个选项。
--prefix 新路径 使用时机: 要将软体安装到其他非正规目录时。举例来说,你想要将某软体安装到/usr/local 而非正规的/bin, /etc 等目录, 就可以使用『 --prefix /usr/local 』来处理了。
--noscripts 使用时机:不想让该软体在安装过程中自行执行某些系统指令。 说明: RPM 的优点除了可以将档案放置到定位之外,还可以自动执行一些前置作业的指令,例如资料库的初始化。如果你不想要让RPM 帮你自动执行这一类型的指令,就加上他吧!

rpm 升级与更新:

-Uvh 后面接的软体即使没有安装过,则系统将予以直接安装; 若后面接的软体有安装过旧版,则系统自动更新至新版;
-Fvh 如果后面接的软体并未安装到你的Linux 系统上,则该软体不会被安装;亦即只有已安装至你 Linux 系统内的软体会被『升级』!

rpm 查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@study ~]# rpm -qa                               <==已安装软体
[root@study ~]# rpm -q[licdR] 已安装的软体名称 <==已安装软体
[root@study ~]# rpm -qf 存在于系统上面的某个档名 <==已安装软体
[root@study ~]# rpm -qp[licdR] 未安装的某个档案名称 <==查阅RPM档案
选项与参数:
查询已安装软体的资讯:
-q :仅查询,后面接的软体名称是否有安装;
-qa :列出所有的,已经安装在本机Linux 系统上面的所有软体名称;
-qi :列出该软体的详细资讯(information),包含开发商、版本与说明等;
-ql :列出该软体所有的档案与目录所在完整档名(list);
-qc :列出该软体的所有设定档(找出在/etc/ 底下的档名而已)
-qd :列出该软体的所有说明档(找出与man 有关的档案而已)
-qR :列出与该软体有关的相依软体所含的档案(Required 的意思)
-qf :由后面接的档案名称,找出该档案属于哪一个已安装的软体;
-q --scripts:列出是否含有安装后需要执行的脚本档,可用以debug 喔!
查询某个RPM 档案内含有的资讯:
-qp[icdlR]:注意-qp 后面接的所有参数以上面的说明一致。但用途仅在于找出
某个RPM 档案内的资讯,而非已安装的软体资讯!注意!
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
范例一:找出你的Linux 是否有安装logrotate 这个软体?
[root@study ~]# rpm -q logrotate
logrotate-3.8.6-4.el7.x86_64
[root@study ~]# rpm -q logrotating
package logrotating is not installed
# 注意到,系统会去找是否有安装后面接的软体名称。注意,不必要加上版本喔!
# 至于显示的结果,一看就知道有没有安装啦!

范例二:列出上题当中,属于该软体所提供的所有目录与档案:
[root@study ~]# rpm -ql logrotate
/etc/cron.daily/logrotate
/etc/logrotate.conf
....(以下省略)....
# 可以看出该软体到底提供了多少的档案与目录,也可以追踪软体的资料。

范例三:列出logrotate 这个软体的相关说明资料:
[root@study ~]# rpm -qi logrotate
Name : logrotate # 软体名称
Version : 3.8.6 # 软体的版本
Release : 4.el7 # 释出的版本
Architecture: x86_64 # 编译时所针对的硬体等级
Install Date: Mon 04 May 2015 05:52:36 PM CST # 这个软体安装到本系统的时间
Group : System Environment/Base # 软体是放再哪一个软体群组中
Size : 102451 # 软体的大小
License : GPL+ # 释出的授权方式
Signature : RSA/SHA256, Fri 04 Jul 2014 11:34:56 AM CST, Key ID 24c6a8a7f4a80eb5
Source RPM : logrotate-3.8.6-4.el7.src.rpm # 这就是SRPM 的档名
Build Date : Tue 10 Jun 2014 05:58:02 AM CST # 软体编译打包的时间
Build Host : worker1.bsys.centos.org # 在哪一部主机上面编译的
Relocations : (not relocatable)
Packager : CentOS BuildSystem <http://bugs.centos.org>
Vendor : CentOS
URL : https://fedorahosted.org/logrotate/
Summary : Rotates, compresses, removes and mails system log files
Description : # 这个是详细的描述!
The logrotate utility is designed to simplify the administration of
log files on a system which generates a lot of log files. Logrotate
allows for the automatic rotation compression, removal and mailing of
log files. Logrotate can be set to handle a log file daily, weekly,
monthly or when the log file gets to a certain size. Normally,
logrotate runs as a daily cron job.

Install the logrotate package if you need a utility to deal with the
log files on your system.
# 列出该软体的information (资讯),里面的资讯可多著呢,包括了软体名称、
# 版本、开发商、SRPM档案名称、打包次数、简单说明资讯、软体打包者、
# 安装日期等等!如果想要详细的知道该软体的资料,用这个参数来了解一下

范例四:分别仅找出logrotate 的设定档与说明档
[root@study ~]# rpm -qc logrotate
[root@study ~]# rpm -qd logrotate

范例五:若要成功安装logrotate ,他还需要什么档案的帮忙?
[root@study ~]# rpm -qR logrotate
/bin/sh
config(logrotate) = 3.8.6-4.el7
coreutils >= 5.92
....(以下省略)....
# 由这里看起来,呵呵~还需要很多档案的支援才行喔!

范例六:由上面的范例五,找出/bin/sh 是那个软体提供的?
[root@study ~]# rpm -qf /bin/sh
bash-4.2.46-12.el7.x86_64
# 这个参数后面接的可是『档案』呐!不像前面都是接软体喔!
# 这个功能在查询系统的某个档案属于哪一个软体所有的。

范例七:假设我有下载一个RPM 档案,想要知道该档案的需求档案,该如何?
[root@study ~]# rpm -qpR filename.i386.rpm
# 加上-qpR ,找出该档案需求的资料!

rpm 卸载与重建数据库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. 找出与pam 有关的软体名称,并尝试移除pam 这个软体: 
[root@study ~]# rpm -qa | grep pam
fprintd-pam-0.5.0-4.0.el7_0.x86_64
pam-1.1.8-12.el7.x86_64
gnome-keyring-pam-3.8.2-10.el7.x86_64
pam-devel-1.1.8-12.el7.x86_64
pam_krb5-2.4.8-4.el7.x86_64
[root@study ~]# rpm -e pam
error: Failed dependencies: <==这里提到的是相依性的问题
libpam.so.0()(64bit) is needed by (installed) systemd-libs-208-20.el7.x86_64
libpam.so.0()(64bit) is needed by (installed) libpwquality-1.2.3-4.el7.x86_64
....(以下省略)....

# 2. 若仅移除pam-devel 这个之前范例安装上的软体呢?
[root@study ~]# rpm -e pam-devel <==不会出现任何讯息!
[root@study ~]# rpm -q pam-devel
package pam-devel is not installed

[root@study ~]# rpm --rebuilddb <==重建数据库

22.2 YUM 在线升级功能

查询:yum [list|info|search|provides|whatprovides] 参数

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
[root@study ~]# yum [option] [查询工作项目] [相关参数]
选项与参数:
[option]:主要的选项,包括有:
-y :当yum 要等待使用者输入时,这个选项可以自动提供yes 的回应;
--installroot=/some/path :将该软体安装在/some/path 而不使用预设路径
[查询工作项目] [相关参数]:这方面的参数有:
search :搜寻某个软体名称或者是描述(description) 的重要关键字;
list :列出目前yum 所管理的所有的软体名称与版本,有点类似rpm -qa;
info :同上,不过有点类似rpm -qai 的执行结果;
provides:从档案去搜寻软体!类似rpm -qf 的功能!

范例一:搜寻磁碟阵列(raid) 相关的软体有哪些?
[root@study ~]# yum search raid
Loaded plugins: fastestmirror, langpacks # yum 系统自己找出最近的yum server
Loading mirror speeds from cached hostfile # 找出速度最快的那一部yum server
* base: ftp.twaren.net # 底下三个软体库,且来源为该伺服器!
* extras: ftp.twaren.net
* updates: ftp.twaren.net
....(前面省略)....
dmraid-events-logwatch.x86_64 : dmraid logwatch-based email reporting
dmraid-events.x86_64 : dmevent_tool (Device-mapper event tool) and DSO
iprutils.x86_64 : Utilities for the IBM Power Linux RAID adapters
mdadm.x86_64 : The mdadm program controls Linux md devices (software RAID arrays)
....(后面省略)....
# 在冒号(:) 左边的是软体名称,右边的则是在RPM 内的name 设定(软体名)
# 瞧!上面的结果,这不就是与RAID 有关的软体吗?如果想了解mdadm 的软体内容呢?

范例二:找出mdadm 这个软体的功能为何
[root@study ~]# yum info mdadm
Installed Packages <==这说明该软体是已经安装的了
Name : mdadm <==这个软体的名称
Arch : x86_64 <==这个软体的编译架构
Version : 3.3.2 <==此软体的版本
Release : 2.el7 <==释出的版本
Size : 920 k <==此软体的档案总容量
Repo : installed <==软体库回报说已安装的
From repo : anaconda
Summary : The mdadm program controls Linux md devices (software RAID arrays)
URL : http://www.kernel.org/pub/linux/utils/raid/mdadm/
License : GPLv2+
Description : The mdadm program is used to create, manage, and monitor Linux MD (software
: RAID) devices. As such, it provides similar functionality to the raidtools
: package. However, mdadm is a single program, and it can perform
: almost all functions without a configuration file, though a configuration
: file can be used to help with some common tasks.
# 不要跟我说,上面说些啥?自己找字典翻一翻吧!拜托拜托!

范例三:列出yum 伺服器上面提供的所有软体名称
[root@study ~]# yum list
Installed Packages <==已安装软体
GConf2.x86_64 3.2.6-8.el7 @anaconda
LibRaw.x86_64 0.14.8-5.el7.20120830git98d925 @base
ModemManager.x86_64 1.1.0-6.git20130913.el7 @anaconda
....(中间省略)....
Available Packages <==还可以安装的其他软体
389-ds-base.x86_64 1.3.3.1-20.el7_1 updates
389-ds-base-devel.x86_64 1.3.3.1-20.el7_1 updates
389-ds-base-libs.x86_64 1.3.3.1-20.el7_1 updates
....(底下省略)....
# 上面提供的意义为:『 软体名称版本在那个软体库内』

范例四:列出目前伺服器上可供本机进行升级的软体有哪些?
[root@study ~]# yum list updates <==一定要是update s喔!
Updated Packages
NetworkManager.x86_64 1:1.0.0-16.git20150121.b4ea599c.el7_1 updates
NetworkManager-adsl.x86_64 1:1.0.0-16.git20150121.b4ea599c.el7_1 updates
....(底下省略)....
# 上面就列出在那个软体库内可以提供升级的软体与版本!

范例五:列出提供passwd 这个档案的软体有哪些
[root@study ~]# yum provides passwd
passwd-0.79-4.el7.x86_64 : An utility for setting or changing passwords using PAM
Repo : base

passwd-0.79-4.el7.x86_64 : An utility for setting or changing passwords using PAM
Repo : @anaconda
# 找到啦!就是上面的这个软体提供了passwd 这个程式!

安装/升级:yum [install|update] 软件

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
[root@study ~]# yum [option] [安装与升级的工作项目] [相关参数]
选项与参数:
install :后面接要安装的软体!
update :后面接要升级的软体,若要整个系统都升级,就直接update 即可

范例一:将前一个练习找到的未安装的pam-devel 安装起来
[root@study ~]# yum install pam-devel
Loaded plugins: fastestmirror, langpacks # 首先的5 行在找出最快的yum server
Loading mirror speeds from cached hostfile
* base: ftp.twaren.net
* extras: ftp.twaren.net
* updates: ftp.twaren.net
Resolving Dependencies # 接下来先处理『属性相依』的软体问题
--> Running transaction check
---> Package pam-devel.x86_64 0:1.1.8-12.el7_1.1 will be installed
--> Processing Dependency: pam(x86-64) = 1.1.8-12.el7_1.1 for package: pam-devel-
1.1.8-12.el7_1.1.x86_64
--> Running transaction check
---> Package pam.x86_64 0:1.1.8-12.el7 will be updated
---> Package pam.x86_64 0:1.1.8-12.el7_1.1 will be an update
--> Finished Dependency Resolution
Dependencies Resolved

# 由上面的检查发现到pam 这个软体也需要同步升级,这样才能够安装新版pam-devel 喔!
# 至于底下则是一个总结的表格显示!
==========================================================================================
Package Arch Version Repository Size
==========================================================================================
Installing:
pam-devel x86_64 1.1.8-12.el7_1.1 updates 183 k
Updating for dependencies:
pam x86_64 1.1.8-12.el7_1.1 updates 714 k

Transaction Summary
==========================================================================================
Install 1 Package # 要安装的是一个软体
Upgrade ( 1 Dependent package) # 因为相依属性问题,需要额外加装一个软体!

Total size: 897 k
Total download size: 183 k # 总共需要下载的容量!
Is this ok [y/d/N]: y # 你得要自己决定是否要下载与安装!当然是y 啊!
Downloading packages: # 开始下载啰!
warning: /var/cache/yum/x86_64/7/updates/packages/pam-devel-1.1.8-12.el7_1.1.x86_64.rpm:
Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY
Public key for pam-devel-1.1.8-12.el7_1.1.x86_64.rpm is not installed
pam-devel-1.1.8-12.el7_1.1.x86_64.rpm | 183 kB 00:00:00
Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Importing GPG key 0xF4A80EB5:
Userid : "CentOS-7 Key (CentOS 7 Official Signing Key) <security@centos.org>"
Fingerprint: 6341 ab27 53d7 8a78 a7c2 7bb1 24c6 a8a7 f4a8 0eb5
Package : centos-release-7-1.1503.el7.centos.2.8.x86_64 (@anaconda)
From : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
Is this ok [y/N]: y # 只有在第一次安装才会出现这个项目『确定要安装数位签章』才能继续!
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
Warning: RPMDB altered outside of yum.
Updating : pam-1.1.8-12.el7_1.1.x86_64 1/3
Installing : pam-devel-1.1.8-12.el7_1.1.x86_64 2/3
Cleanup : pam-1.1.8-12.el7.x86_64 3/3
Verifying : pam-1.1.8-12.el7_1.1.x86_64 1/3
Verifying : pam-devel-1.1.8-12.el7_1.1.x86_64 2/3
Verifying : pam-1.1.8-12.el7.x86_64 3/3

Installed:
pam-devel.x86_64 0:1.1.8-12.el7_1.1

Dependency Updated:
pam.x86_64 0:1.1.8-12.el7_1.1

Complete!

删除:yum [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
26
27
28
29
30
31
32
33
[root@study ~]# yum remove pam-devel
Loaded plugins: fastestmirror, langpacks
Resolving Dependencies <==同样的,先解决属性相依的问题
--> Running transaction check
---> Package pam-devel.x86_64 0:1.1.8-12.el7_1.1 will be erased
--> Finished Dependency Resolution

Dependencies Resolved

==========================================================================================
Package Arch Version Repository Size
==========================================================================================
Removing:
pam-devel x86_64 1.1.8-12.el7_1.1 @updates 528 k

Transaction Summary
==========================================================================================
Remove 1 Package # 还好!没有相依属性的问题,仅移除一个软体!

Installed size: 528 k
Is this ok [y/N]: y
Downloading packages:
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
Erasing : pam-devel-1.1.8-12.el7_1.1.x86_64 1/1
Verifying : pam-devel-1.1.8-12.el7_1.1.x86_64 1/1

Removed:
pam-devel.x86_64 0:1.1.8-12.el7_1.1

Complete!

23. X Window 设置介绍

24. Linux 内核编译与管理


鸟哥的 Linux 私房菜阅读笔记
https://arcsin2.cloud/2023/02/20/鸟哥的-Linux-私房菜阅读笔记/
作者
arcsin2
发布于
2023年2月20日
许可协议