for $i (keys %data) { push (@ips, $i); for $j (keys %{$data{$i}{ports}}) { # push (@names, $data{$i}{cname}); # <--wrong push (@names, $data{$i}{$j}{cname}); } } # compare that with this code that uses logical names # if also uses my to localise variable scope, strict compliant too for my $ip (keys %data) { push @ips, $ip; for my $port ( keys %{$data{$ip}->{ports}} ) { push @names, $data{$ip}->{$port}->{cname}; } } #### # make %data require Data::Dumper; print $q->header; print $q->escapeHTML(Dumper(\%data)); exit;