[Linux] 常用命令之【nl/sed/awk/wc/xargs/perl/sort/uniq/comm/cut】
目录
nl
nl : 在linux系统中用来计算文件中行号.
nl 可以将输出的文件内容自动的加上行号!其默认的结果与 cat -n 有点不太一样, nl 可以将行号做比较多的显示设计,包括位数与是否自动补齐 0 等等的功能。
[root@CENTOS7-20200707 ~]# nl -b a -n rz /etc/p【】asswd #内容按行号右对齐补0列出
000001 root:x:0:0:root:/root:/bin/bash
000002 bin:x:1:1:bin:/bin:/sbin/nologin
000003 daemon:x:2:2:daemon:/sbin:/sbin/nologin
000004 adm:x:3:4:adm:/var/adm:/sbin/nologin
000005 lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
000006 sync:x:5:0:sync:/sbin:/bin/sync
000007 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
000008 halt:x:7:0:halt:/sbin:/sbin/halt
000009 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
...
[root@CENTOS7-20200707 ~]# nl -b a -n rn /etc/passwd #内容按行号右对齐(不补0)列出
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
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
...
[root@CENTOS7-20200707 ~]# nl -b a -n ln /etc/passwd #内容按行号左对齐(不补0)列出
1 root:x:0:0:root:/root:/bin/bash
2 bin:x:1:1:bin:/bin:/sbin/nologin
3 daemon:x:2:2:daemon:/sbin:/sbin/nologin
4 adm:x:3:4:adm:/var/adm:/sbin/nologin
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
...
sed := Stream Editor(常用于动态替换文本) | tr
命令简介
sed对文本的处理很强大,并且sed非常小,参数少,容易掌握,他的操作方式根awk有点像。
sed按顺序逐行读取文件。
sed执行为该行指定的所有操作,并在完成请求的修改之后的内容显示出来,也可以存放到文件中。
参数 -i[扩展名], --in-place[=扩展名] 直接修改文件(如果指定扩展名就备份文件)

案例:内容替换
[root@sdc04 opt]# oldContent="xxx"
[root@sdc04 opt]# newContent="yyy"
[root@sdc04 opt]# fileName="file.txt"
[root@sdc04 opt]# sed -i -e 's/${oldContent}/${newContent}/g' ${fileName}
[root@sdc04 opt]# echo "apple hello world" | sed 's/hello/johnny/g'
apple johnny world
【扩展/延申】cat filename | tr [被替换字符] [替换字符] >> filename1
[root@sdc04 opt]# echo "apple hello world" | tr [a-z] [A-Z]
APPLE HELLO WORLD
【sed带变量的动态参数替换】
[root@sdc04 opt]# str=3535
[root@sdc04 opt]# echo "apple hello world" | sed 's/hello/$str/g'
apple $str world
[root@sdc04 opt]# echo "apple hello world" | sed s/hello/$str/g
apple 3535 world
【补充:变量/shell命令输出的字符串不能带有空格】
[root@sdc04 opt]# echo "apple hello world" | sed s/hello/$(date "+%Y-%m-%d %H:%M:%S %A")/g
sed:-e 表达式 #1,字符 18:未终止的“s”命令
[root@sdc04 opt]# echo "apple hello world" | sed s/hello/$(date "+%Y-%m-%d_%H:%M:%S_%A")/g
apple 2021-01-08_22:50:11_星期五 world

