Frage zu glob

Frage zu glob

am 24.04.2007 09:54:03 von Ferry Bolhar

Hallo,

ich habe ein (für mich!) merkwürdiges Verhalten mit glob() entdeckt.
Kann aber auch gut sein, dass ich die Funktion nur falsch verwende.

Zunächst die Aufgabenstellung: ich habe zwei Verzeichnisse, nennen
wir sie 'char' und 'stat'. Beide Verzeichnisse enthalten Dateien, wobei
mich die in 'stat' tatsächlich interessieren, ich aber deren Namen nicht
kenne. Es sind jedoch nicht alle Dateien in 'stat' für mich von Interesse,
sondern nur jene, deren Namen auch in 'char' aufscheinen. Während
die Dateien in 'char' einen fixen Namen haben, haben die in 'stat' nach
dem fixen Namen, durch ein '#' getrennt, noch eine variable Kennung.
Von jeder Datei gibt es nur eine Version, d.h., gibt es eine Datei in
'char', muss es auch genau eine solche in 'stat' geben.

Ich muss also 'char' Datei für Datei durchlesen und für alle darin
aufscheinenenden Dateien in 'stat' nach einer solchen Datei suchen.
Da ich den vollständigen Name der Datei in 'stat' nicht kenne, muss
ich ihn erst ermitteln.

Um diese Aufgabe zu lösen, muss ich also zweimal 'glob' verwenden:
einmal, um 'char' durchzuarbeiten, und ein zweites mal, um aus dem
Namen aus 'char' den Namen der Datei in 'stat' zu bekommen, damit
ich diese dann schlussendlich verarbeiten kann.

Mein Code sieht so aus:

use strict;
use warnings;

my $char_dir = '/usr/patrol/WEBCheck/char';
my $stat_dir = '/usr/patrol/WEBCheck/stat';
my $char_pattern = $char_dir . '/*';

while (glob $char_pattern) {
s!.+/!!;
my $stat_pattern = $stat_dir . "/$_#*"; # Nach dem '#' beginnt die
variable ID
my $file = glob $stat_pattern;
verarbeite($file);
}

Lasse ich nun die Schleife durchlaufen, klappt es beim ersten
Durchlauf wunderbar. Beim zweiten Mal jedoch enthält $file "undef",
obwohl eine Datei, deren Name auf das Pattern in $stat_pattern
passt, natürlich existiert.

Was ist da falsch? Wie handelt Perl mehrere gleichzeitige globs()
intern ab? Oder ist diese Verwendung von glob() nicht unterstützt?
Kann es sein, dass die beiden globs() einander beeinflussen (wie
es z.B. bei alarm() ist; es kann ja auch immer nur ein alarm() "offen"
sein, ein zweites alarm() überschreibt das erste)?

Danke und schöne Grüße aus Wien,

Ferry

--
Ing Ferry Bolhar
Magistrat der Stadt Wien - MA 14
A-1010 Wien
E-Mail: bol@adv.magwien.gv.at

Re: Frage zu glob

am 24.04.2007 10:29:25 von Ingo Menger

On 24 Apr., 09:54, "Ferry Bolhar" wrote:
> Hallo,
>
> ich habe ein (für mich!) merkwürdiges Verhalten mit glob() entdeckt.
> Kann aber auch gut sein, dass ich die Funktion nur falsch verwende.

Ohne jetzt perldoc bemüht zu haben, aber erwartest Du hier,

