perl - Arrayabfragen wollen nicht funktionieren

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

perl - Arrayabfragen wollen nicht funktionieren

Beitrag von Duff » 04.11.2006 15:16:02

Hallo,

stehe im Moment irgendwie auf dem Schlauch...

Ich lasse mir über iwconfig eth2 scan alle wlan-Netze die gefunden werden ausgeben bzw. speicher diese in ein @array.

So nun will ich aber nicht alle diese Werte haben, sondern nur ein paar, die mich interessieren. Dies wollte ich dann mit substitute oder match machen.
Z.B. will ich nur den Namen der ESSID haben. Aber ich bekomme als Rückgabewert immer nur eine 1 für jedes Netz.

Zusätzlich wollte ich noch alle Leerzeilen (die ja nicht benötigt werden) im Array entfernen.
Oh, yeah!

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 04.11.2006 15:41:53

Open Source ist auch bei der Fehlersuche hilfreich :wink:

Gruß
gms

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 04.11.2006 15:44:24

...mmh...

Ich mache zum Beispiel folgendes:

Code: Alles auswählen

@wlan_networks=`iwlist eth2 scanning`;

foreach $k (@wlan_networks)
{
        if($essid = $k =~m /ESSID/)
        {print $essid, "\n";}
        #print $k, "\n";
}
Bei diesem einfachen match sollte doch eigentlich die essid ausgegeben werden oder habe ich einen totalen Denkfehler?

Es wird nämlich immer nur (bei 2 gefundenen Netzen) das hier ausgegeben:
Oh, yeah!

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 04.11.2006 15:58:25

Code: Alles auswählen

if($essid = $k =~m /ESSID/)
zuerst wird "$k=~ m/ESSID/" ausgewertet, das liefert als Ergebnis True (1) oder False (0) zurück. Diesen Wert speicherst du in $essid und falls $essid gleich True ist wird $essid, also 1, ausgegeben.

so sollte das funktionieren:

Code: Alles auswählen

if($k =~m /ESSID/) {
  print $k
}
Als Einzeiler geht das auch:

Code: Alles auswählen

print grep (/ESSID/, `iwlist eth2 scanning`);
Gruß
gms

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 04.11.2006 16:11:47

Danke.

Habe das ganze jetzt so gelöst (will nämlich so wenig wie möglich auf die unix eigene tools in meinem perl-script zurückgreifen).

Code: Alles auswählen

@wlan_networks=`iwlist eth2 scanning`;

foreach $k (@wlan_networks)
{
        if ($essid = $k =~ s/.*ESSID:"(.*)"/$1/){
                print $1, "\n";
        }
}
Habe dazu auch noch folgendes auf wikipedia gefunden:
Nach Verwendung eines Regulären Ausdruckes stehen folgende Sondervariablen zur Verfügung:

* $& – der erkannte String
* $’ – String vor dem erkanntem String
* $' – String nach dem erkanntem String
* $1..$9 – Ergebnisse der geklammerten Subausdrücke
* $+ – der letzte erkannt Subausdruck
* @- – Start-Offsets der Treffer und Subtreffer
* @+ – dazugehörige End-Offsets
Jetzt muss ich mir nur noch was überlegen, wie ich die ganzen Leerzeilen aus dem @array entfernt bekomme.
Oh, yeah!

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 04.11.2006 16:16:38

Code: Alles auswählen

        if ($essid = $k =~ s/.*ESSID:"(.*)"/$1/){
                print $1, "\n";
        }
du brauchst dazu keine Substitution, so funktioniert es auch:

Code: Alles auswählen

