awk干货汇总

2022/03/07

简介

awk 是一种编程语言,用于在 linux/unix 下对文本和数据进行处理。数据可以来自标准输入(stdin)、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是 linux/unix 下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk 有很多内建的功能,比如数组、函数等,这是它和 C 语言的相同之处,灵活性是 awk 最大的优势。 尽管操作可能会很复杂,但命令的语法始终是:

awk ‘{pattern + action}’ 或者 awk ‘pattern {action}’

grep 、sed、awk 被称为 linux 中的”三剑客”。

  • grep 更适合单纯的查找或匹配文本
  • sed 更适合编辑匹配到的文本
  • awk 更适合格式化文本,对文本进行较复杂格式处理

awk 命令

awk 命令格式和选项

语法格式

awk [options] 'script' var=value file(s)
awk [options] -f scriptfile var=value file(s)

常用命令选项

  • -F fs fs 指定输入分隔符,fs 可以时字符串或正则表达式

  • -v var=value 赋值一个用户定义变量,将外部变量传递给 awk

  • -f scriptfile 从脚本文件中读取 awk 命令

awk 脚本

awk 脚本是由模式和操作组成的。

一个 awk 脚本通常由 BEGIN, 通用语句块,END 语句块组成,三部分都是可选的。 脚本通常是被单引号或双引号包住。

awk 'BEGIN{ i=0 } { i++ } END{ print i }' filename
awk "BEGIN{ i=0 } { i++ } END{ print i }" filename

模式与操作

模式

模式可以是以下任意一种:

  • 正则表达式:使用通配符的扩展集

  • 关系表达式:使用运算符进行操作,可以是字符串或数字的比较测试

  • 模式匹配表达式:用运算符~(匹配)和~!不匹配

  • BEGIN 语句块, pattern 语句块, END 语句块

操作

操作由一个或多个命令、函数、表达式组成,之间由换行符或分号隔开,并位于大刮号内,主要部分是:变量或数组赋值、输出命令、内置函数、控制流语句。

awk 'BEGIN{ commands } pattern{ commands } END{ commands }' file

awk 执行过程分析

  • 第一步: 执行 BEGIN { commands } pattern 语句块中的语句

BEGIN 语句块:在 awk 开始从输入输出流中读取行之前执行,在 BEGIN 语句块中执行如变量初始化,打印输出表头等操作。

  • 第二步:从文件或标准输入中读取一行,然后执行 pattern{ commands }语句块。它逐行扫描文件,从第一行到最后一行重复这个过程,直到全部文件都被读取完毕。

pattern 语句块:pattern 语句块中的通用命令是最重要的部分,它也是可选的。如果没有提供 pattern 语句块,则默认执行{ print },即打印每一个读取到的行。{ }类似一个循环体,会对文件中的每一行进行迭代,通常将变量初始化语句放在 BEGIN 语句块中,将打印结果等语句放在 END 语句块中。

  • 第三步:当读至输入流末尾时,执行 END { command }语句块

END 语句块:在 awk 从输入流中读取完所有的行之后即被执行,比如打印所有行的分析结果这类信息汇总都是在 END 语句块中完成,它也是一个可选语句块。

AWK 内置变量

  • $n : 当前记录的第 n 个字段,比如 n 为 1 表示第一个字段,n 为 2 表示第二个字段。

  • $0 : 这个变量包含执行过程中当前行的文本内容。

  • ARGC : 命令行参数的数目。

  • ARGIND : 命令行中当前文件的位置(从 0 开始算)。

  • ARGV : 包含命令行参数的数组。

  • CONVFMT : 数字转换格式(默认值为%.6g)。

  • ENVIRON : 环境变量关联数组。

  • ERRNO : 最后一个系统错误的描述。

  • FIELDWIDTHS : 字段宽度列表(用空格键分隔)。

  • FILENAME : 当前输入文件的名。

  • NR : 表示记录数,在执行过程中对应于当前的行号

  • FNR : 同 NR :,但相对于当前文件。

  • FS : 字段分隔符(默认是任何空格)。

  • IGNORECASE : 如果为真,则进行忽略大小写的匹配。

  • NF : 表示字段数,在执行过程中对应于当前的字段数。 print $NF 答应一行中最后一个字段

  • OFMT : 数字的输出格式(默认值是%.6g)。

  • OFS : 输出字段分隔符(默认值是一个空格)。

  • ORS : 输出记录分隔符(默认值是一个换行符)。

  • RS : 记录分隔符(默认是一个换行符)。

  • RSTART : 由 match 函数所匹配的字符串的第一个位置。

  • RLENGTH : 由 match 函数所匹配的字符串的长度。

  • SUBSEP : 数组下标分隔符(默认值是 34)。