[root@CENTOS7-20200707 ~]# cat ./myfile.txt | grep "johnny" | sed 's/johnny/hello/g' [直接将将文本中出现"johnny"的行中的"johnny"内容全部替换为"hello"]
(格式: sed 's/将被替换的字符/新的字符/g')
[root@CENTOS7-20200707 ~]# cat ./myfile.txt | grep "johnny" | sed -i 's/johnny/hello/g' [直接将将文本中出现"johnny"的行中的"johnny"内容全部替换为"hello"]
(参数-i: 可让sed直接去修改后面接的文件内容而非屏幕输出)
[root@CENTOS7-20200707 ~]# find <targetDirPath> -name *.properties | xargs sed -i 's/old_str/new_str/g' # 替换<targetDirPath>目录下所有名为.properties的文件内字符串old_str为new_str
[root@CENTOS7-20200707 ~]# nl -b a -n rz /etc/passwd | sed '2,5d' # /etc/passwd文件内容按行号右对齐补0列出,且删除第2-5行
000001 root:x:0:0:root:/root:/bin/bash
000006 sync:x:5:0:sync:/sbin:/bin/sync
000007 shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
000008 halt:x:7:0:halt:/sbin:/sbin/halt
000009 mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
000010 operator:x:11:0:operator:/root:/sbin/nologin
...
案例: 替换域名为 ip
[root@vmw-b go-study]# cat /usr/local/opengemini/topology.yaml | grep -i vmw-b.servers.com | grep -v grep
- host: vmw-b.servers.com
- host: vmw-b.servers.com
- host: vmw-b.servers.com
[root@vmw-b go-study]# cat /usr/local/opengemini/topology.yaml | grep -i vmw-b.servers.com | grep -v grep | sed "s/vmw-b.servers.com/192.168.101.102/g"
- host: 192.168.101.102
- host: 192.168.101.102
- host: 192.168.101.102
sed -i "s/vmw-b.servers.com/192.168.101.102/g" /usr/local/opengemini/topology.yaml
# 检查: cat /usr/local/opengemini/topology.yaml | grep -i 192.168.101.102 | grep -v grep
sed -i "s/vmw-c.servers.com/192.168.101.103/g" /usr/local/opengemini/topology.yaml
# 检查: cat /usr/local/opengemini/topology.yaml | grep -i 192.168.101.103 | grep -v grep
sed -i "s/vmw-d.servers.com/192.168.101.104/g" /usr/local/opengemini/topology.yaml
# 检查: cat /usr/local/opengemini/topology.yaml | grep -i 192.168.101.104 | grep -v grep
sed -i "s/vmw-e.servers.com/192.168.101.105/g" /usr/local/opengemini/topology.yaml
# 检查: cat /usr/local/opengemini/topology.yaml | grep -i 192.168.101.105 | grep -v grep
案例:在文件末尾追加内容
- 在使用
sed
命令向文本文件demo.txt
追加内容时,你可以使用sed
的a
(append)命令。
这个命令允许你在文件的特定行之后追加内容。
如果你想要在文件的末尾追加内容,可以使用$
符号来表示文件的最后一行
sed -i '$a This is a new line.' demo.txt
root@xxx:~# cat demo.txt
hello world
hello world
root@xxx:~# sed -i '$a This is a new line.' demo.txt
root@xxx:~# cat demo.txt
hello world
hello world
This is a new line.
案例:在文件的特定行追加内容
- 如果你想要在文件的特定行之后追加内容,可以指定行号。
例如,假设你想要在 demo.txt 的第2行之后追加
"This is a new line."
,可以使用以下命令:
sed -i '2a This is a new line.' file.txt
root@xxx:~# cat demo.txt
hello world\n
hello world
This is a new line.
root@xxx:~# sed -i '2a This is a new line2.' demo.txt
root@xxx:~# cat demo.txt
hello world\n
hello world
This is a new line2.
This is a new line.
- 特别注意
- 使用
-i
选项时,sed
会直接修改原文件。如果你不想修改原文件,可以去掉-i
选项,将输出重定向到另一个文件:
sed '2a This is a new line.' file.txt > newfile.txt
这样,修改后的内容将保存到 newfile.txt 中,原文件 file.txt 保持不变。
如果你想要在文件的每一行之后都追加相同的内容,可以使用
a
命令,但不指定行号:
sed -i 'a This is a new line.' file.txt
这将导致 "This is a new line." 被追加到每一行之后。
awk
awk有3种形式:awk,gawk,nawk。平时所说的awk,其实就是gawk。


