Stefan Welscher: Hash-Struktur dynmisch bilden

Moin moin,
ich hab hier ein Perl-Script in welchem ich alle Variablen zur Erstellung einer Konfiguration in einem Hash %I speichere. Um das Script flexibler zu machen wollte ich dem Benutzer eine Möglichkeit geben, zusätzliche Parameter über die Variable $I{'commands'} zu übertragen (z.B.: SET_INTERFACE_1_IP=1.1.1.1). Diese Variable muss dann in das bestehende Hash eingebracht werden (z.B. $I{'interface'}{1}{'ip'}="1.1.1.1").

Hierzu habe ich eine Funktion geschrieben, die die Referenz auf das Hash übergeben bekommt:

  
  
sub interpret_commands  
{  
 my $I=$_[0];  
 if ($I->{'commands'})  
 {  
  my $cmd;  
  my @commands=split (/[;:,.|]+/,$I->{'commands'});  
  foreach $cmd (@commands)  
  {  
   #SET-Commands  
   if ($cmd=~/^s*SET[_-]([a-z0-9_]+)(-([a-z0-9_]+))?(-([a-z0-9_]+))?(-([a-z0-9_]+))?(-([a-z0-9_]+))?(-([a-z0-9_]+))?(-([a-z0-9_]+))?(-([a-z0-9_]+))?(-([a-z0-9_]+))?(-([a-z0-9_]+))?\s*=\s*(.*?)\s*$/i)  
   {  
    my $construct="I";  
    my @parts=(lc($1),lc($3),lc($5),lc($7),lc($9),lc($11),lc($13),lc($15),lc($17),lc($19));  
    my $value=$20; $value=~s/(^["']|["']$)//g;  
    foreach $p (@parts)  
    {  
     if ($p=~/^\d+$/)     { $construct.="{".$p."}"; }  
     elsif ($p=~/^[a-z0-9_]+$/i) { $construct.="{'".$p."'}"; }  
    }  
    ${$construct}=$value;  
   }  
   elsif ($cmd=~/SET/i)  
   { print STDERR "\nInvalid command syntax: ".$cmd."\n"; }  
  }  
 }  
}  

Das Ergebnis der Variabel $construct ist soweit richtig, aber wie schaffe ich es den Wert in das hash zu übernehmen?

${$construct}=$value; geht nicht
$->{$construct}=$value; bringt Syntaxfehler

Help!

  1. HI

    Das Ergebnis der Variabel $construct ist soweit richtig, aber wie schaffe ich es den Wert in das hash zu übernehmen?

    ${$construct}=$value; geht nicht
    $->{$construct}=$value; bringt Syntaxfehler

    kannst du nochmal mit einfachen Worten erklären was du willst, sorry für deinen Code fehlt mir die Muse...

    $construct ist eine Hashreferenz???

    Gruß
     Kurt

    1. kannst du nochmal mit einfachen Worten erklären was du willst, sorry für deinen Code fehlt mir die Muse...

      $construct ist eine Hashreferenz???

      Kein Problem...,
      $I ist die Referenz auf das Hash des Hauptskriptes.
      $construct ist der in die Hashstruktur umgewandelte Benutzerbefehl.

      Das Script soll folgendes machen:

      Start:
      $I->{'commands'}="SET-CE-LAN-1-SPEED=100";

      Zwischenergebnis (entsprechende Zeile in der Funktion hab ich etwas gändert):
      $construct="$I->{'ce'}{'lan'}{1}{'speed'}";

      Destination:
      $I->{'ce'}{'lan'}{1}{'speed'}=100;

      1. OK, konnte es jetzt mit eval lösen... keine Ahnung ob das die geschickteste Lösung ist, aber es funkntioniert:

          
        sub interpret_commands  
        {  
                my $I=$_[0];  
                if ($I->{'commands'})  
                {  
                        my $cmd;  
                        my @commands=split (/[;:,.|]+/,$I->{'commands'});  
                        foreach $cmd (@commands)  
                        {  
                                #SET-Commands  
                                if ($cmd=~/^s*SET[_-]([a-z0-9_]+)(-([a-z0-9_]+))?(-([a-z0-9_]+))?(-([a-z0-9_]+))?(-([a-z0-9_]+))?(-([a-z0-9_]+))?(-([a-z0-9_]+))?(-([a-z0-9_]+))?(-([a-z0-9_]+))?(-([a-z0-9_]+))?\s*=\s*(.*?)\s*$/i)  
                                {  
                                        my $construct="\$I->";  
                                        my @parts=(lc($1),lc($3),lc($5),lc($7),lc($9),lc($11),lc($13),lc($15),lc($17),lc($19));  
                                        my $value=$20; $value=~s/(^["']|["']$)//g; $value=~s/"/\\"/g;  
                                        foreach $p (@parts)  
                                        {  
                                                if ($p=~/^\d+$/)                                        { $construct.="{".$p."}";       }  
                                                elsif ($p=~/^[a-z0-9_]+$/i)     { $construct.="{'".$p."'}";     }  
                                        }  
                                        eval($construct."=\"".$value."\"");  
                                }  
                                elsif ($cmd=~/SET/i)  
                                { print STDERR "\nInvalid command syntax: ".$cmd."\n"; }  
                        }  
                }  
        }  
        
        
        1. OK, konnte es jetzt mit eval lösen... keine Ahnung ob das die geschickteste Lösung ist, aber es funkntioniert:

          Ja, eval wäre eine Möglichkeit, eine andere wäre Rekursion. Mal ein kleines Beispielscript:

          use strict;  
          use Data::Dumper;  
            
          my $str = 'SET_FOO_BAR_BAZ_BUM=42';  
          my @hks = split /[=_]/, $str;  
          shift @hks;  
          my $ref = exec_cmd(\@hks);  
          print Dumper $ref;  
            
          sub exec_cmd {  
            my $ar = shift;  
            my $hr = shift || {};  
            my $key = shift @$ar;  
            $hr->{$key} = (@$ar == 1 ? $ar->[0] : {});  
            exec_cmd($ar, $hr->{$key}) if @$ar > 1;  
            return $hr;  
          }
          

          Was letztlich performanter ist, habe ich nicht getestet; perliger ist IMO meine Variante.

          Siechfred

          --
          Hinter den Kulissen passiert viel mehr, als man denkt, aber meistens nicht das, was man denkt.
        2. Hi Stefan,

          eval wäre mein erster Tip gewesen, für deine Herangehensweise(!)

          ... allerdings finde ich sie sicherheits und wartungstechnisch Problematisch:

          1. eval ist immer anfällig für Missbrauch wie Injections
          2. wie bekommt dein User mit, dass er sich vertippt hat?
          3. deine RegEx ist ein ziemlich überdimensioniertes schlecht wartbares Monster

          du hast einen Syntax
          1. CMD-PFAD=VALUE    [*]
          2. PFAD=KEY-KEY-KEY-...

          ich würde erst ersteres zerlegen und dann in einer Schleife den Pfad gesplittet durcharbeiten und jeden KEY auf Zulässigkeit überprüfen und eventuell Fehlermeldungen werfen.

          In der Schleife steigst du  den Hash-Baum runter du bekommt ja immer ne Hash_Ref als Zwischenergebnis, also

          $neu_href=$alt_href->{KEY}

          und wenn die Schleife fehlerfrei ducrhlaufen wurde und VALUE auch gültig ist, schreibst du

          $alt_href->{letzter KEY}=VALUE

          Gruß
            Kurt

          * CMD:PFAD=VALUE fänd ich besser.