将外部变量值传递给 awk

  • 借助 -v 选项,可以将来自外部值(非 stdin)传递给 awk
VAR=10000
echo | awk -v VARIABLE=$VAR '{ print VARIABLE }'
  • 定义内部变量接收外部变量
var1="aaa"
var2="bbb"
echo | awk '{ print v1,v2 }' v1=$var1 v2=$var2
  • 当输入来自文件时
awk '{ print v1,v2 }' v1=$var1 v2=$var2 filename

awk 运算

  • 算术运算:(+,-,*,/,&,!,……,++,–)

所有用作算术运算符进行操作时,操作数自动转为数值,所有非数值都变为 0

  • 赋值运算:(=, +=, -=,*=,/=,%=,……=,**=)

  • 逻辑运算符:(||, &&)

  • 关系运算符:(<, <=, >,>=,!=, ==)

  • 正则运算符:(~,~!)(匹配正则表达式,与不匹配正则表达式)

awk 'BEGIN{a="100testa";if(a ~ /^100*/){print "ok";}}'
ok

awk 高级输入输出

读取下一条记录:next 语句

awk 中 next 语句使用:在循环逐行匹配,如果遇到 next,就会跳过当前行,直接忽略下面语句。而进行下一行匹配。net 语句一般用于多行合并:

awk ‘NR%2==1{next}{print NR,$0;}’ text.txt

说明: 当记录行号除以 2 余 1,就跳过当前行。下面的 print NR,$0 也不会执行。下一行开始,程序有开始判断 NR%2 值。这个时候记录行号是:2 ,就会执行下面语句块:print NR,$0

读取一行记录:getline 语句

  • 用法:输出重定向需用到 getline 函数。getline 从标准输入、管道或者当前正在处理的文件之外的其他输入文件获得输入。它负责从输入获得下一行的内容,并给 NF,NR 和 FNR 等内建变量赋值。如果得到一条记录,getline 函数返回 1,如果到达文件的末尾就返回 0,如果出现错误,例如打开文件失败,就返回-1。
  • 语法格式:getline var 变量 var 包含了特定行的内容
  • 用法说明:
  1. 当其左右无重定向符时|, <时:getline 作用于当前文件,读入当前文件的第一行给其后跟的变量 var 或$0(无变量),应该注意到,由于 awk 在处理 getline 之前已经读入了一行,所以 getline 得到的返回结果是隔行的。

  2. 当其左右有重定向符时|, <时:getline 则作用于定向输入文件,由于该文件是刚打开,并没有被 awk 读入一行,只是 getline 读入,那么 getline 返回的是该文件的第一行,而不是隔行。

循环结构

for 循环

for(变量 in 数组)
{语句}

for(变量;条件;表达式)
{语句}

while 循环

while(表达式)
    {语句}

do…while 循环

do
{语句} while(条件)

其他相关语句

  • break:退出程序循环

  • continue: 进入下一次循环

  • next:读取下一个输入行

  • exit:退出主输入循环,进入 END,若没有 END 或 END 中有 exit 语句,则退出脚本。

数组

在 awk 中数组叫做关联数组(associative arrays)。awk 中的数组不必提前声明,也不必声明大小。数组元素用 0 或空字符串来初始化,这根据上下文而定。

awk 'BEGIN{
        Array[1]="sun"
        Array[2]="kai"
        Array["first"]="www"
        Array["last"]="name"
        Array["birth"]="1987"

        info = "it is a test";
        lens = split(info,tA," ");
        for(item in tA)
        {print tA[item];}
        for(i=1;i<=lens;i++)
        {print tA[i];}
        print length(tA[lens]);
        } {
        print "item in array";
        for(item in Array) {print Array[item]};
        print "print in i++";
        for(i=1;i<=length(Array);i++) {print Array[i]};
        }'

  • 输出数组内容
  1. 有序输出 for…in

因为数组时关联数组,默认是无序的

  1. 无序输出 for(i=1;i<l=ens;i++)

数组下标从 1 开始

  • 判断键值是否存在
#错误的判断方法,awk数组是关联数组,只要通过数组引用它的KEY,就会自动创建。
awk 'BEGIN{
    tB["a"]="a1";
    tB["b"]="b1";
    if(tB["c"]!="1"){   #tB["c"]没有定义,但是循环的时候会输出
        print "no found";
    };
    for(k in tB){
        print k,tB[k];
    }}'

