Long Luo's Life Notes

每一天都是奇迹

By Long Luo

AWK 是一种强大的文本处理工具,广泛用于 Linux/Unix 系统中对文本文件或数据流进行操作。它能够基于条件筛选、统计字段、重新排列数据等。主要特点包括:

  • 基于模式匹配: 根据条件筛选数据。
  • 列操作能力: 简单高效地处理文本列数据。
  • 轻量编程语言: 提供内置变量、循环、条件语句等编程功能。

AWK 程序的结构

AWK 程序的结构:

1
awk 'pattern { action }' file
  • pattern: 指定操作的匹配规则,例如正则表达式、逻辑判断等。
  • action: 指满足条件时要执行的操作,用 {} 包围,例如打印、统计、替换等。
  • file: 要处理的文本文件名称

常用内置变量

变量含义
NR当前处理的行号
FNR当前文件的行号(处理多个文件时的相对行号)
NF当前行的字段数(列数)
1,2第 1 列、第 2 列的值
$NF当前行的最后一列值
FS输入字段分隔符(默认为空格)
OFS输出字段分隔符(默认空格)
RS输入记录分隔符(默认为换行符)
ORS输出记录分隔符(默认为换行符)
ARGIND当前处理的文件在命令行参数中的索引
ARGC命令行参数的数量
ENVIRON存储当前环境变量的关联数组
FILENAME当前正在处理的文件名
SUBSEP数组下标分隔符(默认为 \034)
RSTARTmatch 函数匹配字符串的起始位置
RLENGTHmatch 函数匹配字符串的长度

以下是 常用内置变量 的补充说明:

变量说明:

  1. NR 和 FNR 的区别:
  • NR:累计行号,处理多个文件时行号会累加。
  • FNR:当前文件的行号,处理多个文件时每个文件的行号从 1 开始重新计数。
  1. FS 和 OFS 的作用:
  • FS:指定输入字段的分隔符,默认是空格。
  • OFS:指定输出字段的分隔符,默认是空格。
  1. RS 和 ORS 的作用:
  • RS:指定输入记录的分隔符,默认是换行符。
  • ORS:指定输出记录的分隔符,默认是换行符。
  1. ENVIRON 的使用:
  • ENVIRON 是一个关联数组,用于访问环境变量。例如:
1
awk 'BEGIN { print ENVIRON["HOME"] }'
  1. RSTART 和 RLENGTH:
  • 这两个变量与 match 函数配合使用,用于获取匹配字符串的起始位置和长度。例如:
1
awk 'BEGIN { str = "hello world"; match(str, /world/); print RSTART, RLENGTH }'
  1. SUBSEP:
  • 用于多维数组的下标分隔符,默认是 \034(非打印字符)。例如:
1
awk 'BEGIN { arr["a", "b"] = 10; print arr["a", "b"] }'
  1. ARGIND 和 ARGC:
  • ARGIND:当前处理的文件在命令行参数中的索引(从 1 开始)。
  • ARGC:命令行参数的数量。例如:
1
awk 'BEGIN { print ARGIND, ARGC }' file1.txt file2.txt
  1. FILENAME:
  • 当前正在处理的文件名。例如:
1
awk '{ print FILENAME, $0 }' file1.txt file2.txt
  1. 1,2, …, $NF:
  • $1 表示第 1 列,$2 表示第 2 列,依此类推。
  • $NF 表示当前行的最后一列。
  1. NF:
  • 当前行的字段数(列数)。例如:
1
awk '{ print NF }' file.txt
  1. RS 和 ORS 的高级用法:
  • 可以修改 RS 和 ORS 来处理非标准格式的文件。例如,将 RS 设置为空字符串,以处理多行记录:
1
awk 'BEGIN { RS = "" } { print $0 }' file.txt
  1. SUBSEP 的高级用法:
  • 可以修改 SUBSEP 来定义多维数组的下标分隔符。例如:
1
awk 'BEGIN { SUBSEP = ":"; arr["a", "b"] = 10; print arr["a", "b"] }'
  1. RSTART 和 RLENGTH 的高级用法:
  • 结合 match 函数,可以提取匹配的子字符串。例如:
1
awk 'BEGIN { str = "hello world"; match(str, /world/); print substr(str, RSTART, RLENGTH) }'
  1. ENVIRON 的高级用法:
  • 可以遍历 ENVIRON 数组,打印所有环境变量。例如:
1
awk 'BEGIN { for (key in ENVIRON) print key, ENVIRON[key] }'
  1. FILENAME 的高级用法:
  • 在处理多个文件时,可以根据文件名执行不同的操作。例如:
1
awk '{ if (FILENAME == "file1.txt") print "File1:", $0; else print "File2:", $0 }' file1.txt file2.txt

运行 AWK 程序

AWK 程序可以通过以下方式运行:

  1. 命令行直接运行:
1
awk 'pattern { action }' file
  • pattern:匹配条件(可选)。
  • action:满足条件时执行的操作。
  • file:要处理的文件。
  1. 脚本文件运行:
1
awk -f script.awk file

示例:假设 script.awk 内容如下:

1
{ print $1 }

运行命令:

1
awk -f script.awk file.txt
  • script.awk:包含 AWK 程序的脚本文件。
  • file:要处理的文件。

注意事项: - 未指定文件时,AWK 从标准输入读取数据; - 可同时处理多个文件;

AWK 基本输出

  • 打印文件的所有内容:
1
awk '{ print $0 }' file.txt
  • 打印文件的第 1 列和第 3 列:
1
awk '{ print $1, $3 }' file.txt

AWK 高级输出

printf 格式化输出 printf 可以格式化输出内容,例如:

1
awk '{ printf "Name: %s, Age: %d\n", $1, $2 }' file.txt

排序输出 结合 sort 命令对输出进行排序:

1
awk '{ print $1 }' file.txt | sort

选择/过滤行

按某列的值

  • 打印第 2 列大于 50 的行:
1
awk '$2 > 50 { print }' file.txt

按某几列值的计算结果

  • 打印第 1 列和第 2 列之和大于 100 的行:
1
awk '$1 + $2 > 100 { print }' file.txt

按字符串匹配

  • 打印包含 “error” 的行:
1
awk '/error/ { print }' file.txt

不同方式的组合

  • 打印第 2 列大于 50 且包含 “error” 的行:

awk ‘$2 > 50 && /error/ { print }’ file.txt BEGIN 和 END - BEGIN 块在处理输入前执行,END 块在处理输入后执行:

awk ‘BEGIN { print “Start” } { print } END { print “End” }’ file.txt

计算

统计数量:工时超过 15 小时的员工人数

1
awk '$3 > 15 { count++ } END { print count }' file.txt

求和、求平均:平均工资

1
awk '{ sum += $2 } END { print "Average:", sum/NR }' file.txt

处理文本:打印时薪最高的员工信息

1
awk '$4 > max { max = $4; line = $0 } END { print line }' file.txt

字符串拼接(concatenation):在一行内打印所有员工名

1
awk '{ names = names $1 " " } END { print names }' file.txt

打印最后一行

1
awk '{ last = $0 } END { print last }' file.txt

内置函数

  • 统计行数、单词数、字符数:
1
awk '{ chars += length($0); words += NF } END { print "Lines:", NR, "Words:", words, "Chars:", chars }' file.txt

控制流

If-Else

1
awk '{ if ($1 > 50) print "High"; else print "Low" }' file.txt

While

1
awk '{ i = 1; while (i <= NF) { print $i; i++ } }' file.txt

For

1
awk '{ for (i = 1; i <= NF; i++) print $i }' file.txt

数组

统计每列的总和:

1
awk '{ for (i = 1; i <= NF; i++) sum[i] += $i } END { for (i in sum) print "Column", i, "Sum:", sum[i] }' file.txt

AWK 示例速查表

以下是常见 AWK 功能及其对应程序和类似命令:

编号功能AWK 程序类似命令
1打印总行数END { print NR }wc -l
2打印第 10 行NR == 10 { print }sed -n ‘10p’
3打印最后一列{ print $NF }
4打印最后一行的最后一列{ f = $NF } END { print f }tail -n1
5打印有 4 列以上的行NF > 4 { print }
6打印最后一列的值大于 4 的行$NF > 4 { print }
7打印所有输入的总字段数{ nf += NF } END { print nf }
8打印包含关键字的总行数/keyword/ { n++ } END { print n }grep -c ‘keyword’
9打印第 1 列的最大值及对应的行$1 > max { max = $1; line = $0 } END { print max, line }
10打印列数大于 1 的行NF > 1 { print }
11打印长度大于 80 的行length($0) > 80 { print }
12打印每行的列数和该行内容{ print NF, $0 }
13打印第 2 列和第 1 列{ print $2, $1 }
14交换第 1 列和第 2 列{ t = $1; $1 = $2; $2 = t; print }
15第 1 列替换为行号{ $1 = NR; print }
16删除第 2 列并打印{ $2 = ““; print }
17倒序打印每行的字段{ for (i=NF; i>0; i–) printf “%s”, $i; printf “” }
18计算每行的字段和{ sum=0; for (i=1; i<=NF; i++) sum += $i; print sum }
19计算所有字段的总和{ for (i=1; i<=NF; i++) sum += \(i } END { print sum } | 无 | | 20 | 将所有字段取绝对值并打印 | { for (i=1; i<=NF; i++) if (\)i<0) \(i = -\)i; print }

参考文献

  1. AWK
  2. AWK 简明教程

By Long Luo

建筑是凝固的音乐,斑驳的外墙演绎着历史的旋律。

Cityscape of Bruges 布鲁日全景图

布鲁日是一座被时间静止的美丽小城。漫步在布鲁日的小巷中,脚踏着青石板路,仿佛走入了中世纪。满眼是砖红色屋顶,哥特式的长窗,高高伫立的钟楼,还有墙壁上随处可见的洛可可画作,你依然能感受到当年布鲁日的繁荣和富庶。

Street View of Bruges 布鲁日街景
阅读全文 »

By Long Luo

众所周知,计算机系统从 \(16\) 位发展到 \(32\) 位,再从 \(32\) 位发展到 \(64\) 位,与此同时不同的数据类型也随着系统的位数增大而增大。早期的操作系统是 \(16\) 位系统,int 用 \(2\) 字节表示,范围是 \([-32768, 32767]\) ,long 用 \(4\) 字节表示,范围是 \([-2147483648, 2147483647]\) ;后来发展到 \(32\) 位操作系统,int 用 \(4\) 字节表示,与 long 相同。

目前的操作系统已发展到 \(64\) 位操作系统,但因程序编译工艺的不同,两者表现出不同的差别:

  • \(32\) 位编译系统:int 占四字节,与 long 相同。
  • \(64\) 位编译系统:int 占四字节,long 占 \(8\) 字节,long 数据范围变为:\([-2^{63}, 2^{63}-1]\)

具体在标准中,并没有规定 long 一定要比 int 长,也没有规定 short 要比 int 短,只是规定了长整型至少和整型一样长,整型至少和短整型一样长。这个规则同样适用于浮点型 long double 至少和 double 一样长,double 至少和 float 一样长。至于如何实现要看编译器厂商。

下表所展示的是 Java 的 8 种基本数据类型的详细数据:

基本类型大小最小值最大值包装器类型默认值
boolean///Booleanfalse
char\(2\)Unicode 0Unicode 2^16-1Character‘u0000’
byte\(1\)\(-128\)\(127\)Byte0
short\(2\)\(-2^{15}\)\(+2^{15}-1\)Short0
int\(4\)\(-2^{31}\)\(+2^{31}-1\)Integer0
long\(8\)\(-2^{63}\)\(+2^{63}-1\)Long0
float\(4\)IEEE754IEEE754Float0
double\(8\)IEEE754IEEE754Double0

从上述表格中可以看出,普通工作生活中所涉及的数字都不会超过 int 所能表示的范围,而超过 long long 类型则少之又少。但是具体到一些行业或者科研中,比如天文,石油开采等,经常需要和天文数字进行打交道。举例来说,\(50!\)\(10^{20}\) 这种阶乘或者指数函数轻而易举就突破最大所能表示的范围。

如何表示这些超大数字以及对其进行数学运算呢?

大数字如何表示?

首先需要解决的问题就是如何表示,用数据类型是不可能了,那么我们应该怎么做呢?

很容易想到的就是将超大数字拆分为一个个位进行展示,存储这些位的值就可以了。至于怎么存,可以使用 string , 也可以使用 list, array 等。这里为了方便,我们使用 string 来说明及展示。

