字符串函数
以下是 awk 中常用的字符串函数详解,包含语法和示例:
1.length() - 字符串长度
# 语法 length([string]) # 示例 echo "hello" | awk '{print length()}' # 输出 5 echo "hello" | awk '{print length($0)}' # 输出 5 echo "" | awk '{print length("test")}' # 输出 42.index() - 查找子串位置
# 语法 index(string, substring) # 返回位置(从1开始),未找到返回0 # 示例 echo "hello world" | awk '{print index($0, "world")}' # 输出 7 echo "hello" | awk '{print index($0, "x")}' # 输出 03.substr() - 提取子串
# 语法 substr(string, start [, length]) # 示例 echo "hello world" | awk '{print substr($0, 7, 5)}' # 输出 "world" echo "hello" | awk '{print substr($0, 2, 3)}' # 输出 "ell" echo "hello" | awk '{print substr($0, 3)}' # 输出 "llo"4.split() - 分割字符串
# 语法 split(string, array [, fieldsep]) # 示例 echo "a,b,c,d" | awk '{ n = split($0, arr, ",") for(i=1; i<=n; i++) print arr[i] }' # 输出: # a # b # c # d5.match() - 正则匹配
# 语法 match(string, regexp) # 返回匹配位置,设置RSTART和RLENGTH # 示例 echo "hello 123 world" | awk '{ if (match($0, /[0-9]+/)) { print "位置:", RSTART, "长度:", RLENGTH print "匹配内容:", substr($0, RSTART, RLENGTH) } }' # 输出: # 位置: 7 长度: 3 # 匹配内容: 1236.sub() - 替换第一个匹配
# 语法 sub(regexp, replacement [, target]) # 示例 echo "foo bar foo" | awk '{sub(/foo/, "FOO"); print}' # 输出:FOO bar foo echo "test" | awk '{sub(/t/, "T", $0); print}' # 明确指定target # 输出:Test7.gsub() - 全局替换
# 语法 gsub(regexp, replacement [, target]) # 示例 echo "foo bar foo" | awk '{gsub(/foo/, "FOO"); print}' # 输出:FOO bar FOO # 统计替换次数 echo "a b a c a" | awk '{print gsub(/a/, "A") " 次替换"}' # 输出:3 次替换8.toupper() / tolower() - 大小写转换
# 语法 toupper(string) tolower(string) # 示例 echo "Hello World" | awk '{print toupper($0)}' # 输出 HELLO WORLD echo "HELLO" | awk '{print tolower($0)}' # 输出 hello9.sprintf() - 格式化字符串
# 语法 sprintf(format, expression1, ...) # 示例 awk 'BEGIN { str = sprintf("姓名: %s, 年龄: %d, 分数: %.2f", "张三", 20, 95.5) print str }' # 输出:姓名: 张三, 年龄: 20, 分数: 95.5010.gensub() - 通用替换(GNU awk特有)
# 语法 gensub(regexp, replacement, how [, target]) # 示例 echo "a1 b2 c3" | awk '{print gensub(/([a-z])([0-9])/, "\\2\\1", "g")}' # 输出:1a 2b 3c # how参数:数字(第几个匹配)或 "g"(全局) echo "a b a c a" | awk '{print gensub(/a/, "A", 2)}' # 输出:a b A c a11.strtonum() - 字符串转数字(GNU awk)
# 语法 strtonum(string) # 自动识别八进制、十六进制 # 示例 awk 'BEGIN { print strtonum("123") # 123 print strtonum("0xFF") # 255 print strtonum("0777") # 511(八进制) }'12.asort() / asorti() - 数组排序
# 语法 asort(source [, dest [, how]]) # 按值排序 asorti(source [, dest [, how]]) # 按键排序 # 示例 awk 'BEGIN { arr["z"] = 3 arr["a"] = 1 arr["m"] = 2 n = asort(arr, sorted) for(i=1; i<=n; i++) print sorted[i] # 输出:1 2 3 m = asorti(arr, keys) for(i=1; i<=m; i++) print keys[i] # 输出:a m z }'13.patsplit() - 模式分割(GNU awk)
# 语法 patsplit(string, array [, fieldpat]) # 示例 echo "abc123def456" | awk '{ n = patsplit($0, arr, /[a-z]+/) for(i=1; i<=n; i++) print arr[i] }' # 输出: # abc # def综合示例
# 处理日志文件,提取并格式化 echo '2024-01-15 10:30:45 ERROR: Connection failed' | awk '{ # 提取时间部分 time_part = substr($0, 1, 19) # 提取错误级别 match($0, /(ERROR|WARN|INFO)/, arr) level = arr[0] # 提取错误信息 msg_start = index($0, ": ") if (msg_start > 0) { message = substr($0, msg_start + 2) } # 格式化输出 printf("[%s] %-5s: %s\n", time_part, toupper(level), substr(message, 1, 20) "...") }'使用技巧
链式调用
echo " Hello World " | awk '{ print toupper(substr($0, 3, 5)) }'处理多行
# 连接多行 awk 'BEGIN {ORS=" "} {gsub(/\n/, " "); print} END {print "\n"}' file.txt反向引用
echo "abc123" | awk '{print gensub(/([a-z]+)([0-9]+)/, "\\2-\\1", "g")}' # 输出:123-abc非字符串函数
1.数值处理函数
1.1int()- 取整函数
# 语法 int(x) # 返回x的整数部分(向0取整) # 示例 awk 'BEGIN { print int(3.14) # 3 print int(-3.14) # -3 print int(7.99) # 7 }'1.2sqrt()- 平方根函数
# 语法 sqrt(x) # 返回x的平方根 # 示例 awk 'BEGIN { print sqrt(4) # 2 print sqrt(2) # 1.41421 print sqrt(0) # 0 print sqrt(-1) # 产生错误(NaN) }'1.3exp()- 指数函数
# 语法 exp(x) # 返回e的x次幂 # 示例 awk 'BEGIN { print exp(0) # 1 print exp(1) # 2.71828 print exp(2) # 7.38906 printf "%.4f\n", exp(1) # 2.7183 }'1.4log()- 自然对数
# 语法 log(x) # 返回x的自然对数(以e为底) # 示例 awk 'BEGIN { print log(1) # 0 print log(2.71828) # 1(近似) print log(10) # 2.30259 }'1.5sin(),cos(),atan2()- 三角函数
# 语法 sin(x) # 正弦(x为弧度) cos(x) # 余弦(x为弧度) atan2(y, x) # 反正切(返回弧度) # 示例 awk 'BEGIN { pi = 3.14159265 print sin(pi/2) # 1(近似) print cos(0) # 1 print atan2(1, 1)*180/pi # 45度 }'1.6rand()- 随机数生成
# 语法 rand() # 返回0到1之间的随机数(不包括1) # 示例 awk 'BEGIN { srand() # 用当前时间初始化随机数种子 for(i=1; i<=5; i++) { print rand() } }'1.7srand()- 设置随机种子
# 语法 srand([expr]) # 设置随机数种子,返回先前的种子 # 示例 awk 'BEGIN { # 设置固定种子,使随机数可重复 srand(1234) print rand() # 每次都相同 print rand() # 使用当前时间作为种子 srand() print rand() # 每次运行不同 }'2.时间函数(GNU awk)
2.1systime()- 当前时间戳
# 语法 systime() # 返回从1970-01-01到现在的秒数 # 示例 awk 'BEGIN { print "当前时间戳:", systime() print "格式化时间:", strftime("%Y-%m-%d %H:%M:%S", systime()) }'2.2mktime()- 构造时间戳
# 语法 mktime(datespec) # datespec格式:YYYY MM DD HH MM SS [DST] # 示例 awk 'BEGIN { # 构造时间:2024-01-15 14:30:00 ts = mktime("2024 01 15 14 30 00") print "时间戳:", ts print "格式化:", strftime("%c", ts) }'2.3strftime()- 格式化时间
# 语法 strftime([format [, timestamp [, utc-flag]]]) # 常用格式符 # %Y - 年 %m - 月 %d - 日 # %H - 时(24) %I - 时(12) %M - 分 %S - 秒 # %A - 星期全称 %a - 星期简称 # %B - 月份全称 %b - 月份简称 # 示例 awk 'BEGIN { now = systime() print strftime("日期: %Y-%m-%d", now) print strftime("时间: %H:%M:%S", now) print strftime("完整: %A, %B %d, %Y %I:%M:%S %p", now) # 指定时间戳 print strftime("%Y-%m-%d", 1700000000) }'3.数组函数
SUBSEP
这是一个内置变量,用于数组下标分隔符,默认值是"\034"。在将数组转换为字符串时使用。
3.1 数组操作技巧
# 创建和遍历数组 awk 'BEGIN { # 创建数组 arr["name"] = "张三" arr["age"] = 25 arr["score"] = 95.5 # 遍历数组 for(key in arr) { print key " = " arr[key] } }' # 多维数组(使用SUBSEP分隔) awk 'BEGIN { arr["2024", "01", "15"] = 100 arr["2024", "01", "16"] = 200 for(key in arr) { split(key, idx, SUBSEP) print idx[1] "-" idx[2] "-" idx[3] ":", arr[key] } }'3.2 删除数组元素
# 语法 delete array[index] # 删除单个元素 delete array # 删除整个数组 # 示例 awk 'BEGIN { arr[1] = "a"; arr[2] = "b"; arr[3] = "c" print "删除前长度:", length(arr) delete arr[2] print "删除元素2后:" for(i in arr) print i ":" arr[i] delete arr print "删除整个数组后长度:", length(arr) }'4.位操作函数(GNU awk)
4.1 基本位操作
# 语法 and(v1, v2) # 按位与 or(v1, v2) # 按位或 xor(v1, v2) # 按位异或 lshift(num, count) # 左移 rshift(num, count) # 右移 compl(num) # 按位取反 # 示例 awk 'BEGIN { a = 10 # 1010 b = 6 # 0110 print "a AND b =", and(a, b) # 2 (0010) print "a OR b =", or(a, b) # 14 (1110) print "a XOR b =", xor(a, b) # 12 (1100) print "a左移2位 =", lshift(a, 2) # 40 (101000) print "a右移1位 =", rshift(a, 1) # 5 (0101) print "a取反 =", compl(a) # -11 (取决于位数) }'5.类型判断函数
5.1 类型检测
# 判断变量类型(GNU awk扩展) function get_type(var) { if (var == var+0) return "number" if (var == "") return "unassigned" return "string" } # 示例 awk 'BEGIN { x = 123 y = "hello" z = "" print "x类型:", get_type(x) print "y类型:", get_type(y) print "z类型:", get_type(z) }'6.I/O相关函数
6.1close()- 关闭文件/管道
# 语法 close(expression) # 关闭文件或管道 # 示例 awk '{ # 处理文件 while((getline line < "input.txt") > 0) { print "读取:", line } close("input.txt") # 关闭命令管道 cmd = "date" cmd | getline date_str print "当前日期:", date_str close(cmd) }'6.2system()- 执行系统命令
# 语法 system(command) # 执行命令,返回退出状态 # 示例 awk 'BEGIN { # 执行简单命令 status = system("echo 'Hello from system'") print "退出状态:", status # 执行复杂命令 system("ls -la | wc -l") }'7.getline函数详解
7.1 不同形式的getline
# 1. 从当前文件读取下一行 awk '{ print NR ":", $0 }' file.txt # 2. 从指定文件读取 awk '{ while((getline line < "other.txt") > 0) { print "其他文件:", line } close("other.txt") }' file.txt # 3. 从管道读取 awk 'BEGIN { "date +%Y-%m-%d" | getline today print "今天日期:", today }' # 4. 读取到变量 awk '{ getline # 读取下一行到$0 print "下一行:", $0 }' file.txt7.2 getline返回值
# getline返回值说明: # 1 - 成功读取 # 0 - 文件结束 # -1 - 错误 awk '{ while((ret = (getline line < "data.txt")) > 0) { print "成功读取:", line } if (ret == 0) print "文件结束" if (ret < 0) print "读取错误:", ERRNO }'8.数学计算技巧
8.1 浮点数精度控制
# 使用printf控制精度 awk 'BEGIN { pi = 3.141592653589793 e = 2.718281828459045 printf "pi = %.2f\n", pi # 3.14 printf "e = %.5f\n", e # 2.71828 printf "科学计数: %.2e\n", 1234567 # 1.23e+06 }' # 舍入函数 function round(x, digits) { return sprintf("%." digits "f", x) + 0 } awk 'BEGIN { print round(3.14159, 2) # 3.14 print round(3.14159, 3) # 3.142 }'8.2 统计计算
# 计算平均值、最大值、最小值 awk '{ sum += $1 count++ if (NR == 1 || $1 < min) min = $1 if (NR == 1 || $1 > max) max = $1 } END { if (count > 0) { avg = sum / count print "数量:", count print "总和:", sum print "平均:", avg print "最小:", min print "最大:", max print "范围:", max - min } }' numbers.txt9.时间计算技巧
9.1 时间差计算
# 计算时间差(秒数) function time_diff(start, end) { return end - start } # 计算天数差 function days_diff(start, end) { return (end - start) / 86400 # 86400秒/天 } # 示例 awk 'BEGIN { # 2024-01-01 和 2024-01-15 start = mktime("2024 01 01 00 00 00") end = mktime("2024 01 15 00 00 00") print "相差秒数:", time_diff(start, end) print "相差天数:", days_diff(start, end) }'9.2 添加时间间隔
# 添加天数 function add_days(timestamp, days) { return timestamp + days * 86400 } # 添加小时 function add_hours(timestamp, hours) { return timestamp + hours * 3600 } # 示例 awk 'BEGIN { now = systime() next_week = add_days(now, 7) print "现在:", strftime("%Y-%m-%d %H:%M:%S", now) print "一周后:", strftime("%Y-%m-%d %H:%M:%S", next_week) }'10.实战技巧
10.1 性能优化
# 1. 使用内置函数而不是自定义函数 # 2. 减少数组操作 # 3. 使用位运算代替数学运算(当适用时) # 判断奇偶数的优化 awk '{ # 传统方法 if ($1 % 2 == 0) print "偶数" # 位运算方法(更快) if (and($1, 1) == 0) print "偶数" }'10.2 错误处理
# 安全除法函数 function safe_divide(a, b, default_value) { if (b == 0) { return (default_value != "") ? default_value : "NaN" } return a / b } # 安全数值转换 function safe_num(str, default_value) { if (str == str+0) { return str+0 } return (default_value != "") ? default_value : 0 } # 示例 awk 'BEGIN { print safe_divide(10, 0, 0) # 输出 0 print safe_divide(10, 2) # 输出 5 print safe_num("123") # 123 print safe_num("abc", 0) # 0 }'11.综合示例:日志分析器
#!/usr/bin/awk -f # 日志分析脚本 BEGIN { printf("%-20s %-10s %-10s %-10s\n", "时间", "成功", "失败", "成功率") print "------------------------------------------------------" } # 假设日志格式: [时间] 状态 耗时ms { # 解析时间戳 match($0, /\[([0-9:]+)\]/, time_arr) time_key = substr(time_arr[1], 1, 5) # 只取小时和分钟 # 解析状态 if (match($0, /状态:([^ ]+)/, status_arr)) { status = status_arr[1] } # 解析耗时 if (match($0, /耗时:([0-9]+)/, time_arr)) { duration = time_arr[1]+0 total_time[time_key] += duration count[time_key]++ } # 统计状态 if (status == "成功") { success[time_key]++ } else if (status == "失败") { failure[time_key]++ } } END { # 计算并输出统计结果 for (time in success) { total = success[time] + failure[time] if (total > 0) { rate = success[time] / total * 100 avg_time = total_time[time] / count[time] printf("%-20s %-10d %-10d %-9.1f%% (平均耗时:%.0fms)\n", time, success[time], failure[time], rate, avg_time) } } # 总体统计 print "\n总体统计:" total_success = 0 total_failure = 0 for (time in success) { total_success += success[time] total_failure += failure[time] } total = total_success + total_failure printf("总请求数: %d\n", total) printf("成功率: %.1f%%\n", total_success/total*100) printf("平均耗时: %.0fms\n", total_time_all/count_all) }这些非字符串函数让awk成为一个强大的数据处理工具,特别适合数值计算、时间处理、统计分析等任务。掌握这些函数可以大大提高awk脚本的处理能力。