#正确的判定方法:使用 if ( key in array) 判断数组中是否包含 键值
awk 'BEGIN{
        tB["a"]="a1";
        tB["b"]="b1";
        if( "c" in tB){
            print "ok";
        };
        for(k in tB){
            print k,tB[k];
        }}'

  • 删除键值
awk 'BEGIN{
        tB["a"]="a1";
        tB["b"]="b1";
        delete tB["a"];
        for(k in tB){
            print k,tB[k];
        }}'

内置函数

算数函数

atan2( y, x )

返回 y/x 的反正切。

$ awk 'BEGIN {
  PI = 3.14159265
  x = -10
  y = 10
  result = atan2 (y,x) * 180 / PI;

  printf "The arc tangent for (x=%f, y=%f) is %f degrees\n", x, y, result
}'

输出结果为:

The arc tangent for (x=-10.000000, y=10.000000) is 135.000000 degrees

cos( x )

返回 x 的余弦;x 是弧度。

$ awk 'BEGIN {
  PI = 3.14159265
  param = 60
  result = cos(param * PI / 180.0);

  printf "The cosine of %f degrees is %f.\n", param, result
}'

输出结果为:

The cosine of 60.000000 degrees is 0.500000.

sin( x )

返回 x 的正弦;x 是弧度。

$ awk 'BEGIN {
  PI = 3.14159265
  param = 30.0
  result = sin(param * PI /180)

  printf "The sine of %f degrees is %f.\n", param, result
}'

输出结果为:

The sine of 30.000000 degrees is 0.500000.

exp( x )

返回 x 幂函数。

$ awk 'BEGIN {
  param = 5
  result = exp(param);

  printf "The exponential value of %f is %f.\n", param, result
}'

输出结果为:

The exponential value of 5.000000 is 148.413159.

log( x )

返回 x 的自然对数。

$ awk 'BEGIN {
  param = 5.5
  result = log (param)

  printf "log(%f) = %f\n", param, result
}'

输出结果为:

log(5.500000) = 1.704748

sqrt( x )

返回 x 平方根。

$ awk 'BEGIN {
  param = 1024.0
  result = sqrt(param)

  printf "sqrt(%f) = %f\n", param, result
}'

输出结果为:

sqrt(1024.000000) = 32.000000

int( x )

返回 x 的截断至整数的值。

$ awk 'BEGIN {
  param = 5.12345
  result = int(param)

  print "Truncated value =", result
}'

输出结果为:

Truncated value = 5

rand( )

返回任意数字 n,其中 0 <= n < 1。

$ awk 'BEGIN {
  print "Random num1 =" , rand()
  print "Random num2 =" , rand()
  print "Random num3 =" , rand()
}'

输出结果为:

Random num1 = 0.237788

Random num2 = 0.291066

Random num3 = 0.845814

srand( [Expr] )

将 rand 函数的种子值设置为 Expr 参数的值,或如果省略 Expr 参数则使用某天的时间。返回先前的种子值。

$ awk 'BEGIN {
  param = 10

  printf "srand() = %d\n", srand()
  printf "srand(%d) = %d\n", param, srand(param)
}'

输出结果为:

srand() = 1

srand(10) = 1417959587

字符串函数

gsub( Ere, Repl, [ In ] )

gsub 是全局替换( global substitution )的缩写。除了正则表达式所有具体值被替代这点,它和 sub 函数完全一样地执行。

$ awk 'BEGIN{info="this is a test2012test!";gsub(/[0-9]+/,"||",info);print info}'

输出结果为:

this is a test   test!

sub(regex,sub,string)

sub 函数执行一次子串替换。它将第一次出现的子串用 regex 替换。第三个参数是可选的,默认为 $0。

$ awk 'BEGIN {
    str = "Hello, World"

    print "String before replacement = " str

    sub("World", "Jerry", str)

    print "String after replacement = " str
}'

输出结果为:

String before replacement = Hello, World

String after replacement = Hello, Jerry

substr(str, start, l)

substr 函数返回 str 字符串中从第 start 个字符开始长度为 l 的子串。如果没有指定 l 的值,返回 str 从第 start 个字符开始的后缀子串。

$ awk 'BEGIN {
    str = "Hello, World !!!"
    subs = substr(str, 1, 5)

    print "Substring = " subs
}'

输出结果为:

Substring = Hello

index( String1, String2 )