print $1 if ($k =~ /ESSID:\"(.*)\"/);"

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 04.11.2006 16:23:07

gms hat geschrieben: du brauchst dazu keine Substitution, so funktioniert es auch:
Stimmt, ein einfaches match reicht auch, weil ich eigentlich nichts ersetzen möchte.
Oh, yeah!

roli
Beiträge: 3174
Registriert: 10.09.2003 17:39:58

Beitrag von roli » 04.11.2006 18:14:51

Hi,
Duff hat geschrieben: Jetzt muss ich mir nur noch was überlegen, wie ich die ganzen Leerzeilen aus dem @array entfernt bekomme.

Code: Alles auswählen

@wlan_networks=`iwlist eth2 scanning`;

foreach $k (@wlan_networks)
{
        if (length($k)>0){
                if ($essid = $k =~ s/.*ESSID:"(.*)"/$1/){
                        print $1, "\n";
                }
        }
}
Roland


"Aber wenn du schon so unwissend bist, davon noch nicht gehört zu haben,
so will ich es doch als gut ansehen, daß du lieber einmal töricht fragst,
als weiterhin nichts von etwas zu wissen, das man doch wissen sollte."
aus "Die Edda des Snorri Sturluson", "Gylfis Täuschung"

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 04.11.2006 18:23:09

roli hat geschrieben:

Code: Alles auswählen

        if (length($k)>0){
                if ($essid = $k =~ s/.*ESSID:"(.*)"/$1/){
Die Abrage ob die Länge größer 0 ist, ist implicit auch in dem Match enthalten :wink:

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 04.11.2006 20:13:44

Das mit den Leerzeilen tritt mit den arrays gar nicht mehr auf. Die hätte ich ja vielleicht auch irgendwie mit splice aus den arrays entfernen können (glaube ich).

Hatte heute keine Zeit mehr, um das Script weiter schreiben zu können.

Bisher habe ich es so realisiert (bin aber natürlich noch nicht fertig).

Code: Alles auswählen

use strict;
use warnings;

my @wlan_networks;
my $k;
my @essid;
my @quality;

# Alle verfügbaren WLAN-Netze aufspüren
#
@wlan_networks=`iwlist eth2 scanning`;

foreach $k (@wlan_networks)
{
        # Nach den ESSIDs suchen
        if ($k =~ /.*ESSID:"(.*)"/){
                push(@essid, $1);
        }
        # Nach der Empfangsqualität suchen
        if ($k =~ /.*Quality.*(\d{2,3}\/\d{3}).*/){
                push(@quality, $1);
        }
}
print "@essid\n";
print "@quality\n";
Oh, yeah!

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 05.11.2006 12:02:53

Hallo,

habe mal wieder ein wenig weiter an meinem kleinen Script gebastelt. Habe nun die zwei array miteinander verglichen und bekomme dann ein array geliefert, indem nur die übereinstimmungen aus beiden array enthalten sind.

Doch ich würde das ganze gerne in einer Funtkion unterbringen. Doch ich bekomme es nicht so ganz hin. Es wird kein Wert zurück geliefert.

Code: Alles auswählen

# Konfigurierte Netzwerke suchen
# ssid in @array speichern
#
open (IN,"<","/etc/network/interfaces") or die "Kann die Datei /etc/network/interfaces nicht öffnen";
while (<IN>)
{
        if ($_ =~ /.*ssid\s+(.*)\s+/){
                push(@ssid, $1);
        }
}
close (IN);
print "@ssid\n";

#my (@x,@y);
#my (%hash);
#@x=@ssid;
#@y=@essid;
#push(@y,"Kein Wlan");
#print "@y\n";
#$hash{$_}++ for @x;
#my @equal = grep $hash{$_},@y;
#print "@equal\n";

sub array_compare {
        my (@x,@y,@z);
        my %hash;
        push(@y,"Kein Wlan");
        print "@y\n";
        $hash{$_}++ for @x;
        @z=grep $hash{$_},@y;
        return @z;
}
my @equal;
@equal=array_compare(@ssid,@essid);
print "----------\n";
print "@equal\n";
Mit den Einkommentierten Zeilen funktioniert es. Nur nicht, wenn ich es in einer Funktion unterbringen möchte. Weiß auch nicht so genau, wie ich die Funktion aufrufen muss.
Oh, yeah!

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 05.11.2006 12:07:09

Habe es jetzt so gelöst, indem ich den beiden Array @x und @y in der Funktion die beiden Arrays @ssid und @essid zugewiesen habe (dachte aber, dass ich das nicht noch mal expliziet muss). Naja.

Code: Alles auswählen

sub array_compare {
        my (@x,@y,@z);
        @x=@ssid;
        @y=@essid;
        my %hash;
        $hash{$_}++ for @x;
        @z=grep $hash{$_},@y;
        return @z;
}
my @equal;
@equal=array_compare(@ssid,@essid);
print "----------\n";
print "@equal\n";
Oh, yeah!

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 05.11.2006 16:57:35

Das Handling der Parameter funktioniert in Perl über Arrays, daher können in Perl auch nicht mehrere Array an eine Funktion übergeben werden.
In der "arrayfunc2" werden daher Array Referenzen verwendet:

Code: Alles auswählen

gms@gms1:~$ cat x.pl
#!/usr/bin/perl

my @a = ( "a","aa","aaa" );
my @b = ( "b","bb","bbb" );

# falsch
sub arrayfunc1(@) {
  my (@x,@y)=@_;
  print "func1: array1: @x\n";
  print "func1: array2: y=@y\n";
}

# richtig
sub arrayfunc2($$) {
  my ($xref,$yref)=@_;
  print "func2: array1: @$xref\n";
  print "func2: array2: @$yref\n";
}

arrayfunc1(@a,@b);
arrayfunc2(\@a,\@b);
gms@gms1:~$
gms@gms1:~$ ./x.pl
func1: array1: a aa aaa b bb bbb
func1: array2: y=
func2: array1: a aa aaa
func2: array2: b bb bbb
Gruß
gms

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 05.11.2006 19:04:05

Aber es funktioniert doch bei mir.
Oh, yeah!

gms
Beiträge: 7798
Registriert: 26.11.2004 20:08:38
Lizenz eigener Beiträge: MIT Lizenz

Beitrag von gms » 05.11.2006 20:36:20

Duff hat geschrieben:Habe es jetzt so gelöst, indem ich den beiden Array @x und @y in der Funktion die beiden Arrays @ssid und @essid zugewiesen habe (dachte aber, dass ich das nicht noch mal expliziet muss). Naja.
Das hat sich so angehört als wärst du unzufrieden mit deiner Lösung.

die Parameter kannst du bei deiner Lösung übrigens auch weglassen:

Code: Alles auswählen

@equal=array_compare(@ssid,@essid);
sie werden ja nicht verwendet:

Code: Alles auswählen

sub array_compare {
        my (@x,@y,@z);
        @x=@ssid;
        @y=@essid;
Gruß
gms

nepos
Beiträge: 5238
Registriert: 05.01.2005 10:08:12

Beitrag von nepos » 06.11.2006 09:38:39

Wenn du ganze Arrays so uebergeben willst, dann musst du das ueber Referenzen machen und diese dann via shift aus dem speziellen Array @_ holen, in den Perl die Parameter packt.
Was du machst ist, dass du einfach die "globalen" Variablen in der Sub benutzt.

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 06.11.2006 10:05:14

Wie genau müsste ich das denn auf mein Beispiel bezogen machen?

Will es ja richtig machen und nicht falsch (so wie ich es ja jetzt gelöst habe).
Oh, yeah!

nepos
Beiträge: 5238
Registriert: 05.01.2005 10:08:12

Beitrag von nepos » 06.11.2006 10:44:05

Das sollte dann in etwa so gehen:

Code: Alles auswählen

sub array_compare {
        my @z;
        my $x_ref=shift;
        my $y_ref=shift;
        my %hash;
        $hash{$_}++ for @{$x_ref};
        @z=grep $hash{$_}, @{$y_ref};
        return @z;
}
my @equal;
@equal=array_compare(\@ssid,\@essid);
print "----------\n";
print "@equal\n"; 

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 06.11.2006 16:47:51

Danke, aber ich verstehe dass ganze irgendwie nicht so ganz.

Die beiden Zeilen

Code: Alles auswählen

        my $x_ref=shift;
        my $y_ref=shift;
vorallem nicht. Wieso muss ich dem Array was wegnehmen? Was wird denn überhaupt genau weggenommen?

Eine arrayreferenz zeigt doch auf das ganze array oder nicht?
Oh, yeah!

ToPeG
Beiträge: 437
Registriert: 14.04.2004 00:42:06

Beitrag von ToPeG » 07.11.2006 06:23:17

Das ist die Parmetterübergabe an die Funktion.
Alternativ kann man auch schreiben:

Code: Alles auswählen

my $x_ref=shift(@_);
my $y_ref=shift(@_);
oder:

Code: Alles auswählen

my ($x_ref,$y_ref)=@_;
oder

Code: Alles auswählen

my $x_ref=$_[0];
my $y_ref=$_[1];
Wenn eine Funtion mit Parametteren aufgerufen werden stehen diese in "@_" der funktion zur Verfügung.
Perl ergänzt immer "@_" oder "$_" wenn Parametter gebraucht werden aber keien angeben sind.

nepos
Beiträge: 5238
Registriert: 05.01.2005 10:08:12

Beitrag von nepos » 07.11.2006 09:41:38

Stimmt, haette ich vielleicht erwaehnen sollen.
Perl belegt an vielen Stellen $_ und @_ mit Werten, wenn man nichts anderes angibt.
Wenn du mehr ueber eigene Funktionen erfahren willst, solltest du dir auch mal

Code: Alles auswählen

perldoc perlsub
durchlesen.
Da wird einiges rund um Subs erklaert.

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 07.11.2006 11:04:16

nepos hat geschrieben: Wenn du mehr ueber eigene Funktionen erfahren willst, solltest du dir auch mal

Code: Alles auswählen

perldoc perlsub
durchlesen.
Da wird einiges rund um Subs erklaert.
Danke, werde ich machen. Muss nämlich irgendwie noch mal die Zusammenhänge von Referenzen, Hashes und Arrays durchlesen, da noch immer nicht ganz verstehe, wieso man bei einer Referenz auf ein Array, ein shift braucht.
Oh, yeah!

nepos
Beiträge: 5238
Registriert: 05.01.2005 10:08:12

Beitrag von nepos » 07.11.2006 11:14:49

Nein, das hat nix damit zu tun, dass das ne Referenz ist.
Wenn du eine Funktion aufrufst, dann landen alle Parameter in @_.
Innerhalb der Funktion kommst du ueber diesen Array dran.
Wenn du allerdings sowas machst

Code: Alles auswählen

mySub(@foo,@bar);
dann hast du in @_ alle Werte von @foo und @bar stehen. Wie willst du die dann wieder auseinanderklamuesern?
Deshalb musst du in dem Fall statt der Arrays direkt deren Referenzen uebergeben!
In @_ stehen dann nur die Referenzen, die du mit shift in lokale Variablen packen kannst.
Das ganze findest du in der perlsub-Manpage unter "Pass by Reference".

Benutzeravatar
Duff
Beiträge: 6321
Registriert: 22.03.2005 14:36:03
Wohnort: /home/duff

Beitrag von Duff » 07.11.2006 12:22:46

Danke. Habe mir das ganze mal an folgendem Beispiel angeschaut:

Code: Alles auswählen

@a=(1,2,3);
@b=(4,5,6);
@c=(7,8);
@d=(9,10);

@tailings = popmany ( \@a, \@b, \@c, \@d );

sub popmany {
        my $aref;
        my @retlist = ();
        foreach $aref ( @_ ) {
                push @retlist, pop @$aref;
        }
        return @retlist;
}

print "@tailings\n";
Das Ergebnis dieses Scriptes lautet: 3 6 8 10

Was wird gemacht?
Das array @a wird durch \@a als Referenz dargestellt, so dass @a über einen "Zeiger/eine bestimmte Speicheradresse" angesprochen werden kann.

Diese ganzen "Speicheradressen/Zeiger werden dann in der Funktion in der Standardvariablen @_ gespeichert.

Für jede "Speicheradresse/Zeiger" in diesem Array wird dann über die foreach-Schleife ein bestimmter Wert in das neue Array @retlist gespeichert. Der Wert, der in das Array @retlist gespeichert werden soll, wird mit pop aus dem @$aref genommen (So wird über die Speicheradresse/Zeiger auf den Inhalt des eigentlichen Arrays zugegriffen).

Es werden so alle angegebenen Arrays/Arrayreferenzen durchlaufen.


Habe ich das ganze nun endlich richtig verstanden?
Hoffe, man kann meiner Erklärung folgen :-)
Oh, yeah!

nepos
Beiträge: 5238
Registriert: 05.01.2005 10:08:12

Beitrag von nepos » 07.11.2006 12:55:50

Jo, das hoert sich denke ich ganz ok an.
Innerhalb von popmany() sieht @_ so aus: :

Code: Alles auswählen

[[1,2,3],[4,5,6],[7,8],[9,10]]
Bei jedem Durchlauf der foreach-Schleife holt sich das Skript eine der 3 Referenzen:
1. [1,2,3]
2. [4,5,6]
3. [7,8]
4. [9,10]
Die push-Zeile derefenziert dann $aref mittels @$aref um mittels pop den letzten Wert aus dem Array zu holen:
1. 3
2. 6
3. 8
4. 10
Diese Werte werden in der obigen Reihenfolge dann in @retlist abgelegt:

Code: Alles auswählen

[3,6,8,10]
und dieser Array wird dann zurueckgegeben.

Antworten