一、AWK 核心定位

AWK 是文本处理/轻量数值计算编程语言,诞生初衷处理表格文本,内置:变量、循环、判断、浮点运算、数组、格式化输出。 你前面代码用到的是 AWK 最常用场景:纯数学计算(无需读文件) + Shell 传参。 三大主流实现:

  1. gawk(GNU awk,Linux 默认,功能最全,支持^幂、sqrt等)
  2. mawk(轻量,嵌入式常用)
  3. nawk(BSD/macOS 默认)

二、基础命令格式

awk [选项] '匹配规则{执行代码}' 输入文件/数据流

两大特殊代码块(重中之重)

  1. BEGIN{ ... }
    • 读取任何输入行之前执行仅1次
    • 无输入文件、只做计算、初始化变量专用,你那段代码就是用它
  2. END{ ... }
    • 读完所有输入行之后执行仅1次
    • 常用于汇总统计(求和、计数、平均值)

行处理主体 pattern{action}

无BEGIN/END时,awk会逐行读取输入,每行执行一次{action}

  • pattern:匹配条件(正则、数值判断、布尔),省略则匹配所有行
  • action:要执行的代码,省略则默认打印整行print $0

三、常用启动选项(你代码用到 -v

选项作用示例
-v var=val外部传参:Shell变量 → AWK内部变量awk -v h=32 -v w=32 'BEGIN{print h*w}'
-F "分隔符"指定列分隔符,默认空格/制表符-F "," 处理csv;`-F "
-f script.awk从文件读取awk脚本,不写在行内awk -f calc.awk data.txt
-W math开启高级数学函数(gawk)sqrt() log() exp()

-v 补充说明 Shell 变量无法直接在单引号awk脚本中访问,必须用 -v 注入:

# 错误,$SAMPLE_H 在单引号内不会被shell解析
awk 'BEGIN{print $SAMPLE_H}'
# 正确,-v 把shell变量传给awk内部h变量
awk -v h="$SAMPLE_H" 'BEGIN{print h}'

四、AWK 内置变量(高频)

1. 输入行相关(读文件时用)

变量含义
$0当前完整一行文本
$1,$2,$3...当前行第1、2、3列(按-F分隔)
NF当前行列总数 Number of Fields
NR已读取总行数 Number of Records
FS输入分隔符(等价-F)
OFSprint输出时的列分隔符,默认空格

2. 数学/全局内置(你计算代码用到)

标识功能
^幂运算,x^0.5 = 开平方,x^-0.5 = 1/√x
int(x)截断取整(直接舍弃小数,非四舍五入)int(11.9)=11
sqrt(x)平方根,等价x^0.5
print输出内容到stdout,会被shell $(awk)捕获
;语句分隔符,同一块内多条语句隔开

五、分模块语法详解

1. 纯计算场景(对应你 PRE_LEN 代码,无文件输入)

只使用 BEGIN{},不需要传入任何文件,专门做浮点运算

# 示例复刻你的逻辑
SAMPLE_H=32
SAMPLE_W=32
PRE_LEN=$(awk -v h="$SAMPLE_H" -v w="$SAMPLE_W" '
BEGIN{
    s = (h * w) ^ -0.5;  # s = 1/sqrt(hw)
    ph = int(h * 16 * s);
    pw = int(w * 16 * s);
    print ph * pw;       # 输出计算结果
}
')
echo $PRE_LEN # 输出256

等价简化写法(一行压缩):

PRE_LEN=$(awk -v h=$SAMPLE_H -v w=$SAMPLE_W 'BEGIN{print int(h*16/sqrt(h*w)) * int(w*16/sqrt(h*w))}')

2. 读取文件/数据流(文本处理通用)

示例1:按空格分割,打印第1、3列

# test.txt内容:aa 123 bb 456
awk '{print $1, $3}' test.txt
# 输出 aa bb

示例2:匹配过滤,只打印第二列大于100的行

awk '$2 > 100 {print $0}' test.txt

示例3:BEGIN初始化 + 行循环累加 + END汇总

统计文件第二列总和

awk '
BEGIN{sum=0}
{sum += $2}
END{print "总和=" sum}
' test.txt

3. 运算符体系

数学运算符

+ - * / % ^ 加减乘除取模幂 ++ -- 自增自减 = += -= 赋值复合运算

比较运算符

> < >= <= == != 数值相等判断 ~ !~ 正则匹配 $1 ~ /abc/ 第一列包含abc

逻辑运算符

&& 与、|| 或、!

4. 控制流:if / for / while

if 判断

BEGIN{
    a=20
    if(a > 16){
        print "大于16"
    }else{
        print "小于等于16"
    }
}

for 循环

BEGIN{
    total=0
    for(i=1;i<=10;i++){
        total += i
    }
    print total # 1~10求和
}

5. 内置数学函数(gawk)

BEGIN{
    print sqrt(1024);  # 平方根
    print int(16.99);  # 截断取整16
    print log(2.718);  # 自然对数
    print exp(1);      # e^x
    print sin(3.14/2); # 三角函数(弧度)
}

六、Shell 与 AWK 交互核心技巧

1. 捕获awk输出赋值变量(你代码 $(awk)

$(命令)命令替换:执行内部命令,把标准输出字符串取出,赋值给shell变量

VAL=$(awk 'BEGIN{print 100+56}')
echo $VAL # 156

2. 多行续行符 \

shell行尾\表示换行只是排版,逻辑是同一行命令:

# 换行拆分,可读性高
PRE_LEN=$(awk -v h="$SAMPLE_H" \
           -v w="$SAMPLE_W" \
           'BEGIN{print int(h*16/sqrt(h*w))*int(w*16/sqrt(h*w))}')

3. 引号规则(避坑重点)

  1. awk脚本必须用单引号包裹:'code'
    • 双引号会让shell提前解析$\等符号,破坏awk语法
  2. shell变量传递只能用 -v,不能直接在awk内写$SAMPLE_H
  3. awk内部打印字符串用双引号:print "结果=", val

七、高频实用示例(工程常用)

示例1:快速计算任意数学表达式(替代bc)

shell原生不支持浮点运算,awk是最简方案

# 计算平方根
awk -v n=1024 'BEGIN{print sqrt(n)}'
# 小数乘法
awk 'BEGIN{print 32 * 16 / sqrt(32*64)}'

示例2:批量统计文件夹文件大小

ls -l | awk 'BEGIN{sum=0} NR>1 {sum+=$5} END{print "总字节:",sum}'

示例3:过滤日志,提取错误行并计数

# 打印包含error的行,最后输出错误总数
awk '/error/ {print $0; cnt++} END{print "错误行数:" cnt}' app.log

八、针对你那段代码的踩坑总结

  1. 不能去掉 -v:去掉后awk无法读取h/w,计算为0,PRE_LEN=0,生成逻辑崩溃
  2. 不能用 sqrt(h*w) 替代 (h*w)^-0.5:效果等价,但^写法更贴合原模型公式
  3. int() 是截断不是四舍五入:矩形分辨率会导致ph*pw≠256,不能硬编码256
  4. 必须 BEGIN{}:没有输入文件,不写BEGIN代码不会执行,PRE_LEN为空
  5. 行尾 \ 仅排版:删除后整行写在一起功能完全不变,仅可读性下降

九、极简记忆思维导图

awk 调用结构
├─ 选项:-v 传参 / -F 分隔符
├─ 脚本块
│  ├─ BEGIN{}:预处理/纯计算(无输入必用)
│  ├─ pattern{action}:逐行处理文件
│  └─ END{}:收尾汇总统计
└─ 输出捕获:shell $(awk ...) 存到变量