在由 String1 参数指定的字符串(其中有出现 String2 指定的参数)中,返回位置,从 1 开始编号。如果 String2 参数不在 String1 参数中出现,则返回 0(零)。

$ awk 'BEGIN {
    str = "One Two Three"
    subs = "Two"

    ret = index(str, subs)

    printf "Substring \"%s\" found at %d location.\n", subs, ret
}'

输出结果为:

Substring “Two” found at 5 location.

length [(String)]

返回 String 参数指定的字符串的长度(字符形式)。如果未给出 String 参数,则返回整个记录的长度($0 记录变量)。

$ awk 'BEGIN {
    str = "Hello, World !!!"

    print "Length = ", length(str)
}'

输出结果为:

Substring “Two” found at 5 location.

blength [(String)]

返回 String 参数指定的字符串的长度(以字节为单位)。如果未给出 String 参数,则返回整个记录的长度($0 记录变量)。

substr( String, M, [ N ] )

返回具有 N 参数指定的字符数量子串。子串从 String 参数指定的字符串取得,其字符以 M 参数指定的位置开始。M 参数指定为将 String 参数中的第一个字符作为编号 1。如果未指定 N 参数,则子串的长度将是 M 参数指定的位置到 String 参数的末尾 的长度。

$ awk 'BEGIN {
    str = "Hello, World !!!"
    subs = substr(str, 1, 5)

    print "Substring = " subs
}'

输出结果为:

Substring = Hello

match( String, Ere )

在 String 参数指定的字符串(Ere 参数指定的扩展正则表达式出现在其中)中返回位置(字符形式),从 1 开始编号,或如果 Ere 参数不出现,则返回 0(零)。RSTART 特殊变量设置为返回值。RLENGTH 特殊变量设置为匹配的字符串的长度,或如果未找到任何匹配,则设置为 -1(负一)。

$ awk 'BEGIN {
    str = "One Two Three"
    subs = "Two"

    ret = match(str, subs)

    printf "Substring \"%s\" found at %d location.\n", subs, ret
}'

输出结果为:

Substring “Two” found at 5 location.

split( String, A, [Ere] )

将 String 参数指定的参数分割为数组元素 A[1], A[2], . . ., A[n],并返回 n 变量的值。此分隔可以通过 Ere 参数指定的扩展正则表达式进行,或用当前字段分隔符(FS 特殊变量)来进行(如果没有给出 Ere 参数)。除非上下文指明特定的元素还应具有一个数字值,否则 A 数组中的元素用字符串值来创建。

$ awk 'BEGIN {
    str = "One,Two,Three,Four"

    split(str, arr, ",")

    print "Array contains following values"

    for (i in arr) {
        print arr[i]
    }
}'

输出结果为:

Array contains following values

One

Two

Three

Four

tolower( String )

返回 String 参数指定的字符串,字符串中每个大写字符将更改为小写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。

$ awk 'BEGIN {
    str = "HELLO, WORLD !!!"

    print "Lowercase string = " tolower(str)
}'

输出结果为:

Lowercase string = hello, world !!!

toupper( String )

返回 String 参数指定的字符串,字符串中每个小写字符将更改为大写。大写和小写的映射由当前语言环境的 LC_CTYPE 范畴定义。

$ awk 'BEGIN {
    str = "hello, world !!!"

    print "Uppercase string = " toupper(str)
}'

输出结果为:

Uppercase string = HELLO, WORLD !!!

strtonum(str)

strtonum 将字符串 str 转换为数值。 如果字符串以 0 开始,则将其当作十进制数;如果字符串以 0x 或 0X 开始,则将其当作十六进制数;否则,将其当作浮点数。

$ awk 'BEGIN {
    print "Decimal num = " strtonum("123")
    print "Octal num = " strtonum("0123")
    print "Hexadecimal num = " strtonum("0x123")
}'

输出结果为:

Decimal num = 123

Octal num = 83

Hexadecimal num = 291

sprintf(Format, Expr, Expr, . . . )

根据 Format 参数指定的 printf 子例程格式字符串来格式化 Expr 参数指定的表达式并返回最后生成的字符串。

$ awk 'BEGIN {
    str = sprintf("%s", "Hello, World !!!")

    print str
}'

输出结果为:

Hello, World !!!

格式符 说明
%d 十进制有符号整数
%u 十进制无符号整数
%f 浮点数
%s 字符串
%c 单个字符
%p 指针的值
%e 指数形式的浮点数
%x %X 无符号以十六进制表示的整数
%o 无符号以八进制表示的整数
%g 自动选择合适的表示法

时间函数