- 按指定分隔符,换行输出
(1行 => N行)
[root@centos7 ~]# openssl ciphers
ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DH-DSS-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DH-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DH-RSA-AES256-SHA256:DH-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:DH-RSA-AES256-SHA:DH-DSS-AES256-SHA:DHE-RSA-CAMELLIA256-SHA:DHE-DSS-CAMELLIA256-SHA:DH-RSA-CAMELLIA256-SHA:DH-DSS-CAMELLIA256-SHA:ECDH-RSA-AES256-GCM-SHA384:ECDH-ECDSA-AES256-GCM-SHA384:ECDH-RSA-AES256-SHA384:ECDH-ECDSA-AES256-SHA384:ECDH-RSA-AES256-SHA:ECDH-ECDSA-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:CAMELLIA256-SHA:PSK-AES256-CBC-SHA:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:DH-DSS-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DH-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-SHA256:DHE-DSS-AES128-SHA256:DH-RSA-AES128-SHA256:DH-DSS-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA:DH-RSA-AES128-SHA:DH-DSS-AES128-SHA:DHE-RSA-SEED-SHA:DHE-DSS-SEED-SHA:DH-RSA-SEED-SHA:DH-DSS-SEED-SHA:DHE-RSA-CAMELLIA128-SHA:DHE-DSS-CAMELLIA128-SHA:DH-RSA-CAMELLIA128-SHA:DH-DSS-CAMELLIA128-SHA:ECDH-RSA-AES128-GCM-SHA256:ECDH-ECDSA-AES128-GCM-SHA256:ECDH-RSA-AES128-SHA256:ECDH-ECDSA-AES128-SHA256:ECDH-RSA-AES128-SHA:ECDH-ECDSA-AES128-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA:SEED-SHA:CAMELLIA128-SHA:PSK-AES128-CBC-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:EDH-DSS-DES-CBC3-SHA:DH-RSA-DES-CBC3-SHA:DH-DSS-DES-CBC3-SHA:ECDH-RSA-DES-CBC3-SHA:ECDH-ECDSA-DES-CBC3-SHA:DES-CBC3-SHA:IDEA-CBC-SHA:PSK-3DES-EDE-CBC-SHA:KRB5-IDEA-CBC-SHA:KRB5-DES-CBC3-SHA:KRB5-IDEA-CBC-MD5:KRB5-DES-CBC3-MD5:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:ECDH-RSA-RC4-SHA:ECDH-ECDSA-RC4-SHA:RC4-SHA:RC4-MD5:PSK-RC4-SHA:KRB5-RC4-SHA:KRB5-RC4-MD5
# openssl ciphers | awk 'BEGIN{i=1}{gsub(/:/,",\n");i++;print}' 【按符号`:`分割】
ECDHE-RSA-AES256-GCM-SHA384,
ECDHE-ECDSA-AES256-GCM-SHA384,
ECDHE-RSA-AES256-SHA384,
ECDHE-ECDSA-AES256-SHA384,
ECDHE-RSA-AES256-SHA,
ECDHE-ECDSA-AES256-SHA,
DH-DSS-AES256-GCM-SHA384,
DHE-DSS-AES256-GCM-SHA384,
DH-RSA-AES256-GCM-SHA384,
DHE-RSA-AES256-GCM-SHA384,
DHE-RSA-AES256-SHA256,
DHE-DSS-AES256-SHA256,
DH-RSA-AES256-SHA256,
DH-DSS-AES256-SHA256,
DHE-RSA-AES256-SHA,
DHE-DSS-AES256-SHA,
DH-RSA-AES256-SHA,
DH-DSS-AES256-SHA,
DHE-RSA-CAMELLIA256-SHA,
DHE-DSS-CAMELLIA256-SHA,
DH-RSA-CAMELLIA256-SHA,
DH-DSS-CAMELLIA256-SHA,
ECDH-RSA-AES256-GCM-SHA384,
ECDH-ECDSA-AES256-GCM-SHA384,
ECDH-RSA-AES256-SHA384,
ECDH-ECDSA-AES256-SHA384,
ECDH-RSA-AES256-SHA,
ECDH-ECDSA-AES256-SHA,
AES256-GCM-SHA384,
AES256-SHA256,
AES256-SHA,
CAMELLIA256-SHA,
PSK-AES256-CBC-SHA,
ECDHE-RSA-AES128-GCM-SHA256,
ECDHE-ECDSA-AES128-GCM-SHA256,
ECDHE-RSA-AES128-SHA256,
ECDHE-ECDSA-AES128-SHA256,
ECDHE-RSA-AES128-SHA,
ECDHE-ECDSA-AES128-SHA,
DH-DSS-AES128-GCM-SHA256,
DHE-DSS-AES128-GCM-SHA256,
DH-RSA-AES128-GCM-SHA256,
...
- F: 指定字段分隔符
[root@CENTOS7-20200707 johnny]# echo "32:34" | awk -F: '{print "max = ",max($1,$2)}
> function max(one,two){
> if(one > two){
> return one;
> }else{
> return two;
> }
> }'
max = 34
[root@CENTOS7-20200707 johnny]# echo "aa bb cc : dd ee ff" | awk -F ':' '{print $1}' 【F: 指定字段分隔符】
aa bb cc
- FS(字段分隔符)
默认: 空格,制表符
$0 表示当前整行内容; $1,$ 2 表示第一个字段,第二个字段
[root@CENTOS7-20200707 johnny]# echo "aa bb cc dd" | awk '{ print $0}'
aa bb cc dd
[root@CENTOS7-20200707 johnny]# echo "aa bb cc dd" | awk '{ print $1}'
aa
- NR
打印文本第1行
awk 'NR==1{print}' filename
打印文本第二行第一列
sed -n "2, 1p" filename | awk 'print $1'
wc
为统计指定文件中的字节数、单词数、行数, 并将统计结果显示输出
[root@CENTOS7-20200707 johnny]# cat /etc/passwd | wc -l # 查看passwd文件有多少行
22
[root@CENTOS7-20200707 johnny]# echo "aaa bbb ccc" |wc -w # 查看输出有多少个单词
3
[root@CENTOS7-20200707 johnny]# cat /etc/passwd | grep "root" | wc -l # 统计指定文件中出现"root"字符的总行数
2
[root@CENTOS7-20200707 johnny]# echo "12344hbjkl" |wc -m # 查看输出有多少个字符
11
xargs := eXtended ARGuments

