管道处理示意图:

这个管道命令' | '它只能处理通过前一条指令传递的正确信息,即标准输出。
grep 和 cut 理解成两个非常典型的文本处理命令:
grep:按条件筛选“行”
cut:从每一行中截取指定的“列/字符”
grep [选项] "查找内容" 文件假设 test.txt:
root:x:0:0:root:/root:/bin/bash
zhangsan:x:1000:1000:zhangsan:/home/zhangsan:/bin/bash
lisi:x:1001:1001:lisi:/home/lisi:/bin/bash
root123
执行:
grep root test.txt结果:
root:x:0:0:root:/root:/bin/bash
root123
注意:grep 是以行为单位进行匹配的,会把匹配到的行输出。
[root@localhost testdir]# cat test.txt
root:x:0:0:root:/root:/bin/bash
zhangsan:x:1000:1000:zhangsan:/home/zhangsan:/bin/bash
lisi:x:1001:1001:lisi:/home/lisi:/bin/bash
root123
[root@localhost testdir]# grep root test.txt
root:x:0:0:root:/root:/bin/bash
root123
[root@localhost testdir]#
| 选项 | 作用 | 示例 |
|---|---|---|
-i | 忽略大小写 | grep -i root file |
-v | 反向匹配,排除匹配行 | grep -v root file |
-n | 显示行号 | grep -n root file |
-c | 统计匹配行数量 | grep -c root file |
-l | 只显示包含匹配内容的文件名 | grep -l root *.txt |
-L | 显示不包含匹配内容的文件名 | grep -L root *.txt |
-r / -R | 递归搜索目录 | grep -r root /etc |
-w | 按完整单词匹配 | grep -w root file |
-x | 整行完全匹配 | grep -x root file |
-E | 使用扩展正则表达式 | grep -E 'root|admin' file |
-A n | 显示匹配行及后 n 行 | grep -A 2 root file |
-B n | 显示匹配行及前 n 行 | grep -B 2 root file |
-C n | 显示匹配行前后各 n 行 | grep -C 2 root file |
命令演示:
[root@localhost testdir]# cat file1.txt
http://192.1.1.100/myweb
http://192.1.1.100:8080/myweb
http://192.1.1.100:8081/myweb
root
http://192.1.1.100/myweb
http://192.1.1.100:8080/myweb
http://192.1.1.100:8081/myweb
admin
ROOT
[root@localhost testdir]# grep -i root file1.txt #-i不区分大小写
root
ROOT
[root@localhost testdir]# grep -v root file1.txt #-v 取反,没有root的行都输出
http://192.1.1.100/myweb
http://192.1.1.100:8080/myweb
http://192.1.1.100:8081/myweb
http://192.1.1.100/myweb
http://192.1.1.100:8080/myweb
http://192.1.1.100:8081/myweb
admin
ROOT
[root@localhost testdir]# grep -n root file1.txt #显示root所在的行号
4:root
[root@localhost testdir]# cat file1.txt #以下文件中,有root的行是2行,但是root有3个
http://192.1.1.100/myweb
http://192.1.1.100:8080/myweb
http://192.1.1.100:8081/myweb
root 1234 root
http://192.1.1.100/myweb
http://192.1.1.100:8080/myweb
http://192.1.1.100:8081/myweb
admin
ROOT
root abcdefg
[root@localhost testdir]# grep -c root file1.txt #-c选项,是有root的行,有几行,虽然有3个root,因为在一行中的多个匹配,只算1个,所以结果输出2, 因为匹配的行数是2,两次说明grep是按行筛选&匹配。
2
[root@localhost testdir]# grep root *.txt #在当前目录下,包括root的文件有file1.txt和test.txt
file1.txt:root 1234 root
file1.txt:root abcdefg
test.txt:root:x:0:0:root:/root:/bin/bash
test.txt:root123
[root@localhost testdir]# grep -l root *.txt #-l选项列出,包含root的文件
file1.txt
test.txt
[root@localhost testdir]# grep -r root . #在当前目录及其子目录中找包含root的文件,当前目录中test.txt 和file1.txt包含root,当前目录中的test目录中的test.txt,包含root,所以结果如下,显示出了包含root的文件及对应的行
./test/file1.txt:root
./test.txt:root:x:0:0:root:/root:/bin/bash
./test.txt:root123
./file1.txt:root 1234 root
./file1.txt:root abcdefg
[root@localhost testdir]#
[root@localhost testdir]# cat file1.txt
http://192.1.1.100/myweb
http://192.1.1.100:8080/myweb
http://192.1.1.100:8081/myweb
root 1234 root
http://192.1.1.100/myweb
http://192.1.1.100:8080/myweb
http://192.1.1.100:8081/myweb
admin
ROOT
root abcdefg
root abcdefg 12346#grep -x "root abcdefg" file1.txt 这行不会匹配
[root@localhost testdir]# grep -x "root abcdefg" file1.txt #-x整行匹配
root abcdefg
------------------------------------------------------------------------------------------------------
[root@localhost testdir]# cat file1.txt
server {
listen 80;
server_name yourdomain.com;
root /var/www/html;
index index.html;
location / {
auth_basic "Restricted Access"; # 设置认证提示信息
auth_basic_user_file /etc/nginx/.htpasswd; # 指定用户认证文件
}
}
keyword
http://192.1.1.100/myweb
http://192.1.1.100:8080/myweb
http://192.1.1.100:8081/myweb
admin
[root@localhost testdir]# grep -A 4 keyword file1.txt #显示testfile匹配行,及后面4行内容
keyword
http://192.1.1.100/myweb
http://192.1.1.100:8080/myweb
http://192.1.1.100:8081/myweb
admin
[root@localhost testdir]# grep -B 12 keyword file1.txt #显示testfile匹配行,及前面12行内容
server {
listen 80;
server_name yourdomain.com;
root /var/www/html;
index index.html;
location / {
auth_basic "Restricted Access"; # 设置认证提示信息
auth_basic_user_file /etc/nginx/.htpasswd; # 指定用户认证文件
}
}
keyword
[root@localhost testdir]#
[root@localhost testdir]# grep -C 3 testfile file1.txt ##显示testfile匹配行,及前后3行内容
auth_basic_user_file /etc/nginx/.htpasswd; # 指定用户认证文件
}
}
testfile
http://192.1.1.100/myweb
http://192.1.1.100:8080/myweb
http://192.1.1.100:8081/myweb
[root@localhost testdir]#
在 grep -w 中,整词匹配的分隔符(即“词”的边界)是由 非单词字符(non-word characters) 来界定的。
具体规则如下:
1. 词的组成成分(什么是“单词字符”)grep -w 认为“词”由以下字符连续组成(即 [[:alnum:]_]):
字母(a-z, A-Z)
数字(0-9)
下划线(_)
2. 词的边界(什么是“分隔符”)只要匹配的文本前后紧挨着的是非单词字符,或者是行的开头/结尾,就视为“整词”。
非单词字符包括但不限于:
空格、制表符(\t)
标点符号(,, ., ;, : 等)
括号、方括号、花括号((), [], {})
数学符号(+, -, =, /, * 等)
引号(', ")
[root@localhost testdir]# cat test.txt
root:x:0:0:root:/root:/bin/bash
zhangsan:x:1000:1000:zhangsan:/home/zhangsan:/bin/bash
lisi:x:1001:1001:lisi:/home/lisi:/bin/bash
root123 #不会匹配
root_123 #不会匹配
rootabc #不会匹配
root,123 #会匹配
root.123 #会匹配
root;123 #会匹配
root(123 #会匹配
root[123 #会匹配
root{123 #会匹配
root+123#会匹配
root-123 #会匹配
root=123#会匹配
root/123 #会匹配
root*123 #会匹配
root'123 #会匹配
root"123 #会匹配
root:123 #会匹配
[root@localhost testdir]# grep -w root test.txt
root:x:0:0:root:/root:/bin/bash
root,123 #会匹配
root.123 #会匹配
root;123 #会匹配
root(123 #会匹配
root[123 #会匹配
root{123 #会匹配
root+123#会匹配
root-123 #会匹配
root=123#会匹配
root/123 #会匹配
root*123 #会匹配
root'123 #会匹配
root"123 #会匹配
root:123 #会匹配
[root@localhost testdir]#
在 grep -E(以及大多数 Linux 命令)中,单引号(') 和 双引号(") 的区别在于 Shell 是否会对引号内的内容进行变量替换和转义解释。
核心原则是:
单引号:强引用。里面的所有字符(包括 $、\、``` 等)都视为普通字面量,Shell 完全不处理。
双引号:弱引用。里面的 $(变量)、``(反引号命令替换)、`(部分转义)会被 Shell 先解析展开,然后再传给 grep。
[root@localhost testdir]# echo $HOME
/root
[root@localhost testdir]# cat file1.txt
http://192.1.1.100/myweb
http://192.1.1.100:8080/myweb
http://192.1.1.100:8081/myweb
root 1234 root
http://192.1.1.100/myweb
http://192.1.1.100:8080/myweb
http://192.1.1.100:8081/myweb
admin
ROOT
root abcdefg
/root abcdefg 12346
[root@localhost testdir]# grep -E "$HOME|admin" file1.txt
admin
/root abcdefg 12346
[root@localhost testdir]# grep -E '$HOME|admin' file1.txt
admin
[root@localhost testdir]#
[root@localhost testdir]# grep -E "root|admin" file1.txt
root 1234 root
admin
root abcdefg
/root abcdefg 12346
^在正则中表示行首,$表示行尾,^$表示从行首至行尾为空,也就是空行。
| 案例 | 命令示例 |
|---|---|
| 查找进程 | ps -ef |grep nginx |
| 排除grep自己 | ps -ef |
| 查看ssh配置 | grep -n "PermitRootLogin" /etc/ssh/sshd_config |
| 查找日志中的error不区分大小写 | grep -i error /var/log/messages |
| 排除注释行 | grep -v "^#" /etc/fstab |
| 排除注释行和空行 | grep -vE "^#|^$" /etc/fstab |
命令演示:
[root@localhost testdir]# ps -ef | grep sshd
root 9311010:37 ? 00:00:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
root 2303931010:43 ? 00:00:00 sshd: root [priv]
root 2305931010:43 ? 00:00:00 sshd: root [priv]
root 23382303010:43 ? 00:00:21 sshd: root@pts/0
root 23412305010:43 ? 00:00:00 sshd: root@notty
root 853192344014:37 pts/0 00:00:00 grep--color=auto sshd
[root@localhost testdir]# ps -ef | grep sshd | grep -v grep
root 9311010:37 ? 00:00:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
root 2303931010:43 ? 00:00:00 sshd: root [priv]
root 2305931010:43 ? 00:00:00 sshd: root [priv]
root 23382303010:43 ? 00:00:21 sshd: root@pts/0
root 23412305010:43 ? 00:00:00 sshd: root@notty
[root@localhost testdir]# grep -n "^PermitRootLogin" /etc/ssh/sshd_config
[root@localhost testdir]# grep -n "PermitRootLogin" /etc/ssh/sshd_config
40:#PermitRootLogin prohibit-password
90:# the setting of "PermitRootLogin without-password".
[root@localhost testdir]# grep -i error /var/log/messages | more
Aug 1311:23:45 localhost kernel: unchecked MSR access error: RDMSR from 0x852 at rIP: 0xffffffff9fc74ff2 (native_apic_msr_read+0x12/0x50)
Aug 1311:24:14 localhost mcelog[843]: mcelog: ERROR: AMD Processor family 23: mcelog does not support this processor. Please use the edac_mce_amd module instead.
Aug 1311:24:15 localhost alsactl[886]: alsa-lib main.c:1554:(snd_use_case_mgr_open) error: failed to import hw:0 use case configuration -2
Aug 1311:24:26 localhost /usr/sbin/irqbalance[841]: Cannot change IRQ 0 affinity: Input/output error
Aug 1311:24:43 localhost /usr/libexec/gdm-x-session[1575]: #011(WW) warning, (EE) error, (NI) not implemented, (??) unknown.
Aug 1311:24:47 localhost containerd[939]: time="2026-08-13T11:24:47.214698138+08:00"level=info msg="skip loading plugin \"io.containerd.snapshotter.v1.aufs\"..."error="aufs is n
ot supported (modprobe aufs failed: exit status 1 \"modprobe: FATAL: Module aufs not found in directory /lib/modules/5.14.0-432.el9.x86_64\\n\"): skip plugin" type=io.containerd.sn
apshotter.v1
[root@localhost testdir]# cat /etc/fstab
#
# /etc/fstab
# Created by anaconda on Mon Apr 1 13:31:30 2024
#
# Accessible filesystems, by reference, are maintained under '/dev/disk/'.
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info.
#
# After editing this file, run 'systemctl daemon-reload' to update systemd
# units generated from this file.
#
/dev/mapper/cs-root / xfs defaults 00
UUID=11974649-6ba4-4ef2-87c4-1c641a3ee480 /boot xfs defaults 00
/dev/mapper/cs-swap none swap defaults 00
/dev/mapper/vg--data-lv--data /data ext4 defaults 00
[root@localhost testdir]# grep -v "^#" /etc/fstab
/dev/mapper/cs-root / xfs defaults 00
UUID=11974649-6ba4-4ef2-87c4-1c641a3ee480 /boot xfs defaults 00
/dev/mapper/cs-swap none swap defaults 00
/dev/mapper/vg--data-lv--data /data ext4 defaults 00
[root@localhost testdir]# grep -vE "^#|^$" /etc/fstab
/dev/mapper/cs-root / xfs defaults 00
UUID=11974649-6ba4-4ef2-87c4-1c641a3ee480 /boot xfs defaults 00
/dev/mapper/cs-swap none swap defaults 00
/dev/mapper/vg--data-lv--data /data ext4 defaults 00
cut是”截取字段”。
基本语法:
cut [选项] 文件最常用的是:
cut -d '分隔符' -f 字段编号 文件| 选项 | 作用 | 示例 |
|---|---|---|
-d | 指定字段分隔符 | cut -d: -f1 file |
-f | 指定截取哪个字段 | cut -d: -f1 file |
-c | 按字符位置截取 | cut -c1-5 file |
-b | 按字节位置截取 | cut -b1-5 file |
--complement | 反向选择 | cut -d: -f1 --complement file |
-d 和 -f 一定要结合理解例如:
cut -d: -f1 /etc/passwd可以拆成:
-d:
↓
使用 : 作为分隔符
-f1
↓
取第1个字段
原始数据:
root:x:0:0:root:/root:/bin/bash切割:
root
x
0
0
root
/root
/bin/bash
然后:
第1字段 = root
第2字段 = x
第3字段 = 0
第4字段 = 0
第5字段 = root
第6字段 = /root
第7字段 = /bin/bash
这才是 Linux 运维中非常常见的用法。
例如:
grep root /etc/passwd | cut -d: -f1流程:
/etc/passwd
│
▼
grep root
│
│ 筛选包含 root 的行
▼
root:x:0:0:root:/root:/bin/bash
│
▼
cut -d: -f1
│
▼
root
也就是说:grep 负责找行,cut 负责从找到的行中取字段。
命令演示:
[root@localhost testdir]# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:65534:65534:Kernel Overflow User:/:/sbin/nologin
systemd-coredump:x:999:997:systemd Core Dumper:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
polkitd:x:998:996:User for polkitd:/:/sbin/nologin
avahi:x:70:70:Avahi mDNS/DNS-SD Stack:/var/run/avahi-daemon:/sbin/nologin
rtkit:x:172:172:RealtimeKit:/proc:/sbin/nologin
libstoragemgmt:x:993:993:daemon account for libstoragemgmt:/:/usr/sbin/nologin
geoclue:x:992:992:User for geoclue:/var/lib/geoclue:/sbin/nologin
tss:x:59:59:Account used for TPM access:/:/sbin/nologin
cockpit-ws:x:991:991:User for cockpit web service:/nonexisting:/sbin/nologin
cockpit-wsinstance:x:990:990:User for cockpit-ws instances:/nonexisting:/sbin/nologin
colord:x:989:989:User for colord:/var/lib/colord:/sbin/nologin
sssd:x:988:988:User for sssd:/:/sbin/nologin
clevis:x:987:987:Clevis Decryption Framework unprivileged user:/var/cache/clevis:/usr/sbin/nologin
setroubleshoot:x:986:986:SELinux troubleshoot server:/var/lib/setroubleshoot:/usr/sbin/nologin
pipewire:x:985:985:PipeWire System Daemon:/run/pipewire:/usr/sbin/nologin
flatpak:x:984:984:User for flatpak system helper:/:/sbin/nologin
gdm:x:42:42::/var/lib/gdm:/sbin/nologin
gnome-initial-setup:x:983:982::/run/gnome-initial-setup/:/sbin/nologin
sshd:x:74:74:Privilege-separated SSH:/usr/share/empty.sshd:/usr/sbin/nologin
chrony:x:982:981:chrony system user:/var/lib/chrony:/sbin/nologin
dnsmasq:x:981:980:Dnsmasq DHCP and DNS server:/var/lib/dnsmasq:/usr/sbin/nologin
tcpdump:x:72:72::/:/sbin/nologin
zhaohong:x:1000:1000:zhaohong:/home/zhaohong:/bin/bash
xrdp:x:980:978:Unprivileged xrdp user:/run/xrdp:/sbin/nologin
unbound:x:979:977:Unbound DNS resolver:/var/lib/unbound:/sbin/nologin
qemu:x:107:107:qemu user:/:/sbin/nologin
apache:x:48:48:Apache:/usr/share/httpd:/sbin/nologin
test:x:1001:1001::/home/test:/bin/bash
user1:x:1002:1002::/home/user1:/bin/bash
zhangsan:x:1003:1003::/home/zhangsan:/bin/bash
lisi:x:1004:1004::/home/lisi:/bin/bash
[root@localhost testdir]# cut -d: -f1 /etc/passwd
root
bin
daemon
adm
lp
sync
shutdown
halt
operator
games
ftp
nobody
systemd-coredump
dbus
polkitd
avahi
rtkit
libstoragemgmt
geoclue
tss
cockpit-ws
cockpit-wsinstance
colord
sssd
clevis
setroubleshoot
pipewire
flatpak
gdm
gnome-initial-setup
sshd
chrony
dnsmasq
tcpdump
zhaohong
xrdp
unbound
qemu
apache
test
user1
zhangsan
lisi
[root@localhost testdir]# cut -d: -f1,3 /etc/passwd #截取第1列和第3列
root:0
bin:1
daemon:2
adm:3
lp:4
sync:5
shutdown:6
halt:7
mail:8
operator:11
games:12
ftp:14
nobody:65534
systemd-coredump:999
dbus:81
polkitd:998
avahi:70
rtkit:172
libstoragemgmt:993
geoclue:992
tss:59
cockpit-ws:991
cockpit-wsinstance:990
colord:989
sssd:988
clevis:987
setroubleshoot:986
pipewire:985
flatpak:984
gdm:42
gnome-initial-setup:983
sshd:74
chrony:982
dnsmasq:981
tcpdump:72
zhaohong:1000
xrdp:980
unbound:979
qemu:107
apache:48
test:1001
user1:1002
zhangsan:1003
lisi:1004
[root@localhost testdir]# cut -d: -f1-3 /etc/passwd #截取第1列至第3列
root:x:0
bin:x:1
daemon:x:2
adm:x:3
lp:x:4
sync:x:5
shutdown:x:6
halt:x:7
mail:x:8
operator:x:11
games:x:12
ftp:x:14
nobody:x:65534
systemd-coredump:x:999
dbus:x:81
polkitd:x:998
avahi:x:70
rtkit:x:172
libstoragemgmt:x:993
geoclue:x:992
tss:x:59
cockpit-ws:x:991
cockpit-wsinstance:x:990
colord:x:989
sssd:x:988
clevis:x:987
setroubleshoot:x:986
pipewire:x:985
flatpak:x:984
gdm:x:42
gnome-initial-setup:x:983
sshd:x:74
chrony:x:982
dnsmasq:x:981
tcpdump:x:72
zhaohong:x:1000
xrdp:x:980
unbound:x:979
qemu:x:107
apache:x:48
test:x:1001
user1:x:1002
zhangsan:x:1003
lisi:x:1004
[root@localhost testdir]# echo "abcdefg" | cut -c1-3
abc
[root@localhost testdir]# echo "abcdefg" | cut -c4-6
def
[root@localhost testdir]# grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin
[root@localhost testdir]# grep root /etc/passwd | cut -d: -f1
root
operator
[root@localhost testdir]# grep root /etc/passwd | cut -d: --complement -f1
x:0:0:root:/root:/bin/bash
x:11:0:operator:/root:/sbin/nologin
是对文本内容进行排序。
[root@localhost testdir]# cat test.txt
banana
apple
orange
pear
[root@localhost testdir]# sort test.txt
apple
banana
orange
pear
| 选项 | 作用 | 示例 |
|---|---|---|
-r | 反向排序 | sort -r file |
-n | 按数值大小排序 | sort -n file |
-V | 按版本号规则排序 | sort -V file |
-k | 按指定字段排序 | sort -k2 file |
-t | 指定字段分隔符 | sort -t: -k3 -n file |
-u | 排序时去除重复行 | sort -u file |
-f | 忽略大小写 | sort -f file |
-o | 将排序结果写入指定文件 | sort file -o result.txt |
-h | 按人类可读数字排序 | sort -h file |
-b | 忽略前导空白 | sort -b file |
-M | 按月份名称排序 | sort -M file |
-R | 按哈希顺序排序 | sort -R file |
[root@localhost testdir]# cat test.txt
banana
apple
orange
pear
100
20
10
2
1
11
25
[root@localhost testdir]# sort test.txt #sort默认是按字符顺序排序,而不是数学上数字大小。
1
10
100
11
2
20
25
apple
banana
orange
pear
[root@localhost testdir]# sort -r test.txt #-r反射排序
pear
orange
banana
apple
25
20
2
11
100
10
1
[root@localhost testdir]# sort -rn test.txt #反向+数字
100
25
20
11
10
2
1
pear
orange
banana
apple
[root@localhost testdir]# sort -n test.txt #按数字大小排序
apple
banana
orange
pear
1
2
10
11
20
25
100
[root@localhost testdir]#
[root@localhost testdir]# cat test1.txt
zhangsan 90
lisi 80
wangwu 100
zhaoliu 70
[root@localhost testdir]# sort -k2 -n test1.txt
zhaoliu 70
lisi 80
zhangsan 90
wangwu 100
现在有一个csv的文件,内容如下,想要IP地址这一列,进行按大小排序:
[root@localhost testdir]# cat server.csv
▒▒▒▒▒▒,BMC IP▒▒ַ,▒ڵ▒▒▒▒▒,CPU▒▒▒▒,״̬
server01,192.168.100.182,▒▒▒▒ڵ▒,x86_64,▒▒
server02,192.168.100.40,▒▒▒▒ڵ▒,aarch64,▒▒
server03,192.168.100.118,▒▒▒▒ڵ▒,x86_64,▒▒
server04,192.168.100.50,▒▒▒▒ڵ▒,x86_64,▒▒
server06,192.168.100.76,▒▒▒▒ڵ▒,x86_64,▒▒
server07,192.168.100.134,▒▒▒▒ڵ▒,x86_64,▒▒
server08,192.168.100.124,▒▒▒▒ڵ▒,aarch64,▒▒
server09,192.168.100.181,▒▒▒▒ڵ▒,x86_64,▒▒
server10,192.168.100.128,▒▒▒▒ڵ▒,aarch64,▒▒
server11,192.168.100.184,▒▒▒▒ڵ▒,x86_64,▒▒
server12,192.168.100.35,▒▒▒▒ڵ▒,x86_64,▒▒
server13,192.168.100.93,▒▒▒▒ڵ▒,x86_64,▒▒
server14,192.168.100.159,▒▒▒▒ڵ▒,x86_64,▒▒
server15,192.168.100.71,▒▒▒▒ڵ▒,aarch64,▒▒
server16,192.168.100.32,▒▒▒▒ڵ▒,aarch64,▒▒
server17,192.168.100.11,▒▒▒▒ڵ▒,x86_64,▒▒
server18,192.168.100.46,▒▒▒▒ڵ▒,x86_64,▒▒
server19,192.168.100.60,▒▒▒▒ڵ▒,aarch64,▒▒
server20,192.168.100.163,▒▒▒▒ڵ▒,x86_64,▒▒
[root@localhost testdir]# file -bi server.csv
application/csv; charset=iso-8859-1
[root@localhost testdir]# iconv -f GBK -t UTF-8 server.csv > server_uft8.csv #解决中文乱码问题
[root@localhost testdir]# cat server_uft8.csv
主机名称,BMC IP地址,节点类型,CPU类型,状态
server01,192.168.100.182,计算节点,x86_64,正常
server02,192.168.100.40,计算节点,aarch64,正常
server03,192.168.100.118,计算节点,x86_64,正常
server04,192.168.100.50,计算节点,x86_64,正常
server06,192.168.100.76,计算节点,x86_64,正常
server07,192.168.100.134,计算节点,x86_64,正常
server08,192.168.100.124,计算节点,aarch64,正常
server09,192.168.100.181,计算节点,x86_64,正常
server10,192.168.100.128,计算节点,aarch64,正常
server11,192.168.100.184,计算节点,x86_64,正常
server12,192.168.100.35,计算节点,x86_64,正常
server13,192.168.100.93,计算节点,x86_64,正常
server14,192.168.100.159,计算节点,x86_64,正常
server15,192.168.100.71,计算节点,aarch64,正常
server16,192.168.100.32,计算节点,aarch64,正常
server17,192.168.100.11,计算节点,x86_64,正常
server18,192.168.100.46,计算节点,x86_64,正常
server19,192.168.100.60,计算节点,aarch64,正常
server20,192.168.100.163,计算节点,x86_64,正常
[root@localhost testdir]#
[root@localhost testdir]# sort -t',' -k2,2V server_uft8.csv > server_uft8_order.csv
[root@localhost testdir]# cat server_uft8_order.csv
server17,192.168.100.11,计算节点,x86_64,正常
server16,192.168.100.32,计算节点,aarch64,正常
server12,192.168.100.35,计算节点,x86_64,正常
server02,192.168.100.40,计算节点,aarch64,正常
server18,192.168.100.46,计算节点,x86_64,正常
server04,192.168.100.50,计算节点,x86_64,正常
server19,192.168.100.60,计算节点,aarch64,正常
server15,192.168.100.71,计算节点,aarch64,正常
server06,192.168.100.76,计算节点,x86_64,正常
server13,192.168.100.93,计算节点,x86_64,正常
server03,192.168.100.118,计算节点,x86_64,正常
server08,192.168.100.124,计算节点,aarch64,正常
server10,192.168.100.128,计算节点,aarch64,正常
server07,192.168.100.134,计算节点,x86_64,正常
server14,192.168.100.159,计算节点,x86_64,正常
server20,192.168.100.163,计算节点,x86_64,正常
server09,192.168.100.181,计算节点,x86_64,正常
server01,192.168.100.182,计算节点,x86_64,正常
server11,192.168.100.184,计算节点,x86_64,正常
主机名称,BMC IP地址,节点类型,CPU类型,状态
sort -t',' -k2,2V server_uft8.csv > server_uft8_order.csv 命令解释:
sort -t',' -k2,2V server_utf8.csv > server_uft8_order.csv
│ │ │ │ |
│ │ │ └── 要排序的文件 └── 将结果重定向到新文件
│ │ └── 按第2列排序,并使用版本排序
│ └── 指定字段分隔符为逗号
└── 排序命令
-k2,2V 意思是按照第2列进行排序, 只使用第2列作为排序关键字。
-k2,2
│ │
│ └── 排序字段结束于第2列
└── 从第2列开始
-V :表壳版本号排序version sort,也就是使用sort -V的方式比较字符串中的数字。
[root@localhost testdir]# cat c.txt
1.10
1.2
1.20
1.3
[root@localhost testdir]# sort -n c.txt
1.10
1.2
1.20
1.3
[root@localhost testdir]# sort -V c.txt
1.2
1.3
1.10
1.20
[root@localhost testdir]#
uniq用于去除相邻的重复行。
[root@localhost testdir]# cat a.txt
apple
apple
banana
banana
banana
orange
[root@localhost testdir]# uniq a.txt
apple
banana
orange
[root@localhost testdir]# cat b.txt
apple
banana
apple
banana
[root@localhost testdir]# uniq b.txt
apple
banana
apple
banana
[root@localhost testdir]# sort b.txt | uniq #经常这样用,先排序然后再去重
apple
banana
[root@localhost testdir]#
| 选项 | 作用 | 示例 |
|---|---|---|
-c | 统计每组重复次数 | uniq -c file |
-d | 只显示重复的行 | uniq -d file |
-u | 只显示不重复的行 | uniq -u file |
-i | 忽略大小写 | uniq -i file |
-f n | 忽略前 n 个字段 | uniq -f 1 file |
-s n | 忽略前 n 个字符 | uniq -s 2 file |
[root@localhost testdir]# cat a.txt
apple
apple
banana
banana
banana
orange
[root@localhost testdir]# uniq -c a.txt
2 apple
3 banana
1 orange
[root@localhost testdir]# cat b.txt
apple
banana
apple
banana
apple
[root@localhost testdir]# sort b.txt | uniq -c
3 apple
2 banana
[root@localhost testdir]# sort b.txt | uniq -c | sort -hr #从多到少排序
3 apple
2 banana
[root@localhost testdir]#
wc word count,用于统计文本信息。
例如:
wc test.txt可能得到:
10 30 200 test.txt分别代表:
10 → 行数
30 → 单词数
200 → 字节数
| 选项 | 作用 | 示例 |
|---|---|---|
-l | 统计行数 | wc -l file |
-w | 统计单词数 | wc -w file |
-c | 统计字节数 | wc -c file |
-m | 统计字符数 | wc -m file |
-L | 最长行的长度 | wc -L file |
在 UTF-8 中文环境下:
一个英文字符 → 通常 1 字节
一个中文字符 → 通常 3 字节
[root@localhost testdir]# cat b.txt
apple
banana
apple
banana
apple
[root@localhost testdir]# wc b.txt
5532 b.txt
[root@localhost testdir]# wc -l b.txt
5 b.txt
[root@localhost testdir]# wc -c b.txt
32 b.txt
[root@localhost testdir]# wc -w b.txt
5 b.txt
[root@localhost testdir]# wc -m b.txt
32 b.txt
[root@localhost testdir]#
[root@localhost testdir]# wc -L b.txt
6 b.txt
[root@localhost testdir]#
例如统计日志中访问最多的 IP。
假设日志:
192.168.1.10 GET /index.html
192.168.1.20 GET /login
192.168.1.10 GET /test
192.168.1.30 GET /index.html
192.168.1.10 GET /login
首先:
awk '{print $1}' access.log提取 IP:
192.168.1.10
192.168.1.20
192.168.1.10
192.168.1.30
192.168.1.10
然后:
awk '{print $1}' access.log | sort | uniq -c得到:
3 192.168.1.10
1 192.168.1.20
1 192.168.1.30
再按照次数从大到小:
awk '{print $1}' access.log | sort | uniq -c | sort -nr这就是一个非常典型的:
awk → sort → uniq → sort日志分析流水线。
[root@localhost testdir]# cat access.log
192.168.1.10 GET /index.html
192.168.1.20 GET /login
192.168.1.10 GET /test
192.168.1.30 GET /index.html
192.168.1.10 GET /login
[root@localhost testdir]# awk '{print $1}' access.log
192.168.1.10
192.168.1.20
192.168.1.10
192.168.1.30
192.168.1.10
[root@localhost testdir]# awk '{print $1}' access.log | sort
192.168.1.10
192.168.1.10
192.168.1.10
192.168.1.20
192.168.1.30
[root@localhost testdir]# awk '{print $1}' access.log | sort | uniq -c
3192.168.1.10
1192.168.1.20
1192.168.1.30
[root@localhost testdir]# awk '{print $1}' access.log | sort | uniq -c | sort -nr
3192.168.1.10
1192.168.1.30
1192.168.1.20
如果使用 tee:
[root@localhost testdir]# echo hello | tee test.txt
hello
[root@localhost testdir]# cat test.txt
hello
数据流变成:
┌──→ 屏幕
│
echo → tee ──┤
│
└──→ test.txt
所以: echo "hello" | tee test.txt 会将hello输出到屏幕并同时写入文件test.txt。
┌──→ 标准输出 stdout → 屏幕
│
stdin → tee ─┤
│
└──→ 文件
tee就像一个三通管,它把一份输入数据“分叉”成两份。
| 选项 | 作用 | 示例 |
|---|---|---|
-a | 追加到文件末尾 | tee -a test.txt |
-i | 忽略中断信号 SIGINT | tee -i test.txt |
例如:
ps -ef | tee process.txt | grep nginx这个命令非常值得理解。
数据流:
┌──────────────→ process.txt
│
ps -ef → tee ────┤
│
└──→ grep nginx → 屏幕
也就是说:
ps -ef
↓
tee
├──→ process.txt
│
└──→ grep nginx
↓
屏幕
这样你既可以:
把完整的 ps -ef 保存下来;
又可以继续把数据传给 grep nginx。
[root@localhost testdir]# ps -ef | tee process.txt | grep sshd
root 9381011:09 ? 00:00:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
root 2694938015:03 ? 00:00:00 sshd: root [priv]
root 2697938015:03 ? 00:00:00 sshd: root [priv]
root 27352697015:03 ? 00:00:00 sshd: root@notty
root 27362694015:03 ? 00:00:13 sshd: root@pts/0
root 504262741017:18 pts/0 00:00:00 grep--color=auto sshd
[root@localhost testdir]# cat process.txt
UID PID PPID C STIME TTY TIME CMD
root 10011:08 ? 00:00:16 /usr/lib/systemd/systemd rhgb --switched-root--system--deserialize31
root 20011:08 ? 00:00:00 [kthreadd]
root 32011:08 ? 00:00:00 [rcu_gp]
root 42011:08 ? 00:00:00 [rcu_par_gp]
root 52011:08 ? 00:00:00 [slub_flushwq]
root 62011:08 ? 00:00:00 [netns]
root 82011:08 ? 00:00:00 [kworker/0:0H-events_highpri]
root 102011:08 ? 00:00:00 [mm_percpu_wq]
root 122011:08 ? 00:00:00 [rcu_tasks_kthre]
root 132011:08 ? 00:00:00 [rcu_tasks_rude_]
root 142011:08 ? 00:00:00 [rcu_tasks_trace]
root 152011:08 ? 00:00:00 [ksoftirqd/0]
root 162011:08 ? 00:00:00 [pr/tty0]
root 172011:08 ? 00:00:00 [rcu_preempt]
root 182011:08 ? 00:00:00 [migration/0]
root 192011:08 ? 00:00:00 [idle_inject/0]
root 212011:08 ? 00:00:00 [cpuhp/0]
root 222011:08 ? 00:00:00 [cpuhp/1]
root 232011:08 ? 00:00:00 [idle_inject/1]
root 242011:08 ? 00:00:00 [migration/1]
root 252011:08 ? 00:00:01 [ksoftirqd/1]
root 272011:08 ? 00:00:00 [kworker/1:0H-events_highpri]
root 282011:08 ? 00:00:00 [cpuhp/2]
root 292011:08 ? 00:00:00 [idle_inject/2]
root 302011:08 ? 00:00:00 [migration/2]
root 312011:08 ? 00:00:00 [ksoftirqd/2]
root 332011:08 ? 00:00:00 [kworker/2:0H-events_highpri]
root 342011:08 ? 00:00:00 [cpuhp/3]
root 352011:08 ? 00:00:00 [idle_inject/3]
…… 很多内容
| 案例 | 命令 |
|---|---|
| 一边实时查看日志,一边保存日志 | tail -f /var/log/messages |
| 实时看到安装过程,同时留下日志 | ./install.sh |tee install.log |
| tee和sudo配合 | echo "hello" |sudo tee /etc/test.conf |
tr = translate,转换或删除字符。它主要处理的是字符,而 grep 主要是筛选行,cut 主要是提取字段。
| 功能 | 选项 | 作用 |
|---|---|---|
| 字符替换 | 无 | SET1 → SET2 |
| 删除字符 | -d | 删除指定字符 |
| 压缩重复字符 | -s | 连续重复字符压缩成一个 |
| 删除后替换 | -c / -C | 对指定字符集取反 |
tr [选项] SET1 [SET2]#小写转大写
----------------------------------------------------------------
[root@localhost testdir]# echo "hello linux" | tr 'a-z' 'A-Z'
HELLO LINUX
#大写转小写
----------------------------------------------------------------
[root@localhost testdir]# echo "HELLO LINUX" | tr 'A-Z' 'a-z'
hello linux
#字符一一替换
a → 1
b → 2
c → 3
----------------------------------------------------------------
[root@localhost testdir]# echo "abcdabc" | tr 'abc' '123'
123d123
[root@localhost testdir]# echo "abcdabcdab" | tr 'abc' '123'
123d123d12
[root@localhost testdir]# echo "abcdabcdab" | tr 'abc' '12'
122d122d12
----------------------------------------------------------------------
#`echo "abcdabcdab" | tr 'abc' '12'` 命令说明:
tr 'abc''12' 表示将第一组(abc)中的字符,替换为第二组(12)中相同位置的字符
对应关系:
a → 1(第1个位置)
b → 2(第2个位置)
c → ?第二组只有2个字符,没有第3个位置
关键点:当第二组比第一组短时,tr 会重复使用第二组的最后一个字符来补足。
所以实际的映射是:
a → 1
b → 2
c → 2(因为第二组不够,重复最后一个字符2)
-----------------------------------------------------------------------
#删除数字
-----------------------------------------------------------------------
[root@localhost testdir]# echo "abc123def456" | tr -d '0-9'
abcdef
#删除小写字母
-----------------------------------------------------------------------
[root@localhost testdir]# echo "abc123DEF" | tr -d 'a-z'
123DEF
[root@localhost testdir]# cat test.txt
hello
linux
world
#删除换行符,\n 代表换行符
-----------------------------------------------------------------------
[root@localhost testdir]# cat test.txt | tr -d '\n'
hellolinuxworld[root@localhost testdir]#
#删除空格
-----------------------------------------------------------------------
[root@localhost testdir]# echo "hello world" | tr -d ' '
helloworld
#删除制表符Tab,^I 代表制表符
-----------------------------------------------------------------------
[root@localhost testdir]# cat test.txt
hello
linux
world 123
[root@localhost testdir]# cat -A test.txt
hello$
linux$
world^I123$
[root@localhost testdir]# cat test.txt | tr -d '\t'
hello
linux
world123
cat -A 显示的各种符号含义| 显示的符号 | 实际字符 | ASCII码 | 说明 |
|---|---|---|---|
$ | \n(换行符) | 0x0A | 每行结尾显示,表示换行(LF) |
^I | \t(制表符/Tab) | 0x09 | 水平制表符 |
^M | \r(回车符) | 0x0D | 回车(常见于Windows文件) |
^@ | \0(空字符) | 0x00 | Null字符 |
^A - ^Z | 控制字符 | 0x01-0x1A | Ctrl+A 到 Ctrl+Z |
^[ | ESC键 | 0x1B | Escape字符 |
^\\ | \ | 0x1C | 文件分隔符(FS) |
^] | ] | 0x1D | 组分隔符(GS) |
^^ | ^ | 0x1E | 记录分隔符(RS) |
^_ | _ | 0x1F | 单元分隔符(US) |
^? | DEL键 | 0x7F | 删除字符 |
M- 前缀 | 高位字符 | 0x80-0xFF | 表示扩展ASCII字符 |
[root@localhost testdir]# echo "hello linux" | tr -s ' ' #多个连续的空格,变成一个空格
hello linux
[root@localhost testdir]# echo "hellllllo" | tr -s 'l' #多个连续的l,变成一个l
helo
例如:
echo "abc123" | tr -cd '0-9'结果:
123[root@localhost testdir]# echo "abc123" | tr -cd '0-9'
123[root@localhost testdir]#
拆开:
-c → complement,取反
-d → delete,删除
'0-9' 表示:数字
-c '0-9' 表示:除数字之外的所有字符
再加:
-d就是:删除所有非数字字符。
因此:
echo "abc123def456" | tr -cd '0-9'得到:
123456命令演示
#提取数字
----------------------------------------------------------------------------------------------
[root@localhost testdir]# echo "CPU使用率:85%" | tr -cd '0-9'
85[root@localhost testdir]#
#提取字母
----------------------------------------------------------------------------------------------
[root@localhost testdir]# echo "abc123DEF456" | tr -cd 'a-zA-Z'
abcDEF[root@localhost testdir]#
[root@localhost testdir]# echo "abc123DEF456" | tr -cd 'a-zA-Z';echo #命令后面加上;echo,结果就会换行
abcDEF
[root@localhost testdir]#
#把空格替换成换行
----------------------------------------------------------------------------------------------
[root@localhost testdir]# echo "hello linux world" | tr ' ' '\n'
hello
linux
world
#把换行嘉伟把你成空格
----------------------------------------------------------------------------------------------
[root@localhost testdir]# cat test.txt
hello
linux
world
[root@localhost testdir]# cat test.txt | tr '\n' ' ' ;echo
hello linux world
[root@localhost testdir]#
[root@localhost testdir]# cat test.txt
hello
linux
world
[root@localhost testdir]# cat test.txt | tr -t 'hello' '12'
12llo
linux
world
col 的全称可以理解为 columnation / column filter,主要作用是:
处理输入文本中的控制字符
↓
转换成适合终端显示的普通文本
控制字符包括:
退格 \b
回车 \r
反向换行
控制字符
| 选项 | 作用 |
|---|---|
-b | 删除退格字符以及退格前面的字符 |
-f | 允许 col 处理反向换行 |
-x | 将制表符转换为空格 |
-l | 缓冲至少一行后再输出 |
[root@localhost testdir]# cat -A test.txt #使用cat -A显示各种符号,^I表示制表符
hello^I123$
linux^Icentos$
world^Imessage$
[root@localhost testdir]# cat test.txt | col -x | cat -A #制表符替换成个空格
hello 123$
linux centos$
world message$
join 的核心就是“根据共同字段关联两个文件”。默认情况下:按照两个文件的第 1 列进行关联。
user.txt score.txt
ID 用户名 ID 成绩
1 zhangsan 1 90
2 lisi 2 80
3 wangwu 3 95
4 zhaoliu 4 88
│ │
└──────── ID ────────────┘
↓
join
↓
ID 用户名 成绩
1 zhangsan 90
2 lisi 80
3 wangwu 95
4 zhaoliu 88
[root@localhost testdir]# cat user.txt
1 zhangsan
2 lisi
3 wangwu
4 zhaoliu
[root@localhost testdir]# cat score.txt
190
280
395
488
[root@localhost testdir]# join user.txt score.txt
1 zhangsan 90
2 lisi 80
3 wangwu 95
4 zhaoliu 88
-----------------------------------------------------------------------------
#如果没有正确排序,则会报错
[root@localhost testdir]# cat user.txt
1 zhangsan
2 lisi
3 wangwu
4 zhaoliu
[root@localhost testdir]# cat score.txt
290
180
395
488
[root@localhost testdir]# join user.txt score.txt
join: score.txt:2:未排序:1 80
2 lisi 90
3 wangwu 95
4 zhaoliu 88
join: 输入没有被正确排序
[root@localhost testdir]#
[root@localhost testdir]# sort -k1,1 score.txt -o score_order.txt #按第1列进行排序,生成新文件
[root@localhost testdir]# cat score_order.txt
180
290
395
488
[root@localhost testdir]# join user.txt score_order.txt
1 zhangsan 80
2 lisi 90
3 wangwu 95
4 zhaoliu 88
[root@localhost testdir]#
这是 join 最重要的选项之一。
-1 N表示:使用文件1的第 N 列进行关联。
-2 N表示:使用文件2的第 N 列进行关联。
user.txt
zhangsan 100
lisi 101
wangwu 102
score.txt
90 100
80 101
95 102
这里:
user.txt 第2列 = ID
score.txt 第2列 = ID
所以:
join -1 2 -2 2 user.txt score.txt结果:
100 zhangsan 90
101 lisi 80
102 wangwu 95
[root@localhost testdir]# cat user.txt
zhangsan 100
lisi 101
wangwu 102
[root@localhost testdir]# cat score.txt
90100
80101
95102
[root@localhost testdir]# join -1 2 -2 2 user.txt score.txt
100 zhangsan 90
101 lisi 80
102 wangwu 95
[root@localhost testdir]#
默认情况下,join 使用空白字符作为字段分隔。
如果文件是 CSV,使用逗号作为分割符
1,zhangsan,100
2,lisi,101
3,wangwu,102
就需要指定:
join -t',' ...[root@localhost testdir]# cat user.csv
1,zhangsan
2,lisi
3,wangwu
[root@localhost testdir]# cat score.csv
1,90
2,80
3,95
[root@localhost testdir]# join -t ',' user.csv score.csv
1,zhangsan,90
2,lisi,80
3,wangwu,95
join 默认输出:
关联字段 + 文件1其他字段 + 文件2其他字段[root@localhost testdir]# join -t ',' -o 1.2,2.2 user.csv score.csv #只输出姓名和成绩
zhangsan,90
lisi,80
wangwu,95
这里:
1.2
│ │
│ └── 文件1第2列
└──── 文件1
2.2
│ │
│ └── 文件2第2列
└──── 文件2
[root@localhost testdir]# cat user.txt
1 zhangsan
2 lisi
3 wangwu
4 zhaoliu
[root@localhost testdir]# cat score.txt
190
280
395
5100
#-a 1 显示文件1中未匹配的
---------------------------------------------------------------------
[root@localhost testdir]# join -a 1 user.txt score.txt
1 zhangsan 90
2 lisi 80
3 wangwu 95
4 zhaoliu
#-a 2 显示文件2中未匹配的
---------------------------------------------------------------------
[root@localhost testdir]# join -a 2 user.txt score.txt
1 zhangsan 90
2 lisi 80
3 wangwu 95
5100
#-a 1 -a 2 两个文件没有匹配的数据都显示
------------------------------------------------------------------
[root@localhost testdir]# join -a 1 -a 2 user.txt score.txt
1 zhangsan 90
2 lisi 80
3 wangwu 95
4 zhaoliu
5100
-e生效的条件,字段存在但为空。
[root@localhost testdir]# cat user.txt
1 zhangsan
2 lisi
3 wangwu
4 zhaoliu
[root@localhost testdir]# vim score.txt
[root@localhost testdir]# cat -A score.txt
190$
280$
395$
4$
5100$
[root@localhost testdir]# join -a 1 -e "无成绩" user.txt score.txt #结果中未匹配时,未补全“无成绩”的原因是,join未指定分隔符时,以空白字符分隔,文件2中4后面没有空格,所以-e不生效,未补全。
1 zhangsan 90
2 lisi 80
3 wangwu 95
4 zhaoliu
[root@localhost testdir]# cat -A score.txt #score.txt 4后面有了空格分割符,后-e生效,补全“无成绩”
190$
280$
395$
4$
5100$
[root@localhost testdir]# join -a 1 -e "无成绩" user.txt score.txt
1 zhangsan 90
2 lisi 80
3 wangwu 95
4 zhaoliu 无成绩
[root@localhost testdir]#
| 选项 | 作用 | 示例 |
|---|---|---|
-1 N | 使用文件1的第N列作为关联字段 | join -1 2 file1 file2 |
-2 N | 使用文件2的第N列作为关联字段 | join -2 2 file1 file2 |
-t CHAR | 指定字段分隔符 | join -t',' file1 file2 |
-o LIST | 指定输出字段 | join -o 1.2,2.2 file1 file2 |
-a 1 | 显示文件1中未匹配的行 | join -a 1 file1 file2 |
-a 2 | 显示文件2中未匹配的行 | join -a 2 file1 file2 |
-e STRING | 未匹配字段使用指定字符串填充 | join -a 1 -e 'NULL' file1 file2 |
-i | 忽略大小写 | join -i file1 file2 |
-v 1 | 只显示文件1中没有匹配的行 | join -v 1 file1 file2 |
-v 2 | 只显示文件2中没有匹配的行 | join -v 2 file1 file2 |
paste:按“行”横向拼接文件。不管内容是否相同,直接按照行的位置拼接。
file1 file2
│ │
│ 第1行 │ 第1行
├─────────────────────┤
│ 第2行 │ 第2行
├─────────────────────┤
│ 第3行 │ 第3行
└─────────────────────┘
↓
paste
↓
横向合并成一行
命令演示
[root@localhost testdir]# cat file1
zhangsan
lisi
wangwu
[root@localhost testdir]# cat file2
90
80
95
[root@localhost testdir]# paste file1 file2
zhangsan 90
lisi 80
wangwu 95
[root@localhost testdir]#
#paste 默认使用 Tab 分隔,^I 代表Tab制表符
[root@localhost testdir]# paste file1 file2 | cat -A
zhangsan^I90$
lisi^I80$
wangwu^I95$
[root@localhost testdir]#
#-d 指定分割符
[root@localhost testdir]# paste -d ',' file1 file2
zhangsan,90
lisi,80
wangwu,95
[root@localhost testdir]# cat file3
Beijing
Shanghai
Guangzhou
#paste可以多个文件一起执行
[root@localhost testdir]# paste -d ',' file1 file2 file3
zhangsan,90,Beijing
lisi,80,Shanghai
wangwu,95,Guangzhou
# -d可以指定多个分割符
[root@localhost testdir]# paste -d ',:' file1 file2 file3
zhangsan,90:Beijing
lisi,80:Shanghai
wangwu,95:Guangzhou
#-s 串行合并,类似于excel中的转置
[root@localhost testdir]# cat file1
zhangsan
lisi
wangwu
[root@localhost testdir]# paste -s -d ',' file1
zhangsan,lisi,wangwu
#paste 可以处理标准输入
[root@localhost testdir]# echo -e "a\nb\nc"
a
b
c
[root@localhost testdir]# echo -e "a\nb\nc" | paste -sd ','
a,b,c
expand:把 Tab 转换成空格。
[root@localhost testdir]# cat file1 #注意zhangsan和20之间是制表符tab,不是空格
zhangsan 20
lisi 18
wangwu 32
[root@localhost testdir]# cat -A file1 #^I 代表制表符
zhangsan^I20$
lisi^I18$
wangwu^I32$
[root@localhost testdir]# expand file1 | cat -A #制表符不见了,变成空格
zhangsan 20$
lisi 18$
wangwu 32$
[root@localhost testdir]#
| 命令/选项 | 作用 |
|---|---|
expand file | 将 Tab 转换为空格 |
expand -t 4 file | 按 4 个字符设置 Tab 停靠位置 |
expand -t 8 file | 按 8 个字符设置 Tab 停靠位置 |
expand -i file | 只转换行首的 Tab |
unexpand file | 将空格转换成 Tab |
有些命令是可以从stdin标准输出读取数据,有些命令是不能从stdin标准输出读取数据,总结如下:
| 命令 | 主要输入方式 | 能否从 stdin 读取数据 | 是否适合放在 | 后面 | 典型用法 |
|---|---|---|---|---|
grep | stdin / 文件 | 是 | 是 | cat file \| grep root |
sort | stdin / 文件 | 是 | 是 | cat file \| sort |
uniq | stdin / 文件 | 是 | 是 | sort file \| uniq |
wc | stdin / 文件 | 是 | 是 | cat file \| wc -l |
head | stdin / 文件 | 是 | 是 | cat file \| head |
tail | stdin / 文件 | 是 | 是 | cat file \| tail |
cut | stdin / 文件 | 是 | 是 | cat file \| cut -d: -f1 |
awk | stdin / 文件 | 是 | 是 | cat file \| awk '{print $1}' |
sed | stdin / 文件 | 是 | 是 | cat file \| sed 's/a/b/' |
tr | stdin | 是 | 是 | cat file \| tr a-z A-Z |
xargs | stdin → 命令参数 | 是 | 是 | cat list \| xargs rm |
ls | 命令行参数 | 否(不以 stdin 为主要输入) | 是(通常作为前一个命令) | ls \| grep txt |
cd | 命令行参数 | 否 | 否 | cd /tmp |
mkdir | 命令行参数 | 否 | 否 | mkdir /data/test |
rm | 命令行参数 | 否(不直接把 stdin 当文件名) | 否 | rm a.txt |
pwd | 无需输入 | 否 | 否 | pwd |
xargs:把标准输入(stdin)中的数据,转换成命令的参数。
[root@localhost test]# ls
file1.txt
[root@localhost test]# ls | rm #rm命令是不能从stdin读取数据当作命令的参数,命令报错
rm: 缺少操作数
请尝试执行 "rm --help" 来获取更多信息。
[root@localhost test]# ls |xargs rm #xargs把stdin转换成rm命令的参数,删除成功
[root@localhost test]# ls
[root@localhost test]#
一定要区分:
管道 |和:
xargs管道
命令A | 命令B表示:
命令A 的 stdout
↓
命令B 的 stdin
xargs
命令A | xargs 命令B表示:
命令A 的 stdout
↓
xargs
↓
转换成 B 的命令行参数
↓
命令B 参数
所以:
|
↓
stdin
xargs
↓
命令参数
这是学习 xargs 最关键的一点。
| 命令组合 | 命令示例 |
|---|---|
| xargs + rm | ls |grep txt |xargs rm |
| xargs + touch | echo "a.txt b.txt c.txt" |xargs touch |
| xargs + mkdir | echo "dir1 dir2 dir3" |xargs mkdir |
| xargs + grep | echo "a.log b.log c.log" |xargs grep "error" 相当于grep "error" a.log b.log c.log |
| xargs + find | find /data -name "*.log" |xargs rm |
| xargs + grep | find /var/log -name "*.log" -print0 |xargs -0 grep "ERROR" |
find
↓
找到很多 .log 文件
↓
stdout
↓
xargs
↓
转换成 rm 的参数
↓
rm 文件1 文件2 文件3 ...
命令演示:
[root@localhost test]# echo "a.txt b.txt c.txt" | xargs touch
[root@localhost test]# ls
a.txt b.txt c.txt
[root@localhost test]# ls | grep txt
a.txt
b.txt
c.txt
[root@localhost test]# ls | grep txt | xargs rm #txt文件已经被删除
[root@localhost test]# ls
[root@localhost test]# echo "dir1 dir2 dir3" |xargs mkdir
[root@localhost test]# ls
dir1 dir2 dir3
[root@localhost test]# echo "1 2 3 4 5" | xargs echo
12345
find → 找
xargs → 传参数
命令 → 执行操作
例如:
find /data -name "*.log" | xargs grep "ERROR"意思:找 /data 下所有 .log 文件,然后在这些文件中搜索 ERROR。
xargs与find一起使用总结
find
↓
负责找文件
grep
↓
筛选内容
xargs
↓
把 stdin 转成命令参数
rm / cp / mv / grep / chmod ...
↓
真正执行操作
| 选项 | 作用 | 示例 |
|---|---|---|
-n N | 每次传递 N 个参数 | xargs -n 2 echo |
-I {} | 指定参数替换字符串,{}就是一个占位符 | xargs -I {} echo {} |
-d CHAR | 指定输入分隔符 | xargs -d ',' |
-0 | 使用 NULL(\0)作为分隔符 | xargs -0 rm |
-r | stdin 没有数据时不执行命令 | xargs -r rm |
-t | 执行前先打印实际执行的命令 | xargs -t rm |
-p | 执行前提示用户确认 | xargs -p rm |
-P N | 并行执行,最多 N 个进程 | xargs -P 4 ... |
命令演示
[root@localhost testdir]# echo "1 2 3 4 5" | xargs -n 2 echo
12
34
5
[root@localhost testdir]# find . -name "*.csv"
./server_uft8_order1.csv
./server.csv
./cps_order.csv
./user.csv
./server_uft8.csv
./score.csv
./server_uft8_order.csv
./cps_utf8.csv
./cps.csv
[root@localhost testdir]# find . -name "*.csv" | xargs -I {} cp {} test #找出文件拷贝至test目录
[root@localhost testdir]# ls test
cps.csv cps_order.csv cps_utf8.csv dir1 dir2 dir3 score.csv server.csv server_uft8.csv server_uft8_order1.csv server_uft8_order.csv user.csv
#文件名中有空格,删除时会报错,因为xargs默认按照空白字符分隔。find中的print0和xargs -0,文件名中的“空格、tab、换行”都不会被错误拆分。重要:find ... -print0 | xargs -0 ...
[root@localhost testdir]# find . -name "123*"
./123 .txt
[root@localhost testdir]# find . -name "123*" | xargs rm
rm: 无法删除 './123': 没有那个文件或目录
[root@localhost testdir]# find . -name "123*" -print0 | xargs -0 rm #删除成功
[root@localhost testdir]#
| 选项 | 作用 | 示例 |
|---|---|---|
-l N | 每 N 行拆分一个文件 | split -l 1000 file |
-b SIZE | 按字节大小拆分 | split -b 100M file |
-C SIZE | 按大小拆分,但尽量不拆断行 | split -C 100M file |
-n N | 将文件拆成 N 份 | split -n 4 file |
-d | 使用数字作为文件后缀 | split -d file |
-a N | 指定文件后缀长度 | split -a 3 file |
-e | 不创建空的最后一个文件 | split -e ... |
--additional-suffix | 给输出文件增加额外后缀 | split --additional-suffix=.log file |
在 Linux 命令中,- 经常具有特殊含义:表示标准输入或标准输出,而不是一个普通文件。
tar -cvf - /home | tar -xvf - -C /tmp/homeback它的核心思想是:不生成中间 tar 文件,直接把 /home 打包的数据通过管道传给另一个 tar,然后解包到 /tmp/homeback。
可以理解成:
/home
│
│ tar 打包
▼
标准输出 stdout
│
│ 管道 |
▼
标准输入 stdin
│
│ tar 解包
▼
/tmp/homeback
tar -cvf - /home逐个解释:
| 参数 | 含义 |
|---|---|
tar | 打包/解包工具 |
-c | create,创建 tar 包 |
-v | verbose,显示处理过程 |
-f | 指定 tar 文件 |
- | 这里表示标准输出 stdout |
/home | 要打包的目录 |
重点是:
-f -这里的 -不是普通的文件名。
它告诉 tar:tar 包不要保存到某个磁盘文件,而是输出到标准输出 stdout。
平时我们可能这样:
tar -cvf home.tar /home意思是:
/home
↓
tar 打包
↓
home.tar
这里:
-f home.tar表示:把 tar 包写入 home.tar。
但是:
tar -cvf - /home变成:
/home
↓
tar 打包
↓
stdout
也就是说:不创建 home.tar 文件,直接把 tar 数据输出到 stdout。
|命令:
tar -cvf - /home | tar -xvf - -C /tmp/homeback中间的:
|表示:前一个命令的标准输出,作为后一个命令的标准输入。
所以:
tar -cvf - /home
│
│ stdout
▼
|
│
│ stdin
▼
tar -xvf -
tar -xvf - -C /tmp/homeback参数:
| 参数 | 含义 |
|---|---|
-x | extract,解包 |
-v | verbose,显示过程 |
-f | 指定 tar 文件 |
- | 这里表示标准输入 stdin |
-C | 指定解包目录 |
/tmp/homeback | 解包目标目录 |
这里最关键的是:
-f -在这里的 - 表示:不要从某个磁盘文件读取 tar 包,而是从标准输入 stdin 读取。
所以两个 - 的含义正好连接起来:
第一个 -
stdout
↓ 管道
第二个 -
stdin
把整个命令画出来:
第一条 tar
tar -cvf - /home
│
│
- = stdout
│
▼
|
管道传输
│
▼
- = stdin
│
▼
tar -xvf - -C /tmp/homeback
│
▼
/tmp/homeback
所以实际上没有:
/home
↓
home.tar
↓
/tmp/homeback
而是:
/home
↓
tar打包
↓
内存中的数据流
↓
管道
↓
tar解包
↓
/tmp/homeback