mktime( YYYY MM DD HH MM SS[ DST])

生成时间格式

$ awk 'BEGIN {
    print "Number of seconds since the Epoch = " mktime("2014 12 14 30 20 10")
}'

输出结果为:

Number of seconds since the Epoch = 1418604610

strftime([format [, timestamp]])

格式化时间输出,将时间戳转为时间字符串. 具体格式,见下表.

$ awk 'BEGIN {
    print strftime("Time = %m/%d/%Y %H:%M:%S", systime())
}'

输出结果为:

Time = 12/14/2014 22:08:42

序号 描述
%a 星期缩写(Mon-Sun)。
%A 星期全称(Monday-Sunday)。
%b 月份缩写(Jan)。
%B 月份全称(January)。
%c 本地日期与时间。
%C 年份中的世纪部分,其值为年份整除 100。
%d 十进制日期(01-31)
%D 等价于 %m/%d/%y.
%e 日期,如果只有一位数字则用空格补齐
%F 等价于 %Y-%m-%d,这也是 ISO 8601 标准日期格式。
%g ISO8610 标准周所在的年份模除 100(00-99)。比如,1993 年 1 月 1 日属于 1992 年的第 53 周。所以,虽然它是 1993 年第 1 天,但是其  ISO8601 标准周所在年份却是 1992。同样,尽管 1973 年 12 月 31 日属于 1973 年但是它却属于 1994 年的第一周。所以 1973 年 12 月 31 日的 ISO8610  标准周所在的年是 1974 而不是 1973。
%G ISO 标准周所在年份的全称。
%h 等价于 %b.
%H 用十进制表示的 24 小时格式的小时(00-23)
%I 用十进制表示的 12 小时格式的小时(00-12)
%j 一年中的第几天(001-366)
%m 月份(01-12)
%M 分钟数(00-59)
%n 换行符 (ASCII LF)
%p 十二进制表示法(AM/PM)
%r 十二进制表示法的时间(等价于 %I:%M:%S %p)。
%R 等价于 %H:%M。
%S 时间的秒数值(00-60)
%t 制表符 (tab)
%T 等价于 %H:%M:%S。
%u 以数字表示的星期(1-7),1 表示星期一。
%U 一年中的第几个星期(第一个星期天作为第一周的开始),00-53
%V 一年中的第几个星期(第一个星期一作为第一周的开始),01-53。
%w 以数字表示的星期(0-6),0 表示星期日 。
%W 十进制表示的一年中的第几个星期(第一个星期一作为第一周的开始),00-53。
%x 本地日期表示
%X 本地时间表示
%y 年份模除 100。
%Y 十进制表示的完整年份。
%z 时区,表示格式为+HHMM(例如,格式要求生成的 RFC 822 或者 RFC 1036 时间头)
%Z 时区名称或缩写,如果时区待定则无输出。

systime()

得到时间戳,返回从 1970 年 1 月 1 日开始到当前时间(不计闰年)的整秒数

awk 'BEGIN{now=systime();print now}'

输出结果为:

1343210982

位操作函数

and

compl

按位求补。

lshift

左移位操作

rshift

右移位操作

or

按位或操作

xor

按位异或操作

其他函数

close(expr)

delete

exit

flush

getline

next

nextfile

return

system

执行特定的命令然后返回其退出状态。返回值为 0 表示命令执行成功;非 0 表示命令执行失败

$ awk 'BEGIN { ret = system("date"); print "Return value = " ret }'

输出结果为:

Sun Dec 21 23:16:07 IST 2014

Return value = 0

举例子

输出整行

awk '{print $0 }' col_print.txt

输出最后一列

cat col_print.txt  | awk '{print $NF }'

输出倒数第二列

cat col_print.txt  | awk '{print $(NF-1)}'

输出共有多少列

cat col_print.txt  | awk '{print  NF}'

输出多列

cat  col_print.txt | awk '{print $2,$3}'

我的脚本

#!/bin/bash

echo "用户修改区-开始"
#要替换的源代码所在的根目录,该脚本文件与根目录处于同级文件夹
ROOTFOLDER="MyCode" # KYSecurityDefenseDemo

echo "当前工程名:"$ROOTFOLDER

BUILD_VERSION="3.8.1"

echo "当前版本:"$BUILD_VERSION

