[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 追加内容时,你可以使用 sedaappend)命令。

这个命令允许你在文件的特定行之后追加内容。
如果你想要在文件的末尾追加内容,可以使用 $ 符号来表示文件的最后一行

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 - 阮一峰教程

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 命令用于去除文件中重复出现的行

  • 它可以识别相邻的重复行,并根据需要保留或删除这些重复行。

  • 主要功能

  1. 识别并删除相邻重复行
  • uniq 默认会删除连续的重复行,只保留一个实例。
  1. 保留重复行的第一个实例
  • 如果需要保留第一个重复行,uniq 会保留第一次出现的行,并删除后续的重复行。
  1. 统计重复次数
  • 可以输出每一行及其重复次数。
  1. 忽略指定列的比较
  • 可以指定忽略某些列来进行比较,这对于某些特定的文本处理非常有用。

语法

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

  • 命令介绍
  • commLINUX系统下的一个指令,用来对两个己排序文件进行逐行比较。

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
  1. 提取第二列(年龄):
cut -d ',' -f 2 data.txt

out:

age
30
25
28
  1. 提取第一列(名字)和第三列(性别):
cut -d ',' -f 1,3 data.txt

out:

name,gender
Alice,female
Bob,male
Carol,female

示例: 按字符位置提取

  • 假设有一个文本文件 text.txt,内容如下:
abcdefg
1234567
  1. 提取前 3 个字符:
cut -c 1-3 text.txt

out:

abc
123
  1. 提取第 2 到第 5 个字符:
cut -c 2-5 text.txt

out:

bcd
234

示例: 使用 --complement

  1. 提取除第二列外的其它列:
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
  1. 提取特定字段,忽略没有分隔符的行:
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

示例: 结合其他命令

  • sortuniq 结合:查找文件中各个不同名字的数量。
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"

参考文献

posted @   千千寰宇  阅读(941)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
点击右上角即可分享
微信分享提示