xargs 又称管道命令,构造参数等;
xargs 可以将管道或标准输入(stdin)数据转换成命令行参数,也能够从文件的输出中读取数据。
xargs 也可以将单行或多行文本输入转换为其他格式,例如多行变单行,单行变多行。
xargs 是给命令传递参数的一个过滤器,也是组合多个命令的一个工具。它把一个数据流分割为一些足够小的块,以方便过滤器和命令进行处理 。
即 把其他命令的给它的数据 传递给它后面的命令作为参数
[root@CENTOS7-20200707 johnny]# find /usr/sbin /7000 | xargs ls -l [找出/usr/sbin下具有特殊权限7000的文件名,并使用ls -l列出详细属性]
[root@localhost ~]# ls | grep .php | xargs -i mv {} {}.bak [将当前目录下php文件,改名字]
[root@localhost ~]# ls | grep .php | xargs -I {} mv {} {}.bak
(参数-i : {} 代替 传递的数据)
[root@localhost ~]# find <targetDirPath> -name "*.json" | xargs grep <key-word> # 查找所有含有<key-word>关键词的<json>文件
[root@localhost ~]# find ./ -name "*.tmp" | xargs -i rm -rf {} # 删除当前文件夹下的,tmp文件
[root@localhost ~]# find ./ -type f -print0 |xargs -0 rm # 删除该目录的所有普通文件
[root@localhost ~]# find <targetDirPath> -name *.properties |xargs sed -i 's/old_str/new_str/g' # 替换<targetDirPath>目录下所有名为.properties的文件内字符串old_str为new_str
[root@localhost ~]# find /opt/wydaas/webapps/wydaas/static/dist/js/main.*.js -name main.*.js | xargs sed -i "s/数据共享资源中心/数据共享中心/g"
[root@localhost ~]# ls -l | xargs -I @@ echo @@


【补充DEMO】

