windows grep GBK编码问题

我在Windows上使用grep时发现中文会出错。
比如在cmd里执行echo 测试 | grep 测试,就没有输出,查了查说是要设置环境变量LANG=zh_CN.GBK,我设置了之后确实可以了。但是对于这个环境变量的作用,我一直不很清楚,今天就做点实验来研究一下。

首先要清楚,cmd代码页默认是GBK,代码页分为输入代码页和输出代码页,这俩一般是一样的,可以用命令chcp查看,也可以用windows api中的GetConsoleCPGetConsoleOutputCP查看。
代码页GBK的代号是936,utf-8是65001

grep以及cat等有个特别的之处,当你设置了LANG=zh_CN.UTF-8之后,在cmd的936(GBK)代码页执行cat file.txt,file.txt是utf-8编码的文件,可以正常显示,这说明cat不是单纯的读取文件并直接输出(不然936肯定不能显示utf-8),cat和grep之类应该有统一的在读取LANG环境变量后进行的操作。

接下来设置LANG=zh_CN.UTF-8进行实验。
如果执行git status | grep 文件是有输出的。

image

那git status的输出编码是什么?执行git status > st.txt,看文件编码是utf-8.显然git也应该会受到LANG的影响。也就是git在936代码页正常显示了utf-8,那只能是git程序内部临时改了输出代码页为65001,执行完后又改了回来。这点很重要:【会根据LANG调用SetConsoleOutputCP(65001),结束再改回来】。

而grep看到LANG是utf-8,就把输出代码页改成了65001,并将参数“文件”的编码从GBK改成utf-8.也就是说,grep会把参数按utf-8进行编码。为了测试上述猜想的正确性,我们做这么个实验:
测试这两个字的utf-8编码和娴嬭瘯这三个字的GBK编码是一样的。你问我怎么知道?

cout<<u8"测试"

编译并在936代码页执行就可。

然后我在936代码页执行echo "娴嬭瘯utf-8" | grep "测试",结果如下:
image

结果非常amazing啊!不仅筛选出来了还正确显示了测试两个字。

那么grep会不会改输入代码页呢?上面是用管道,在grep看来和文件一样,直接读取二进制字节流,不会做什么转换的,输入代码页影响的是终端的标准输入。

再来实验一下,这次从标准输入读取文字
image

结论一目了然,grep根据LANG环境变量做的操作就是:

    1. 获取当前代码页,把参数编码从当前编码转为LANG指定的编码格式
    1. 设置输入和输出代码页为LANG指定的编码,把结果输出
    1. 恢复原来的代码页设置

同样,cat也是:

    1. 直接读取文件
    1. 设置输出代码页为LANG
    1. 将读取到的内容输出(不会做任何编码转换)

综上,要使用grep过滤中文,要保证一点:
LANG中的字符编码和输入的实际字符编码相同。
LANG会影响grep如何转换参数编码以及临时对输出代码页的更改。

posted @ 2021-09-07 16:51  王冰冰  阅读(977)  评论(0编辑  收藏  举报