use Carp; sub dive { my ($ref,@path) = @_; for my $i (0..$#path) { $ref = $path[$i]=~/^\[(\d+)\]$/ ? $ref->[$1] : $ref->{$path[$i]}; if (!defined $ref) { carp "undef at: ".join('', map {/^\[\d+\]$/?$_:"{$_}"} @path[0..$i]); return undef; } } return $ref; } my $data = { this => { is => { a => { deep => { structure => [ 'abc', 'def', undef ] } } } } }; printf "<%s>\n", dive($data, qw/ this is a deep structure [0] /); printf "<%s>\n", dive($data, qw/ this is a deep structure [3] /); printf "<%s>\n", dive($data, qw/ this is an example /); __END__ undef at: {this}{is}{a}{deep}{structure}[3] at - line 10. main::dive(HASH(0x9e47a4), "this", "is", "a", "deep", "structure", "[3]") called at - line 22 Use of uninitialized value in printf at - line 22. <> undef at: {this}{is}{an} at - line 10. main::dive(HASH(0x9e47a4), "this", "is", "an", "example") called at - line 23 Use of uninitialized value in printf at - line 23. <>