stibba has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks, I would like to ask your help on a subroutine that is getting me crazy. I am using a perl script (not written by me, I am not practising perl for years :() that creates backups of configurations of some Cisco devices. The script was running on a old server with Perl 5.8 and apparently there were no issues. Now the script is moved to a different server with Perl 5.10.1. Since the script is moved to the new location we are getting the famous error message in this post title. The subroutine that is giving the error is the following:
sub check_configs { my ($kernel, $heap) = @_[KERNEL, HEAP]; my $RCS = Rcs->new; $RCS->bindir('/usr/bin'); $RCS->workdir($heap->{cfg}->{default}->{destination}); $RCS->rcsdir($heap->{cfg}->{default}->{rcsdir}); $RCS->quiet(1); print "\nStart CFG check at: ".localtime()."\n"; foreach my $name (@{ $heap->{cfg}->{devices} }) { my $CFG = $heap->{cfg}->{device}->{$name}->{localfile}; if (-f $CFG) { my $md5 = eliminate_timestamps($CFG); $RCS->file(basename($CFG)); $RCS->ci('-l', "-t-Configfile for $name", "-mMD5-sum: $md5"); } else { print "Error: File $CFG does not exist\n"; } } }
The error refers to the line: my $CFG = $heap->{cfg}->{device}->{$name}->{localfile}; And of course is: "Can't use string ("XX.XX.XX.XX) as HASH ref while "strict refs" in use". Basically the IP of the device is considered to be a string and can't be used as a hash ref... I've read tens of posts about how to fix this but - definitely also because of my very bad knowledge of PERL after years of non-practice - but can't find how to fix it... can you help me finding the possible solution? Thanks!! Stibba

Replies are listed 'Best First'.
Re: Again on "can't use string as hash as HASH ref while "strict refs" in use"
by Corion (Patriarch) on Aug 26, 2015 at 07:12 UTC

    The problem is with your understanding of the data structure and the value of $heap. If you output the data structure you actually have in $heap, maybe that will show where the error is. Use Data::Dumper to look at the data structure:

    use Data::Dumper; warn Dumper $heap;

    You can help us help you better by providing a short, self-contained example that fails. Also, telling us the exact error message and the exact error location in your code provides us with more hints. There is a reason why Perl outputs them, but you chose to hide them from us.

Re: Again on "can't use string as hash as HASH ref while "strict refs" in use"
by stevieb (Canon) on Aug 26, 2015 at 13:23 UTC

    Definitely not wanting to take away from Corion's answer above as it is very important, but I did notice something while glancing at the code.

    In the for line, you use {devices}, but in the subsequent assignment to $CFG, you use {device}.

    foreach my $name (@{ $heap->{cfg}->{devices} }) { my $CFG = $heap->{cfg}->{device}->{$name}->{localfile};

    We won't know whether this is a typo or not unless you post some Data::Dumper output.

Re: Again on "can't use string as hash as HASH ref while "strict refs" in use"
by stibba (Initiate) on Aug 26, 2015 at 14:10 UTC

    Hello guys,
    first of all thank you very much for your help, much appreciated.
    I tried to run the Dumper and got the following output:

    $VAR1 = { 'cfg' => bless( { 'devicecount' => 2, 'sshpass' => '/home/cfgbackup/bin/sshpass' +, 'version' => '$Id: 1.3$', 'task' => { '1' => 'ip', '2' => 'name' }, 'default' => { 'passwd' => 'XXXX', 'KH' => '/tmp/known_hosts.1 +0823', 'destination' => '/home/cfg +backup/cfg', 'cryptpwd' => 'XXXXX', 'remotefile' => 'running-co +nfig', 'logdir' => '/home/cfgbacku +p/log', 'rcsdir' => '/home/cfgbacku +p/cfg/RCS', 'logfile' => '/home/cfgback +up/log/cfgbackup.log', 'username' => 'XXXXXX' }, 'device' => { 'ip' => '192.168.0.1', 'name' => 'switch' }, 'devices' => [ 'ip', 'name' ], 'bindir' => '/home/cfgbackup/bin' }, 'CFGBACKUP' ), 'devfail' => 2, 'devok' => 0, 'starttime' => 'Wed Aug 26 08:21:23 2015', 'task' => {} };

    The error that I get when I run the script is:

    Can't use string ("192.168.0.1") as a HASH ref while "strict refs" in +use at ./cfg_backup line 203.

    The data for the devices are taken from a xml file that is read via XML::Simple.

      Your data structure has no localfile entry, but you try to access it:

      my $CFG = $heap->{cfg}->{device}->{$name}->{localfile};

      What do you expect to happen here?

      The path through your data structure ends at 192.168.0.1, which is what Perl told you. There is no localfile below that. You need to go back and talk to the people writing the XML structure what should happen here. Also, using XML::Simple is often problematic, as it usually brings problem if a structure only repeats once or repeats multiple times.

      Ok, so when you do this:

      foreach my $name (@{ $heap->{cfg}->{devices} }) {

      ...you're actually looping over:

      devices => [ 'ip', 'name', ]

      array. In the next line, you're taking each one of those elements, say ip, and using it as the $name key in $heap->{cfg}->{device}->{$name} to get the value.

      However, the value of $heap->{cfg}->{device}->{ip} is an actual IP address. You then proceed to attempt to access the localfile key from it, but of course the IP address is a string, so there's nothing perl can do (with use strict;) which is a very good thing (trust me).

      With what I've seen so far, it doesn't look like you want to be doing any looping at all based on the structure you've got here... well, at least it looks like you don't want to loop over this specific structure.

      Also, localfile is not present in your data at all. It appears it might fit into the default hash, but can't tell at this point.

      You're going to have to post the entire script you're using in order to figure out at a higher level what's going on and is supposed to be going on.

      -stevieb

      This demo shows how with XML simple your structure can change depending on how many devices you have in the xml.

      #!perl use strict; use warnings; use XML::Simple; use Data::Dump 'pp'; my @xml=(); # 2 devices $xml[0] = ' <xml> <cfg> <device name="switch1" ip="192.168.0.1" localfile="filename1"/> <device name="switch2" ip="192.168.0.2" localfile="filename2"/> </cfg> </xml>'; # 1 device $xml[1] = ' <xml> <cfg> <device name="switch1" ip="192.168.0.1" localfile="filename1"/> </cfg> </xml>'; # run for each xml for my $i (0..1){ my $heap = XMLin($xml[$i]); for my $name (keys %{$heap->{cfg}->{device}}){ push @{ $heap->{cfg}->{devices}},$name; } print pp $heap; print "\n-----------------------\n"; foreach my $name (@{ $heap->{cfg}->{devices} }) { my $CFG = $heap->{cfg}->{device}->{$name}->{localfile}; print "$CFG\n"; } print "-----------------------\n"; }
      poj