#要排除的文件夹,例如demo中用到的第三方库AFNetworking,pods的第三方库等
EXCLUDE_DIR=" --exclude-dir=Pods --exclude-dir=buildAppstore --exclude-dir=Carthage --exclude-dir=Images.xcassets  --exclude-dir=Assets.xcassets  --exclude-dir=Certificates --exclude-dir=fastlane --exclude-dir=fastlanelog --exclude-dir=${ROOTFOLDER}Tests --exclude-dir=${ROOTFOLDER}Tests"

echo "要排除的文件夹 "${EXCLUDE_DIR}

ROOT_NEW_DIR=$ROOTFOLDER"_"$BUILD_VERSION

if [ ! -d $ROOT_NEW_DIR ]; then
mkdir -p -m 755 $ROOT_NEW_DIR
echo "创建工程目录 "$ROOT_NEW_DIR" 成功"
fi

cp  -Rf  ${ROOTFOLDER}/*  ${ROOT_NEW_DIR}/

ROOTFOLDER=$ROOTFOLDER"_"$BUILD_VERSION

echo "用户修改区-结束"

#自定义的保留关键字,相当与白名单,添加到该文件中,一行一个,加入该文件的关键字将不被混淆;如工程中自定义的文件夹名称
RESCUSTOM="resCustom.txt"

#保留关键字文件不可删除
RESERVEDKEYWORDS="./reskeys.txt"
#最终的保留关键字=保留关键字+文件名
RESKEYSALL="./reskeysall.txt"
#提取的所有关键字
SOURCECODEKEYWORDS="./srckeys.txt"
#过滤后,最终要替换的关键字,混淆结束后,不删除,用于bug分析
REPLACEKEYWORDS="./replacekeys.txt"
#过滤后,最终要替换的文件名,混淆结束后,不删除,用于bug分析
REPLACEFILES="./replacefiles.txt"

#删除已经存在的临时文件
rm -f $SOURCECODEKEYWORDS
rm -f $REPLACEKEYWORDS
rm -f $REPLACEFILES
rm -f $RESKEYSALL
rm -f temp.res

#提取文件名列表
echo '' > f.list
find $ROOTFOLDER -type f | sed "/\/\./d" >f.list
#根据要排除的文件目录,将文件列表分离
#Exclude=$(echo $EXCLUDE_DIR | sed "s/--exclude-dir\=//g" |sed "s/ $//g" | sed "s/[*.]//g" | sed "s/ /\\\|/g")
Exclude=$(echo $EXCLUDE_DIR | sed "s/--exclude-dir\=//g" |sed "s/ $//g" | sed "s/ /\\\|/g")
#保留文件列表
if [ $Exclude ]; then
    rm -f f_res.list
	cat f.list | grep "$Exclude" >f_res.list
fi

#混淆文件列表
rm -f f_rep.list
cat f.list | grep -v "$Exclude" >f_rep.list
#rm -f f.list

echo "[step1] - 提取文件名"
rm -f filter_file.txt

AssignSrcKeys="my.txt"
if [ $AssignSrcKeys ]; then
    echo "[step2] - 指定关键字"
    cat `echo $AssignSrcKeys` | sed "/^$/d"| sort | uniq >$SOURCECODEKEYWORDS
else
    echo "[step2] - 提取要过滤的函数关键字"
    #提取文件名
    cat f_rep.list | awk -F/ '{print $NF;}'| awk -F. '{print $1;}' | sed "/^$/d" | sort | uniq >filter_file.txt
    #从源代码目录中提取要过滤的函数关键字
    rm -f filter_fun.txt
    grep -h -r -I  "^[-+]" $FIND_DIR $EXCLUDE_DIR $INCLUDE_DIR --include '*.[mh]' |sed "s/[+-]//g"|sed "s/[();,: *\^\/\{]/ /g"|sed "s/[ ]*</</"|awk '{split($0,b," ");print b[2];}'| sort|uniq |sed "/^$/d"|sed "/^init/d" >filter_fun.txt

    echo "[step3] - 提取要过滤的属性关键字"
    #从源代码目录中提取要过滤的属性关键字
    rm -f filter_property.txt
    grep -r -h -I  ^@property $FIND_DIR  $EXCLUDE_DIR $INCLUDE_DIR --include '*.[mh]' | sed "s/(.*)/ /g"  | sed "s/<.*>//g" |sed "s/[,*;]/ /g" | sed "s/IBOutlet/ /g" |awk '{split($0,s," ");print s[3];}'|sed "/^$/d" | sort |uniq >filter_property.txt

    echo "[step4] - 提取要过滤的类关键字"
    #从源代码目录中提取要过滤的类关键字
    rm -f filter_class.txt
    grep -h -r -I  "^@interface" $FIND_DIR  $EXCLUDE_DIR $INCLUDE_DIR  --include '*.[mh]' | sed "s/[:(]/ /" |awk '{split($0,s," ");print s[2];}'|sort|uniq >filter_class.txt

    echo "[step5] - 提取要过滤的协议关键字"
    #从源代码目录中提取要过滤的协议关键字
    grep -h -r -I  "^@protocol" $FIND_DIR  $EXCLUDE_DIR $INCLUDE_DIR  --include '*.[mh]'| sed "s/[\<,;].*$//g"|awk '{print $2;}' | sort | uniq >>filter_class.txt

    echo "[step6] - 合并要过滤的关键字"
    #合并要过滤的关键字,并重新排序过滤
    rm -f $SOURCECODEKEYWORDS
    cat filter_fun.txt filter_property.txt filter_class.txt filter_file.txt |sed "/^$/d" | sort | uniq >$SOURCECODEKEYWORDS
    rm -f filter_fun.txt
    rm -f filter_class.txt
    rm -f filter_file.txt
fi

echo "[step7] - 自动获取保留字,工程名等"
#自动获取保留字,工程名等
rm -f temp.res
cat `cat f_rep.list | grep project.pbxproj` | grep -w productName | sed "s/;//g"|awk '{print $NF;}'>temp.res
#提取要保留的文件名
cat f_res.list | awk -F/ '{print $NF;}'| awk -F. '{print $1;}' | sed "/^$/d" | sort | uniq >>temp.res
rm -f f_res.list
#合并自定义保留字
#判断自定义保留字文件是否存在,不存在即创建一个空的
if [ ! -f "$RESCUSTOM" ]; then 
touch "$RESCUSTOM" 
fi 
cat $RESERVEDKEYWORDS $RESCUSTOM temp.res | sort |uniq >$RESKEYSALL
#rm -f temp.res

echo "[step8] - 过滤保留字,将需要混淆的关键字加密后写入文件"
#过滤保留字,将需要混淆的关键字加密后写入文件
rm -f $REPLACEKEYWORDS
echo "REPLACEKEYWORDS"$REPLACEKEYWORDS
cat $SOURCECODEKEYWORDS | 
while read line
do
if grep $line $RESKEYSALL
then
echo filter1: $line
else
#使用md5对关键字进行加密
md5 -r -s $line  | sed s/\"//g >> $REPLACEKEYWORDS
fi
done
rm -f $SOURCECODEKEYWORDS

echo "[step9] - 开始混淆,替换源代码中的关键字为加密后的"
#开始混淆,替换源代码中的关键字为加密后的,防止开头为数字的情况
cat $REPLACEKEYWORDS |
while read line
do
var1=$(echo "$line"|awk '{print "z"$1"m"}')
var2=$(echo "$line"|awk '{print $2}')
underline1=$(echo "$line"|awk '{print "_z"$1"m"}')
underline2=$(echo "$line"|awk '{print "_"$2}')
rm -f rep.tmp
assignDir="./ZAInsurance_3.8.1/ZAInsurance/ZAIFoundation/ZAIManagers/SignInWithAppleID/ZAISignInWithAppleIDManager.m"
# 查找带关键字的那一行代码,输出到rep.tmp
if grep -r -n -I -w "[_]\{0,1\}$var2" $ROOTFOLDER $EXCLUDE_DIR $INCLUDE_DIR  --include="*.[mhc]" --include="*.mm" --include="*.pch" --include="*.storyboard" --include="*.xib" --include="*.nib" --include="contents" --include="*.pbxproj" >rep.tmp
then
cat rep.tmp |
while read -r l
do
#获取文件路径
v1=$(echo "$l"|cut -d: -f 1 )
#获取行号
v2=$(echo "$l"|cut -d: -f 2 )
#获取指定行数据
v3=$(sed -n "$v2"p "$v1")
##sed自带文件文本替换功能,不符合我们的期望,故放弃使用;有无适合的脚本命令,还希望脚本高手予以指点~
#sed -i '' ''"$v2"'s/'"$var2"'/'"$var1"'/g' $v1
#特殊字符转义替换,echo中 输出的变量 一定要加双引号!!!
v4=$(echo "$v3" | awk '{gsub(/"/, "\\\"", $0);gsub(/</, "\\\<", $0);gsub(/>/, "\\\>", $0);gsub(/\*/, "\\\*", $0);gsub(/\//, "\\\/", $0);gsub(/\[/, "\\\[", $0);gsub(/\]/, "\\\]", $0);gsub(/\{/, "\\\{", $0);gsub(/\}/, "\\\}", $0);gsub(/\&/, "\\\\\&", $0); print $0;}')

#判断是否包含双引号
quoteRes=$(echo $v4 | grep "\"")
if [[ "$quoteRes" != "" ]]; then
    #包含引号,引号中的不处理
    idx=0
    newV3=$(echo "$v3"|sed 's/\\n/|line|/'|sed  's/ /|nbsp|/')
    echo "$newV3"  | awk -F '\"' '{for(i=1;i<=NF;i++) print $i;}' |
    while read line
    do
    let idx+=1
    if [ $idx -gt 1 ]; then
    newStr=$newStr"\\\""
    fi
    #echo -E "line:$idx$line"
    if [ $(($idx%2)) -eq 1 ]; then
    newStr=$newStr$(./KYReplacewords.run "$line" "$var2" "$var1")
    newStr=$(./KYReplacewords.run "$newStr" "$underline2" "$underline1")
    else
    newStr=$newStr$line
    fi
    #整行替换
    sed -i '' "$v2"'s/.*/'"$newStr"'/g' "$v1"
    sed -i '' "$v2"'s/|nbsp|/ /g' "$v1"
    sed -i '' "$v2"'s/|line|/\\n/g' "$v1"
    done
else
    #单词替换
    var3=$(./KYReplacewords.run "$v4" "$var2" "$var1") 
    var3=$(./KYReplacewords.run "$var3" "$underline2" "$underline1") 
    #整行替换
    sed -i '' "$v2"'s/.*/'"$var3"'/g' "$v1"
fi

sed -i '' "$v2"'s/|_|/_/g' "$v1"

echo "step2:$l"
done
else
echo "step2:do not find:$var2"
fi
done
rm -f tmp.txt

echo "[step10] - 过滤保留字,用于属性设置函数混淆"
#过滤保留字,用于属性设置函数混淆,将需要混淆的关键字加密后写入文件
rm -f repProperty.txt
cat filter_property.txt | 
while read line
do
if grep $line $RESKEYSALL
then
echo filter1: $line
else
md5 -r -s $line  | sed s/\"//g >> repProperty.txt
fi
done
rm -f filter_property.txt

echo "[step11] - 开始混淆,替换属性前带下划线的地方"
#开始混淆,替换属性前带下划线的地方
cat repProperty.txt |
while read line
do
ar=(`echo "$line"|cut -f 1-2 -d " "`)
lastFind=`echo _${ar[1]}`
lastRep=`echo _z${ar[0]}m`
rm -f rep.tmp
if grep -r -n -I -w "$lastFind"  $ROOTFOLDER $EXCLUDE_DIR $INCLUDE_DIR   --include="*.[mhc]" --include="*.mm" --include="*.storyboard" --include="*.xib" >rep.tmp
then
cat rep.tmp |
while read l
do
v1=$(echo "$l"|cut -d: -f 1 )
v2=$(echo "$l"|cut -d: -f 2 )
sed -i '' ''"$v2"'s/'"$lastFind"'/'"$lastRep"'/g' $v1
echo "step3:"$l
done
else
echo "step3:do not find:"$lastFind
fi
done
rm -f rep.tmp

echo "[step12] - 开始混淆,替换属性设置函数"
#开始混淆,替换属性设置函数
cat repProperty.txt |
while read line
do
ar=(`echo "$line"|cut -f 1-2 -d " "`)
first=`echo ${ar[1]}|cut -c -1| sed "y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/"`
second=`echo ${ar[1]}|cut -c 2-`
lastFind=`echo set$first$second`
lastRep=`echo setZ${ar[0]}m`
rm -f rep.tmp
if grep -r -n -I -w "$lastFind"  $ROOTFOLDER $EXCLUDE_DIR $INCLUDE_DIR  --include="*.[mhc]" --include="*.mm" --include="*.storyboard" --include="*.xib" >rep.tmp
then
cat rep.tmp |
while read l
do
v1=$(echo "$l"|cut -d: -f 1 )
v2=$(echo "$l"|cut -d: -f 2 )
sed -i '' ''"$v2"'s/'"$lastFind"'/'"$lastRep"'/g' $v1
echo "step3:"$l
done
else
echo "step3:do not find:"$lastFind
fi
done
rm -f rep.tmp
rm -f repProperty.txt

echo "###########################  恭喜您,代码混淆完成!  ###########################"
echo "###########################  运行混淆后的工程!  ###########################"

exit


Post Directory