Windows构建系统中GREP/Perl命令异常及跨平台差异问询
问题背景与疑问
需求场景
需要从elf.h头文件中提取ELFOSABIV_LATEST对应的版本值6U,用于给mdConverter.exe的--abi-version参数传值。
elf.h内容如下:
#define ELFOSABIV_STDELF_V1 8U // STD ELF ABI #define ELFOSABIV_PRE_STDELF 7U // latest abi version #define ELFOSABIV_LATEST 6U // latest abi version
原方案问题
Linux下使用grep的Makefile代码可正常工作:
MDCONVERTER := $(HOST_TOOLSBIN)/mdConverter$(EXESUFFIX) --abi-version `$(GREP) -Po '^\#define\s+ELFOSABIV_LATEST\s+\K\S+' $(ELF_DIR)/elf.h`
但Windows下依赖busybox_glob.exe提供的grep报错,原因是该版本busybox的grep不支持-P(Perl正则)和-o参数,直接输出了grep的用法提示。
Perl方案的跨平台差异
改用Perl单行命令后,Linux和Windows输出不一致:
Linux执行结果
perl -ne "print $1 if /#define\s+ELFOSABIV_LATEST\s+(\S+)/" /home/test/elf/elf.h #define ELFOSABIV_LATEST 6U
Windows执行结果
perl -ne "print $1 if /#define\s+ELFOSABIV_LATEST\s+(\S+)/" /home/test/elf/elf.h 6U
疑问:为什么同一条Perl命令在两个系统下输出存在差异?
差异原因解析
核心原因是不同系统的shell对双引号内变量的解析行为不同:
- 在Linux的bash等Unix shell中,双引号包裹的
$1会被shell优先解析为当前脚本的第一个参数(此处无传参,$1为空),导致Perl实际收到的命令是:
此时Perl未输出捕获组-ne "print if /#define\s+ELFOSABIV_LATEST\s+(\S+)/"$1,而是输出了匹配到的整行内容,也就是#define ELFOSABIV_LATEST 6U。 - 在Windows的cmd环境中,双引号内的
$1不会被shell解析,会原封不动传递给Perl,Perl能正确识别$1为正则表达式的第一个捕获分组,因此输出捕获到的6U(前导空格是正则中\s+匹配了变量名后的多个空格)。
解决方法
要让Perl命令在两个系统下都正确输出6U,需避免shell解析$1:
- 将Perl命令的双引号替换为单引号(Unix shell和Windows cmd都不会解析单引号内的变量):
perl -ne 'print $1 if /#define\s+ELFOSABIV_LATEST\s+(\S+)/' /home/test/elf/elf.h - 若必须使用双引号,对
$进行转义(跨平台通用写法):perl -ne "print \$1 if /#define\s+ELFOSABIV_LATEST\s+(\S+)/" /home/test/elf/elf.h
如果需要去掉输出的前导空格,可以调整正则表达式,用\K忽略前面的匹配内容:
perl -ne 'print $1 if /#define\s+ELFOSABIV_LATEST\s*\K(\S+)/' /home/test/elf/elf.h
内容的提问来源于stack exchange,提问作者vam
相关产品推荐
相关产品推荐

