Did you try this? Generally, "$hash->{shift}" is interpreted as "$hash->{'shift'}", but I think you mean "$hash->{shift()}".
Also, this and other solutions I've seen here seem to ignore the possibility of running into a non-hash somewhere along the way.
use Test::More 'tests' => 5;
sub deep_exists {
my $hash = shift;
my $key = shift;
return 0 if ( ref $hash ne ref {} || ! exists $hash->{$key} );
return deep_exists( $hash->{$key}, @_ ) if @_;
return 1;
}
my %h;
$h{there}{also}{yes} = 1;
$h{hash}{shift}{scalar} = 1;
ok( deep_exists( \%h, 'there', 'also', 'yes' ), 'there also yes' );
ok( deep_exists( \%h, 'there', 'also' ), 'there also' );
ok( ! deep_exists( \%h, 'there', 'also', 'no' ), 'there also no' );
ok( deep_exists( \%h, 'hash', 'shift', 'scalar' ), 'hash shift scalar'
+ );
ok( ! deep_exists( \%h, 'hash', 'shift', 'scalar', 'hash' ),
'hash shift scalar hash' );