Perl中修改foreach遍历局部变量导致原数组被修改的问题
Perl遍历数组时原数组被意外修改问题排查
复现代码
use File::Basename; my @powerArray; my $powerLoc = "/home/yuerki/10.12/proj"; @powerArray = glob("$powerLoc/*") if (-e $powerLoc); print "FIRST: @powerArray is powerArray\n"; foreach my $powerNum (@powerArray) { $powerNum= basename ($powerNum); if($powerNum =~ /power/) { $powerNum =~ s/power//g; $powerNum =~ s/\n//g; $powerNum =~ s/^\s+|\s+$//g; push(@powerList,"$powerNum") if ($powerNum =~ /^\d+$/); } } print "SECOND: @powerArray is powerArray\n";
测试环境
- 目标目录
/home/yuerki/10.12/proj/下文件为:power1、power2、power3、power4、power5
实际运行输出
FIRST: /home/yuerki/10.12/proj/power1 /home/yuerki/10.12/proj/power2 /home/yuerki/10.12/proj/power3 /home/yuerki/10.12/proj/power4 /home/yuerki/10.12/proj/power5 is powerArray Second: 1 2 3 4 5 is powerArray
问题原因
这是Perlforeach循环的固有特性,不是bug:
foreach循环声明的循环变量(本例中的$powerNum)不是数组元素的值拷贝副本,而是当前遍历到的原数组元素的别名,相当于给原数组里的元素绑定了一个新名字。- 所有对循环变量的赋值、正则替换等修改操作,本质都是直接操作原数组里的对应元素,没有任何隔离。
- 本例中循环内对
$powerNum依次做了取basename、替换power字符串、去换行、去首尾空白的操作,每一步都在直接修改@powerArray里存储的原始值,因此最后打印数组时,原来的全路径已经被一步步替换成了纯数字1~5。
规避方案
根据编码习惯选任意一种即可,都能保证原数组内容不被修改:
- 遍历数组副本:循环时直接遍历原数组的一次性临时拷贝,循环变量的别名指向副本里的元素,和原数组完全隔离。修改循环头即可:
# 括号内的操作会生成一份原数组的临时拷贝,遍历操作不会影响原数组 foreach my $powerNum (my @temp = @powerArray) { # 原有业务逻辑不用改 } - 循环内手动拷贝值:进入循环后第一时间把循环变量的值拷贝到独立的局部变量,后续所有处理都针对这个独立变量操作,不修改循环变量本身:
foreach my $powerNum (@powerArray) { my $current = $powerNum; # 手动拷贝值到独立变量 $current = basename($current); if($current =~ /power/) { $current =~ s/power//g; $current =~ s/\n//g; $current =~ s/^\s+|\s+$//g; push(@powerList,"$current") if ($current =~ /^\d+$/); } }
补充提示:Perl中
for/foreach、map、grep这类遍历数组的语法块,默认的$_或者自定义循环变量都是原元素的别名,修改时如果不想改动原数组,一定要记得手动做值拷贝。
内容的提问来源于stack exchange,提问作者PPP
相关产品推荐
相关产品推荐