perl
# 字符串不包含/
perl -pi -e "s/目标字符串/替换字符串/g" 目标文件
# 字符串包含/ 则可改成 #
perl -pi -e "s#目标字符串/替换字符串/g" 目标文件
应用:升级Maven Project下的快照版本
#!/bin/bash
# description : 升级本工程下的快照版本
# note :
# [1] 用于开发者在【本地电脑】端执行本脚本,以进行快速升级本maven工程内多个pom.xml文件的快照版本
# [2] 要求本工程目录内除了自身工程为快照版本外,不得引入别的快照版本(Snapshot Version)
# [3] 需在支持 perl / find 的 Linux shell 命令的命令行环境下执行,推荐: Git Bash
# author : johnny zen
# url : https://www.cnblogs.com/johnnyzen
# reference-doc :
# https://www.cnblogs.com/johnnyzen/p/15067593.html
# https://blog.csdn.net/bluewait321/article/details/110643279
# usage :
# sample: 1.1.9-SNAPSHOT ==> 1.1.10-SNAPSHOT
# cmd : ./upgrade-project-snapshot-version.sh 1.1.9 1.1.10
# create-time : 2023-03-01 17:40
# [1] variables
oldSnapshotVersion="${1}-SNAPSHOT"
newSnapshotVersion="${2}-SNAPSHOT"
projectName="bdp-gateway-service-parent"
fileName="pom.xml"
echo "[INFO] oldSnapshotVersion: ${oldSnapshotVersion}"
echo "[INFO] newSnapshotVersion: ${newSnapshotVersion}"
echo "[INFO] fileName: ${fileName}"
# [2] execution
for filePath in `find ./ -type f -name "${fileName}"` ;
do
echo "[INFO] perl -pi -e 's#${oldSnapshotVersion}#${newSnapshotVersion}#g' ${filePath}"
perl -pi -e "s/${oldSnapshotVersion}/${newSnapshotVersion}/g" ${filePath}
done
echo "[INFO] success to upgrade snapshot version at ${fileName} for in the maven project(${projectName})~"