> while (glob $char_pattern) {

daß Perl die Liste der passenden Dateien durchgeht?
Vielleicht ersetzt Du das besser durch

for (glob $char_pattern) {

Re: Frage zu glob

am 24.04.2007 10:47:17 von Frank Seitz

Ferry Bolhar wrote:

> Was ist da falsch? Wie handelt Perl mehrere gleichzeitige globs()
> intern ab? Oder ist diese Verwendung von glob() nicht unterstützt?
> Kann es sein, dass die beiden globs() einander beeinflussen

Letzeres ist zu befürchten. Du bist auf der sicheren Seite, wenn
Du glob() ausschließlich im Arraykontext nutzt, auch wenn
Du nur einen Treffer erwartest, à la

$file = (glob $pat)[0];

Grüße
Frank
--
Dipl.-Inform. Frank Seitz; http://www.fseitz.de/
Anwendungen für Ihr Internet und Intranet
Tel: 04103/180301; Fax: -02; Industriestr. 31, 22880 Wedel

Re: Frage zu glob

am 24.04.2007 13:24:27 von Ferry Bolhar

Ingo Menger:

> Ohne jetzt perldoc bemüht zu haben, aber erwartest Du hier,
>
>> while (glob $char_pattern) {
>
> daß Perl die Liste der passenden Dateien durchgeht?

Ja. Und das passiert ja auch. Das ist die Kurzform für

while (defined($_ = glob $char_pattern))

und das funktioniert. Jedesmal, wenn du glob() aufrufst,
bekommst du den nächsten passenden Dateinamen zurück,
bis am Ende 'undef' zurückgegeben wird. glob() hat
offensichtlich "intern" einen Zeiger, der ihm sagt, wo es
gerade steht und welcher Name als Nächster auszugeben
ist. Das funktioniert ähnlich wie mit einer regex mit dem 'g'
Modifier, die im skalaren Kontext ausgewertet wird - man
bekommt immer den nächsten Match und am Ende 'undef'
zurück.

Mein Vermutung war ja, dass eben dieser "interne Zeiger"
durcheinanderkommt, wenn zwei glob()'s gleichzeitig
verwendet werden. Leider ist das nirgendwo dokumentiert.

> Vielleicht ersetzt Du das besser durch
>
> for (glob $char_pattern) {

Das ändert am Verhalten nichts.

LG, Ferry

--
Ing Ferry Bolhar
Magistrat der Stadt Wien - MA 14
A-1010 Wien
E-Mail: bol@adv.magwien.gv.at

Re: Frage zu glob

am 24.04.2007 13:30:44 von Ferry Bolhar

Frank Seitz:

> Letzeres ist zu befürchten. Du bist auf der sicheren Seite, wenn
> Du glob() ausschließlich im Arraykontext nutzt, auch wenn
> Du nur einen Treffer erwartest, à la
>
> $file = (glob $pat)[0];

Hast du es ausprobiert? Bei mir hat sich dadurch nichts geändert.

LG, Ferry

--
Ing Ferry Bolhar
Magistrat der Stadt Wien - MA 14
A-1010 Wien
E-Mail: bol@adv.magwien.gv.at

Re: Frage zu glob

am 24.04.2007 13:36:55 von Frank Seitz

Ferry Bolhar wrote:
> Frank Seitz:
>>
>>Letzeres ist zu befürchten. Du bist auf der sicheren Seite, wenn
>>Du glob() ausschließlich im Arraykontext nutzt, auch wenn
>>Du nur einen Treffer erwartest, à la
>>
>> $file = (glob $pat)[0];
>
> Hast du es ausprobiert?

Nur diesen Ausdruck, nicht Deine Schleife.

> Bei mir hat sich dadurch nichts geändert.

Hast Du die Schleife auch auf Arraykontext umgestellt?

Grüße
Frank
--
Dipl.-Inform. Frank Seitz; http://www.fseitz.de/
Anwendungen für Ihr Internet und Intranet
Tel: 04103/180301; Fax: -02; Industriestr. 31, 22880 Wedel

Re: Frage zu glob

am 24.04.2007 14:05:43 von Christian Winter

Ferry Bolhar schrieb:
> Ingo Menger:
>
>> Ohne jetzt perldoc bemüht zu haben, aber erwartest Du hier,
>>
>>> while (glob $char_pattern) {
>> daß Perl die Liste der passenden Dateien durchgeht?
>
> Ja. Und das passiert ja auch. Das ist die Kurzform für
>
> while (defined($_ = glob $char_pattern))
>
> und das funktioniert. Jedesmal, wenn du glob() aufrufst,
> bekommst du den nächsten passenden Dateinamen zurück,
> bis am Ende 'undef' zurückgegeben wird. glob() hat
> offensichtlich "intern" einen Zeiger, der ihm sagt, wo es
> gerade steht und welcher Name als Nächster auszugeben
> ist. Das funktioniert ähnlich wie mit einer regex mit dem 'g'
> Modifier, die im skalaren Kontext ausgewertet wird - man
> bekommt immer den nächsten Match und am Ende 'undef'
> zurück.
>
> Mein Vermutung war ja, dass eben dieser "interne Zeiger"
> durcheinanderkommt, wenn zwei glob()'s gleichzeitig
> verwendet werden. Leider ist das nirgendwo dokumentiert.

s/nirgendwo/ziemlich versteckt/;

Zu finden ist das ganze in perlop unter "I/O Operators":

[schnipp]
A (file)glob evaluates its (embedded) argument only when it is starting
a new list. All values must be read before it will start over. In list
context, this isn't important because you automatically get them all
anyway. However, in scalar context the operator returns the next value
each time it's called, or "undef" when the list has run out. As with
filehandle reads, an automatic "defined" is generated when the glob
occurs in the test part of a "while", because legal glob returns (e.g. a
file called 0) would otherwise terminate the loop. Again, "undef" is
returned only once. So if you're expecting a single value from a glob,
it is much better to say

($file) = ;

than

$file = ;

because the latter will alternate between returning a filename and
returning false.
[schnapp]

Keine Ahnung warum perlfunc, File::Glob und perlfaq5 sich dazu so
ausschweigen.

-Christian

Re: Frage zu glob

am 24.04.2007 17:47:32 von Ingo Menger

On 24 Apr., 13:24, "Ferry Bolhar" wrote:
> Ingo Menger:

> > Vielleicht ersetzt Du das besser durch
>
> > for (glob $char_pattern) {
>
> Das ändert am Verhalten nichts.

Das wundert mich jetzt wirklich, denn auf die Weise sollte er
eigentlich den "äußeren" glob Aufruf auf einmal und endgültig
abgearbeitet haben, wg. des Array-Kontexts.

Re: Frage zu glob

am 24.04.2007 20:59:12 von Frank Seitz

Ingo Menger wrote:
> On 24 Apr., 13:24, "Ferry Bolhar" wrote:
>>Ingo Menger:
>>>
>>>Vielleicht ersetzt Du das besser durch
>>>
>>> for (glob $char_pattern) {
>>
>>Das ändert am Verhalten nichts.
>
> Das wundert mich jetzt wirklich, denn auf die Weise sollte er
> eigentlich den "äußeren" glob Aufruf auf einmal und endgültig
> abgearbeitet haben, wg. des Array-Kontexts.

So ist es auch. Der andere Aufruf ist der problematische.

Grüße
Frank
--
Dipl.-Inform. Frank Seitz; http://www.fseitz.de/
Anwendungen für Ihr Internet und Intranet
Tel: 04103/180301; Fax: -02; Industriestr. 31, 22880 Wedel

Re: Frage zu glob

am 25.04.2007 13:40:08 von Ferry Bolhar

Christian Winter:

> Zu finden ist das ganze in perlop unter "I/O Operators":
>
> [schnipp]
>
> So if you're expecting a single value from a glob,
> it is much better to say
>
> ($file) = ;
>
> than
>
> $file = ;
>
> because the latter will alternate between returning a filename and
> returning false.

Danke, Christian. Genau das war's.

> Keine Ahnung warum perlfunc, File::Glob und perlfaq5 sich dazu so
> ausschweigen.

Ja, vor allem in perlfunc hätte ich das erwartet. Dann hätte ich mir
mein Posting sparen können.

Um zu einem Ende zu kommen, der Code sieht jetzt wie folgt aus:

use strict;
use warnings;

my $char_dir = '/usr/patrol/WEBCheck/char';
my $stat_dir = '/usr/patrol/WEBCheck/stat';
my $char_pattern = $char_dir . '/*';

my @files = glob $char_pattern;

foreach (@files) {
s!.+/!!;
my $stat_pattern = $stat_dir . "/$_#*";
my ($file) = glob $stat_pattern;
verarbeite($file);
}

Und das haut jetzt hin.

Manchmal denke ich, was perldoc fehlt, ist ein gescheiter Index.
Ich gebe zu, dass ich nicht daran gedacht habe, für Informationen
über glob() in perlop nachzusehen...

Nochmals danke an alle und LG

Ferry

--
Ing Ferry Bolhar
Magistrat der Stadt Wien - MA 14
A-1010 Wien
E-Mail: bol@adv.magwien.gv.at

Re: Frage zu glob

am 25.04.2007 13:49:11 von Ferry Bolhar

Frank Seitz:

> Hast Du die Schleife auch auf Arraykontext umgestellt?

Als ich deine Änderung ausprobiert habe, noch nicht (da waren
die Postings der anderen scheinbar noch nicht durch). Jetzt
funktioniert's - man muss den ersten glob() komplett "aussaugen":

my @files = glob $char_pattern;

_UND_ beim zweiten auch einen Listcontext, obwohl nur ein
Name erwartet wird, bereitstellen:

my ($file) = glob $stat_pattern;

Das von glob() _immer_ zuletzt zurückgelieferte "undef" wird
durch den Listkontext ebenfalls geholt (und sofort wieder
verworfen), wodurch glob() für das nächste Matching wieder
bereit ist.

Daraus folgt natürlich, dass mehrere globs() parallel _nicht_
verwendet werden können; man muss die Resultate eben in
Arrays zwischenspeichern. Kein Problem, man muss es nur
wissen!

Ach wäre das schön, wenn das so in der Beschreibung von
perldoc -f glob beschrieben wäre!

Vielen Dank für deine Hilfe!

Manchmal frage ich mich, wieso immer ich über solche Fallen
stolpere... ;-)

LG, Ferry

--
Ing Ferry Bolhar
Magistrat der Stadt Wien - MA 14
A-1010 Wien
E-Mail: bol@adv.magwien.gv.at

Re: Frage zu glob

am 26.04.2007 12:58:10 von Frank Seitz

Ferry Bolhar wrote:
>
> Manchmal frage ich mich, wieso immer ich über solche Fallen
> stolpere... ;-)

Ich war kürzlich in eine ähnliche Falle getappt.
Daher wusste ich, dass der Skalarkontext von glob()
mit Vorsicht zu geniessen ist, vor allem, wenn man nicht
vor hat zu iterieren.

Grüße
Frank
--
Dipl.-Inform. Frank Seitz; http://www.fseitz.de/
Anwendungen für Ihr Internet und Intranet
Tel: 04103/180301; Fax: -02; Industriestr. 31, 22880 Wedel

Re: Frage zu glob

am 27.04.2007 01:50:26 von Thomas Jahns

"Ferry Bolhar" writes:
> Um diese Aufgabe zu lösen, muss ich also zweimal 'glob' verwenden:
> einmal, um 'char' durchzuarbeiten, und ein zweites mal, um aus dem
> Namen aus 'char' den Namen der Datei in 'stat' zu bekommen, damit
> ich diese dann schlussendlich verarbeiten kann.
>
> Mein Code sieht so aus:
>
> use strict;
> use warnings;
>
> my $char_dir = '/usr/patrol/WEBCheck/char';
> my $stat_dir = '/usr/patrol/WEBCheck/stat';
> my $char_pattern = $char_dir . '/*';
>
> while (glob $char_pattern) {
> s!.+/!!;
> my $stat_pattern = $stat_dir . "/$_#*"; # Nach dem '#' beginnt die
> variable ID
> my $file = glob $stat_pattern;
> verarbeite($file);
> }

Ungeachtet Deiner Probleme bezüglich des doppelten glob-Aufrufs, wäre es
vielleicht ganz sinnvoll wenn das pattern eh "*" ist einfach readdir zu
nehmen?

use strict;
use warnings;
use File::Spec;

my $char_dir = '/usr/patrol/WEBCheck/char';
my $stat_dir = '/usr/patrol/WEBCheck/stat';

opendir CHARDIR, $char_dir or die("Open directory failed for ", $char_dir);
while ($_ = readdir(CHARDIR)) {
next if($_ eq File::Spec->updir() or $_ eq File::Spec->curdir());
s!.+/!!;
my $stat_pattern = $stat_dir . "/$_#*"; # Nach dem '#' beginnt die variable ID
my $file = glob $stat_pattern;
verarbeite($file);
}


Thomas Jahns
--
"Computers are good at following instructions,
but not at reading your mind."
D. E. Knuth, The TeXbook, Addison-Wesley 1984, 1986, 1996, p. 9