例子:

1
string number = "3377733333332222";

解决了展示问题,下面我们来展示如何对超大数字进行数学运算:

加法(Add)

让我们回到小学课堂,重温我们学习加法的第一课。回想我们是如何做加法的呢?

1
2
3
Input  : str1 = "3333311111111111", 
str2 = "44422222221111"
Output : 3377733333332222

很明显,我们是从低位开始,按位相加,直至最高位。

那么实现大数字的加法就可以很简单的分成3步: 1. 翻转每个 string ; 2. 从第 \(0\) 位开始依次相加到相对小的 string ,每位的数字是 \(sum \, \% 10\) ,如果有进位则进位为 \(\dfrac {sum}{10}\) ; 3. 对得到的结果进行翻转。

我们直接看实现吧:

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
string sum_of_large_number(string num1, string num2) {
if (num1.length() > num2.length()) {
swap(num1, num2);
}

int len1 = num1.length();
int len2 = num2.length();
reverse(num1.begin(), num1.end());
reverse(num2.begin(), num2.end());

string result = "";
int carry = 0;
for (int i = 0; i < len1; i++) {
int sum = (num1.at(i) - '0') + (num2.at(i) - '0') + carry;
result.push_back(sum % 10 + '0');
carry = sum / 10;
}

for (int i = len1; i < len2; i++) {
int sum = num2.at(i) - '0' + carry;
result.push_back(sum % 10 + '0');
carry = sum / 10;
}

if (carry) {
result.push_back(carry + '0');
}

reverse(result.begin(), result.end());
return result;
}
阅读全文 »

By Long Luo

Do you remember the first time going to somewhere that you enjoyed a lot? Well I remember my first time at Chimelong Ocean Kingdom . To be honest this was my first time going to Chimelong Resort. I know what you’re probably thinking right now “wow! He hasn’t been to the before!”.

As part of our annual company team-building activity, we embarked on a day journey to Chimelong Ocean Kingdom in Zhuhai. This extraordinary adventure park is known to house one of the world’s largest aquariums, making it a must-see attraction for marine life enthusiasts like myself.

Chimelong Ocean Kingdom Map

The highlight of the aquarium was the whale shark, a magnificent creature stretching over ten meters in length. It was truly awe-inspiring to witness such a gigantic, graceful creature up close. The aquarium was also teeming with an array of other aquatic life forms, each one more fascinating than the last.

阅读全文 »

By Long Luo

find 指令是 Unix/Linux 系统中很常用的指令之一,在日常开发维护中常常使用到这个工具。find 是一个很有用的指令,它支援非常多的查找选项,可以依照权限、拥有者、群组、文件类型、日期与大小等条件来查找,这里整理了一些常用的 find 指令的使用技巧。

  1. Find files by name

$ find /home/user/documents -name “example.txt”

  1. Find files by extension

$ find /var/log -name “*.log”

$ find /etc -mtime -7

$ find /usr/local -mtime +30

$ find /tmp -name “oldfile.txt” -delete

$ find /var/www -empty

$ find /home/user/downloads -size +100M

$ find /home -user username

$ find /etc -perm 0644

$ find /var/log -name “*.log” -exec {} ;

$ find /home/user/documents -type f -empty -exec {} ;

$ find /home/user/documents -type f -exec {} ;

$ find / -path “/proc” -prune -o -name “*.conf -print

$ find /var/www -mmin -60

$ find /home/user/pictures -name “*.jpg” | xargs tar -czvf archive.tar.gz

$ find /usr/bin -type I

  1. Find Files by Inode Number

$ find / -inum 456332

$ find /home/user -not -name “*.txt”

$ find /var/log -group syslog

$ find /home/user/downloads -size +50M -size -100M

$ find /var/log -type f -exec s {} +

$ find /var/log -mmin -120

$ find /home -user username -group groupname

$ find /var/log -perm 600

$ find /var/log -size +1G -exec {} ;

$ find /home/user -maxdepth -name “*txt”

$ find /var/log -atime +90

$ find /home/user -name “.*”

$ find /home/user -ctime +1

$ find /dev -type b

$ find / -perm /a=r -not -perm /a=w

$ find /home/user -name “config

参考文献

  1. find (Unix)
  2. find (Windows)
  3. findstr
0%