sort
作用
- Linux
sort
命令用于将文本文件内容加以排序。 sort
可针对文本文件的内容,以行为单位来排序。
1、排序时,默认是按每行/每个域的首字符排序,数字的优先级要 > 字符的优先级
2、不指定升序还是降序时,默认是升序
-r
参数 : 逆序
语法
sort [-bcdfimMnr][-o<输出文件>][-t<分隔字符>][+<起始栏位>-<结束栏位>][--help][--verison][文件][-k field1[,field2]]
参数
-b
忽略每行前面开始出的空格字符。-c
检查文件是否已经按照顺序排序。-d
排序时,处理英文字母、数字及空格字符外,忽略其他的字符。-f
排序时,将小写字母视为大写字母。-i
排序时,除了040至176之间的ASCII字符外,忽略其他的字符。-m
将几个排序好的文件进行合并。-M
将前面3个字母依照月份的缩写进行排序。-n
依照【数值】的大小排序。-u
意味着是【唯一的(unique)】,输出的结果是去重好了的。-o<输出文件>
将排序后的结果存入指定的文件。-r
以【相反的顺序】来排序。-t<分隔字符>
指定排序时所用的栏位分隔字符。+<起始栏位>-<结束栏位>
以指定的栏位来排序,范围由起始栏位到结束栏位的前一栏位。--help
显示帮助。--version
显示版本信息。[-k field1[,field2]]
按指定的列进行排序。
示例
- 数据准备
> cat salary.txt
google 110 5000
baidu 100 5000
guge 50 3000
sohu 100 4500
sohu 30 1500
示例:逆序排序(-r
)
- 单独使用
sort
命令对文件内容进行排序,默认情况按照第一列字段内容的首字母进行升序排序
> sort salary.txt
baidu 100 5000
google 110 5000
guge 50 3000
sohu 100 4500
sohu 30 1500
- 使用
-r
选项可按照逆序排序,但排序依据仍旧是第一列字段内容的首字母
sohu 30 1500
sohu 100 4500
guge 50 3000
google 110 5000
baidu 100 5000
示例:指定按照某列进行排序(-k
)
先使用salary.txt
文本内容进行实验,输入以下命令
> sort -k 2 salary.txt
sohu 100 4500
baidu 100 5000
google 110 5000
sohu 30 1500
guge 50 3000
从排序结果可见,按照第二列字段的内容进行升序排序的。
示例:按照数值大小排列(-n
)
- 以第2列的数值作为排序键,默认升序排序
$ sort -k 2 -n salary.txt
sohu 30 1500
guge 50 3000
baidu 100 5000
sohu 100 4500
google 110 5000
- 以第3列的数值作为排序键,降序排序
$ sort -k 3 -nr salary.txt
google 110 5000
baidu 100 5000
sohu 100 4500
guge 50 3000
sohu 30 1500
示例:指定字段分隔符(-t
)
该选项的作用是指定字段分隔符
- 数据准备
> cat data.csv
name,age,city
Alice,30,New York
Bob,25,Los Angeles
Charlie,35,Chicago
- 不指定分隔键的排序
> sort -k2 -n data.csv
Alice,30,New York
Bob,25,Los Angeles
Charlie,35,Chicago
name,age,city
不符合我们预期。原因:
- 当我们想使用-k选项找到第二列,它首先需要知道文本是按照什么分割符划分第一列第二列
- 我们文件内容的每一行的每一列都是按照逗号进行分割的,sort命令不知道这一点
- 默认情况下,sort默认使用空格或者制表符作为分隔符。因此,它此时找到的第二列是一个空列,也就是每一行的末尾
- 指定分隔键的排序
-t
选项的作用:正是让sort知道我们的文本是按照什么分割符进行划分的,好让-k选项真正生效
> sort -t ',' -k 2 -n data.csv
name,age,city
Bob,25,Los Angeles
Alice,30,New York
Charlie,35,Chicago
$ sort -t ',' -k 2 -n data.csv
name,age,city
Bob,25,Los Angeles
Alice,30,New York
Charlie,35,Chicago
uniq
作用
-
uniq
命令用于去除文件中重复出现的行。 -
它可以识别相邻的重复行,并根据需要保留或删除这些重复行。
-
主要功能
- 识别并删除相邻重复行
- uniq 默认会删除连续的重复行,只保留一个实例。
- 保留重复行的第一个实例
- 如果需要保留第一个重复行,uniq 会保留第一次出现的行,并删除后续的重复行。
- 统计重复次数
- 可以输出每一行及其重复次数。
- 忽略指定列的比较
- 可以指定忽略某些列来进行比较,这对于某些特定的文本处理非常有用。
语法
uniq [OPTION]... [INPUT [OUTPUT]]
- 如果不指定输入文件,则默认从标准输入(stdin)读取数据。
(1) input-file 是要处理的输入文件名。如果未指定,uniq 将从标准输入读取数据。
(2)output-file 是输出文件的名称。如果未指定,uniq 将把结果输出到标准输出。
- 获取帮助
uniq –h
参数
-c
: 显示每一行及其重复次数-d
: 只显示重复行-u
: 只显示唯一行(即不重复的行)-f {N}
: 忽略每行开头的 N 个字段-w {N}
: 比较时忽略每行的前 N 字符-n {N}
: 指定忽略每行的前 N 列(适用于固定宽度的列)
示例
- 数据准备
> cat input.txt
apple
banana
apple
cherry
apple
date
删除相邻重复行
$ sort input.txt | uniq
apple
banana
cherry
date
显示每一行及其重复次数
$ sort input.txt | uniq -c
3 apple
1 banana
1 cherry
1 date
只显示重复行
$ sort input.txt | uniq -d
apple
只显示唯一行
$ sort input.txt | uniq -u
banana
cherry
date
忽略每行开头的 1 个字段
假设每行由空格分隔的多个字段组成:
$ cat input.txt | uniq -f 2
apple
comm
- 命令介绍
comm
是LINUX
系统下的一个指令,用来对两个己排序文件进行逐行比较。
comm
命令对两个已经排好序的文件进行比较。其中,filel和file2是已经排好序的文件。
- 命令语法
comm[ -1 -2 -3 ] File1 File2
comm从这2个文件中读取正文行,进行比较,最后生成3列输出:
- 仅在filel中出现的行
- 仅在file2中出现的行
- 在2个文件中都存在的行。
如果文件名为“-”则表示从标准输入读取。
-123
: 选项1,2和3,分别表示不显示comm输出中的第一列、第二列和第三列。
- 参数选项
-1 不输出第一列。
-2 不输出第二列。
-3 不输出第三列。
--check-order 检查输入行是否正确的排序,即使它们确实是已排序过的。
--nocheck-order 不检查输入行是否正确的排序。
--output-delimiter=STR 使用STR作为输出列之间的分隔符而不是默认的TAB。
--total 额外地增加第四列输出概要。
-z, --zero-terminated 设置行终止符为NUL(空),而不是换行符。
--help 显示帮助信息并退出。
--version 显示版本信息并退出。
- 返回值
返回
0
表示成功,返回非0值表示失败。
- 案例: 对比文本 b.txt,相比 all.txt 的差异,每个文本的每行含有一个唯一编号
# 确保两个文件都是按行排序的
sort all-devices.txt -o all-devices.txt
sort exported-devices.txt -o exported-devices.txt
# 使用comm命令找出 exported-devices.txt 中缺少的唯一编号
comm -23 all-devices.txt exported-devices.txt > diff.txt
- 案例:比较文件ml和m2,且只显示它们共有的行
comm -12 m1.txt m2.txt
cut
作用
cut
命令是一个用于文本处理的工具,特别是在 Linux 和 Unix 系统中。
它可以从文件或标准输入中提取特定的列或字段,常用于格式化文本、分析日志文件等。
语法
cut [选项] [文件...]
- 参数
-f
:指定要提取的字段,字段号以逗号分隔。与-d
选项配合使用。-d
:指定字段分隔符,默认为制表符(\t
)。可以指定为其他字符(如逗号、冒号等)。-c
:直接按字符位置提取字符。--complement
:提取不匹配给定字段或字符的位置。-s
:忽略没有分隔符的行(仅在使用-d
和-f
时有效)。
示例
示例: 提取特定字段
- 假设有一个文件 data.txt,内容如下:
name,age,gender
Alice,30,female
Bob,25,male
Carol,28,female
- 提取第二列(年龄):
cut -d ',' -f 2 data.txt
out:
age
30
25
28
- 提取第一列(名字)和第三列(性别):
cut -d ',' -f 1,3 data.txt
out:
name,gender
Alice,female
Bob,male
Carol,female
示例: 按字符位置提取
- 假设有一个文本文件 text.txt,内容如下:
abcdefg
1234567
- 提取前 3 个字符:
cut -c 1-3 text.txt
out:
abc
123
- 提取第 2 到第 5 个字符:
cut -c 2-5 text.txt
out:
bcd
234
示例: 使用 --complement
- 提取除第二列外的其它列:
cut -d ',' -f 2 --complement data.txt
out:
name,gender
Alice,female
Bob,male
Carol,female
示例 4: 忽略没有分隔符的行
假设有一个文件 data_with_empty.txt,内容如下:
name:age:gender
Alice:30:female
Bob:25:male
NoData
Carol:28:female
- 提取特定字段,忽略没有分隔符的行:
cut -d ':' -f 1,3 -s data_with_empty.txt
out:
name:gender
Alice:female
Bob:male
Carol:female
示例: 从标准输入提取
- 使用
echo
命令结合cut
:
从标准输入提取:
echo "apple:banana:cherry" | cut -d ':' -f 2
out:
banana
示例: 实际应用
- 处理 CSV 文件:假设你有一个 CSV 文件,你可以快速提取特定列:
cat users.csv | cut -d ',' -f 1,3
- 处理日志文件:如果有一个日志文件 access.log,你想提取 IP 地址和请求时间:
cat access.log | cut -d ' ' -f 1,4
示例: 结合其他命令
- 与
sort
和uniq
结合:查找文件中各个不同名字的数量。
cut -d ',' -f 1 data.txt | sort | uniq | wc -l
- 提取特定格式的文本:假设你需要从多个文本文件中提取数字:
cut -d ':' -f 2 numbers.txt
示例: 提取指定目录下的文件和子目录的空间大小
如果是制表符分隔,提取文件路径
root@xxx:/# du --max-depth=2 -a /usr/local/opengemini/gemini-log/logs/ | grep -i ".error.log"
1516 /usr/local/opengemini/gemini-log/logs/ts-sql-8086/sql.error.log
2132 /usr/local/opengemini/gemini-log/logs/monitored/monitor.error.log
4 /usr/local/opengemini/gemini-log/logs/ts-meta-8091/meta.error.log
4 /usr/local/opengemini/gemini-log/logs/ts-store-8401/store.error.log
root@xxx:/# du --max-depth=2 -a /usr/local/opengemini/gemini-log/logs/ | grep -i ".error.log" | cut -f 1
1516
2132
4
4
root@xxx:/# du --max-depth=2 -a /usr/local/opengemini/gemini-log/logs/ | grep -i ".error.log" | cut -f 2
/usr/local/opengemini/gemini-log/logs/ts-sql-8086/sql.error.log
/usr/local/opengemini/gemini-log/logs/monitored/monitor.error.log
/usr/local/opengemini/gemini-log/logs/ts-meta-8091/meta.error.log
/usr/local/opengemini/gemini-log/logs/ts-store-8401/store.error.log
root@xxx:/# du --max-depth=2 -a /usr/local/opengemini/gemini-log/logs/ | grep -i ".error.log" | cut -f 1,2
1516 /usr/local/opengemini/gemini-log/logs/ts-sql-8086/sql.error.log
2132 /usr/local/opengemini/gemini-log/logs/monitored/monitor.error.log
4 /usr/local/opengemini/gemini-log/logs/ts-meta-8091/meta.error.log
4 /usr/local/opengemini/gemini-log/logs/ts-store-8401/store.error.log
- 参考:
awk
命令 和xargs
命令
// 基于 awk 命令,遍历每一行并输出大于4KB的文件路径
root@xxx:/# du --max-depth=2 -a /usr/local/opengemini/gemini-log/logs/ | grep -i ".error.log" | awk '$1 > 4 {print $2}'
/usr/local/opengemini/gemini-log/logs/ts-sql-8086/sql.error.log
/usr/local/opengemini/gemini-log/logs/monitored/monitor.error.log
注:`awk`命令的`$1`表示第一列,`$2`表示第二列。所以,我们可以使用`$1 > 4`来判断第1列的值是否大于4。
// 基于 awk 命令,遍历每一行并输出大于4KB的文件路径;然后遍历这些文件的文本内容,统计其文件内前 n 行中含"error"字符串的个数
du --max-depth=2 -a /usr/local/opengemini/gemini-log/logs/ | grep -i ".error.log" | awk '$1 > 4 {print $2}' | xargs cat | head -n 10 | grep '"error"' | wc -l
- 进一步运用到极致:
// 基于 awk 命令,遍历每一行并输出大于4KB的文件路径;然后遍历这些文件的文本内容,做统计
du --max-depth=2 -a /usr/local/opengemini/gemini-log/logs/ | grep -i ".error.log" | awk '$1 > 4 {print $2}' | xargs cat | grep '"error"' | head -n 10 | jq -r '"\(.errno) \"\(.error)\""' | sort -n -k 1 | uniq -c | sort -k 1 -nr
out:
xargs: cat: terminated by signal 13
510 10215013 "point time is expired, compared with rp duration"
266 null "partial write: point time is expired, compared with rp duration dropped=2"
40 null "partial write: point time is expired, compared with rp duration dropped=4"
39 null "partial write: point time is expired, compared with rp duration dropped=5"
39 null "partial write: point time is expired, compared with rp duration dropped=19"
37 null "partial write: point time is expired, compared with rp duration dropped=1"
14 null "partial write: point time is expired, compared with rp duration dropped=14"
9 null "partial write: point time is expired, compared with rp duration dropped=26"
5 null "partial write: point time is expired, compared with rp duration dropped=36"
4 null "partial write: point time is expired, compared with rp duration dropped=9"
4 null "partial write: point time is expired, compared with rp duration dropped=6"
4 null "partial write: point time is expired, compared with rp duration dropped=16"
2 null "partial write: point time is expired, compared with rp duration dropped=8"
2 null "partial write: point time is expired, compared with rp duration dropped=7"
2 null "partial write: point time is expired, compared with rp duration dropped=22"
2 null "partial write: point time is expired, compared with rp duration dropped=20"
2 null "partial write: point time is expired, compared with rp duration dropped=18"
2 null "partial write: point time is expired, compared with rp duration dropped=17"
2 null "partial write: point time is expired, compared with rp duration dropped=15"
2 null "partial write: point time is expired, compared with rp duration dropped=13"
2 null "partial write: point time is expired, compared with rp duration dropped=11"
2 null "null"
1 null "partial write: point time is expired, compared with rp duration dropped=38"
1 null "partial write: point time is expired, compared with rp duration dropped=32"
1 null "partial write: point time is expired, compared with rp duration dropped=31"
1 null "partial write: point time is expired, compared with rp duration dropped=3"
1 null "partial write: point time is expired, compared with rp duration dropped=27"
1 null "partial write: point time is expired, compared with rp duration dropped=24"
1 null "partial write: point time is expired, compared with rp duration dropped=23"
1 null "partial write: point time is expired, compared with rp duration dropped=12"
1 null "partial write: point time is expired, compared with rp duration dropped=10"
参考文献

本文链接: https://www.cnblogs.com/johnnyzen/p/13437352.html
关于博文:评论和私信会在第一时间回复,或直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
日常交流:大数据与软件开发-QQ交流群: 774386015 【入群二维码】参见左下角。您的支持、鼓励是博主技术写作的重要动力!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)