Forum: PC-Programmierung FOR-Schleife (Python vs. C)


von Matthias (Gast)


Lesenswert?

Gegeben ist:
1
def scan_dir(dir):
2
   for name in os.listdir(dir):
3
        path = os.path.join(dir, name)
4
        if os.path.isfile(path):
5
            try:
6
                print(path)
7
            except:
8
                continue
9
10
        if os.path.isdir(path):
11
            try:
12
                scan_dir(path)
13
            except:
14
                continue
15
        
16
scan_dir("/usr/lib/ssl")

Problem:
Die Schleife läuft, bis sie auf ein Verzeichnis trifft bei dem ein 
'Permission denied' ausgelöst wird. In C würde ich das einfach mit einem 
i++ in der Schleife abfangen.

Frage, wie kann ich der obigen FOR-Schleife mitteilen, dass sie im 
Fehlerfall einfach mit dem nächsten Listenglied fortfahren soll? 
continue does not work.

von Matthias (Gast)


Lesenswert?

ergänzend:

Ich habe schon überlegt das mittels
1
len = os.listdir(dir)
2
und einer
3
 FOR-Range[::] Schleife zu machen

Würde vermutlich funktionieren, denke ich. Mich interessiert aber nach 
wie vor, ob es im obigen Bsp. eine Möglichkeit gäbe, einen Schritt in 
der Rekursion zu überspringen

von Spaminator (Gast)


Lesenswert?

Dein try/catch gilt nur für recursive Aufrufe von scan_dir. Benutz doch 
einfach os.walk.

von Sheeva P. (sheevaplug)


Lesenswert?

Matthias schrieb:
> Problem:
> Die Schleife läuft, bis sie auf ein Verzeichnis trifft bei dem ein
> 'Permission denied' ausgelöst wird.

Das sollte mit dem geposteten Code nicht passieren?!? Kannst Du Dein 
Problem vielleicht etwas genauer beschreiben?

von Matthias (Gast)


Lesenswert?

@Sheeva Plug

Code steht ja schon oben. Lasse ich try/except weg kommt es im 
Verzeichnis /usr/lib/ssl zu einem Fehler, nämlich, wenn es zu 'private' 
gelangt. Das ist ein link in die etc..., aber auch egal.

***Fehlermeldung***
for name in os.listdir(dir):
PermissionError: [Errno 13] Permission denied: '/usr/lib/ssl/private'

Kann man hinterher ja noch alles verfeinern, so dass auch Links und 
Berechtigungen mit einbezogen werden. Erst einmal geht es mir aber um 
etwas viel simpleres, nämlich wie ich einen Durchlauf in der 
FOR-Schleife überspringe.

>Das sollte mit dem geposteten Code nicht passieren?!?

except bewirkt im konkreten Fall, dass die Schleife an genau der 
besagten Stelle endet, was sie aber nicht soll.

von tictactoe (Gast)


Lesenswert?

Hab das jetzt mal ausprobiert. Bei mir funzt deine Funktion wie sie soll 
(ohne Fehlermeldung). Nur wenn ich sie direkt mit "/etc/ssl/private" 
aufrufe, kommt die von dir zitierte Fehlermeldung, was nicht verwundert, 
weil der äußerste Aufruf vom os.listdir nicht von einem try-except 
geschützt wird.

von Sheeva P. (sheevaplug)


Lesenswert?

Matthias schrieb:
> @Sheeva Plug
>
> Code steht ja schon oben. Lasse ich try/except weg kommt es im
> Verzeichnis /usr/lib/ssl zu einem Fehler, nämlich, wenn es zu 'private'
> gelangt. Das ist ein link in die etc..., aber auch egal.
>
> ***Fehlermeldung***
> for name in os.listdir(dir):
> PermissionError: [Errno 13] Permission denied: '/usr/lib/ssl/private'

Klar: ohne Exceptionhandling bricht der Code an der Stelle ab, wo Du ein 
Listing aus einem Verzeichnis lesen willst, welches Du nicht lesen 
darfst. Das ist ja auch vollkommen richtig so.

> Kann man hinterher ja noch alles verfeinern, so dass auch Links und
> Berechtigungen mit einbezogen werden.

Das wäre eher un-pythonisch. Klar kannst Du mit den Funktionen aus dem 
os-Modul die Permissions abfragen, wie man das in C/C++ und den meisten 
anderen Sprachen machen würde. Aber der Python-Weg ist hier ein anderer: 
probier's einfach aus und reagiere korrekt auf mögliche Exceptions. Das 
erfordert allerdings, die Exception an der richtigen Stelle zu fangen. 
Dein try/except ist dafür also genau das Richtige.

> Erst einmal geht es mir aber um
> etwas viel simpleres, nämlich wie ich einen Durchlauf in der
> FOR-Schleife überspringe.

Ich fürchte, das ist der große Irrtum: darum geht es gar nicht.

>>Das sollte mit dem geposteten Code nicht passieren?!?
>
> except bewirkt im konkreten Fall, dass die Schleife an genau der
> besagten Stelle endet, was sie aber nicht soll.

Hier bei mir bricht Dein Code nur ab, wenn ich für das oberste 
Verzeichnis -- also das, was beim ersten Aufruf von scan_dir() übergeben 
wird -- keine Leserechte habe -- kurz gesagt: Für 
scan_dir('/usr/lib/ssl') funktioniert Dein Code, weil ich Leserechte für 
/usr/lib/ssl/ habe, für scan_dir('/usr/lib/ssl/private/') wirft Dein 
Code eine Exception, weil ich darauf nicht lesen darf. Um diesen kleinen 
Fehler zu beheben, gehört der Aufruf von os.listdir() in den try-Block:
1
     1  import os, sys
2
     2
3
     3  def scan_dir(directory):
4
     4      try:
5
     5          for name in os.listdir(directory):
6
     6              path = os.path.join(directory, name)
7
     7              if os.path.isfile(path):
8
     8                  print(path)
9
     9              if os.path.isdir(path):
10
    10                  scan_dir(path)
11
    11      except OSError as e:
12
    12          pass
13
    13
14
    14  scan_dir(sys.argv[1])

Dieser Code fängt die Exception ab, wenn ich das oberste Verzeichnis 
nicht lesen kann. Das hat aber nichts damit zu tun, etwas in der 
For-Schleife zu überspringen, sondern nur damit, die Exception an der 
richtigen Stelle zu fangen, nämlich genau da, wo sie auftritt. Das 
"pass" im Exceptionhandler ignoriert die Exception dann einfach.

Wenn auch dies nicht das ist, was Du haben möchtest, dann habe ich Dein 
Problem noch nicht verstanden.

PS: Liebe Admin/Moderatoren, wären ein- und ausschaltbare Zeilennummern 
nicht eine nette Erweiterung für Codeblöcke?

von Mark B. (markbrandis)


Lesenswert?

Sheeva P. schrieb:
> PS: Liebe Admin/Moderatoren, wären ein- und ausschaltbare Zeilennummern
> nicht eine nette Erweiterung für Codeblöcke?

Längere Codestücke soll man ja ohnehin eher als Anhang posten. Aber wenn 
man es optional ein-/ausschalten kann... warum nicht.

Kannst es ja mal als Verbesserungsvorschlag posten. Dafür gibt es extra 
einen eigenen Thread: Beitrag "Wunschliste & Verbesserungsvorschläge"

: Bearbeitet durch User
von Matthias (Gast)


Lesenswert?

Sheeva Plug schrieb:

>Klar: ohne Exceptionhandling bricht der Code an der Stelle ab, wo Du ein
>Listing aus einem Verzeichnis lesen willst, welches Du nicht lesen
>darfst. Das ist ja auch vollkommen richtig so.

Richtig. Mein Fehler bestand darin, dass ich mich ausschließlich auf die 
innere Schleife konzentriert habe und mich gewundert habe, wieso die 
darin enthaltenen try/excepts mein 'Problem' mit den Leserechten nicht 
abfangen konnten.

>Das wäre eher un-pythonisch. Klar kannst Du mit den Funktionen aus dem
>os-Modul die Permissions abfragen, wie man das in C/C++ und den meisten
>anderen Sprachen machen würde.

Hierbei war der Hintergrund der, dass mir lediglich die 
Interpreter-Rückmeldung gezeigt hat, dass es ein solches Problem gibt 
und ich hier  ganz einfach eine Form des internen Debuggings einbaue 
wollte, indem ich auf diese mögliche Eigenschaft und in der Folge auch 
Fehlerursache selbst hin prüfe.

>Dieser Code fängt die Exception ab, wenn ich das oberste Verzeichnis
>nicht lesen kann. Das hat aber nichts damit zu tun, etwas in der
>For-Schleife zu überspringen, sondern nur damit, die Exception an der
>richtigen Stelle zu fangen, nämlich genau da, wo sie auftritt. Das
>"pass" im Exceptionhandler ignoriert die Exception dann einfach.

Mein Denkfehler lag darin, dass ich dass Pferd mehr oder weniger von 
hinten aufgezäumt?! habe, indem ich dachte, eben jenes Problem in dem 
Block abzufangen, in welchem ich die Funktion erneut aufrufe...
1
if os.path.isdir(path):
2
----->      try:
3
----->          scan_dir(path)
4
            except:
5
                continue

>Wenn auch dies nicht das ist, was Du haben möchtest, dann habe ich Dein
>Problem noch nicht verstanden.

Doch doch, dass Problem wurde verstanden und gelöst. Vielen Dank dafür!

von Matthias (Gast)


Lesenswert?

Ähm, kleine Verständnisfrage. Ich sehe gerade, dass Dein Code genau das 
selbe macht, wie der in meinem Eingangs-Post.

Statt einer Fehlermeldung, bricht die Rekursion im Falle von 
/usr/lib/ssl sauber bei private ab. Geplant war aber eigentlich, dass 
das Programm dann in das nächste auflistbare Verzeichnis geht und weiter 
rekursiert.

Mein usrpünglicher Gedanke war es, einmal durch den kompletten 
Verzeichnisbaum zu laufen. Das ich hier /usr/lib/ssl angegeben habe, war 
dem Umstand geschuldet, dass dort der 'Fehler' auftrat.

von Sheeva P. (sheevaplug)


Lesenswert?

Matthias schrieb:
> Ähm, kleine Verständnisfrage. Ich sehe gerade, dass Dein Code
> genau das
> selbe macht, wie der in meinem Eingangs-Post.
>
> Statt einer Fehlermeldung, bricht die Rekursion im Falle von
> /usr/lib/ssl sauber bei private ab. Geplant war aber eigentlich, dass
> das Programm dann in das nächste auflistbare Verzeichnis geht und weiter
> rekursiert.

Also bei mir bricht die Rekursion nicht ab, sondern macht mit dem 
nächsten Eintrag weiter. Wenn Du Dir eine Fehlermeldung wünschst, kannst 
Du vor dem "pass" einfach ein print() einfügen, und so die Exception 
(oder einen Text Deiner Wahl) ausgeben:
1
     1  import os, sys
2
     2
3
     3  def scan_dir(directory):
4
     4      try:
5
     5          for name in os.listdir(directory):
6
     6              path = os.path.join(directory, name)
7
     7              if os.path.isfile(path):
8
     8                  print(path)
9
     9              if os.path.isdir(path):
10
    10                  scan_dir(path)
11
    11      except OSError as e:
12
    12          print(str(e))
13
    13          pass
14
    14
15
    15  scan_dir(sys.argv[1])

von Sheeva P. (sheevaplug)


Lesenswert?

Mark B. schrieb:
> Sheeva P. schrieb:
>> PS: Liebe Admin/Moderatoren, wären ein- und ausschaltbare Zeilennummern
>> nicht eine nette Erweiterung für Codeblöcke?
>
> Längere Codestücke soll man ja ohnehin eher als Anhang posten. Aber wenn
> man es optional ein-/ausschalten kann... warum nicht.
>
> Kannst es ja mal als Verbesserungsvorschlag posten. Dafür gibt es extra
> einen eigenen Thread: Beitrag "Wunschliste & Verbesserungsvorschläge"

Hab' ich gemacht: Beitrag "Re: Wunschliste & Verbesserungsvorschläge"

Danke!

von tictactoe (Gast)


Lesenswert?

Sheeva P. schrieb:
> Also bei mir bricht die Rekursion nicht ab, sondern macht mit dem
> nächsten Eintrag weiter.

Stimmt schon. Aber dein Code macht nur weiter, wenn der Fehler in der 
Rekursion auftritt. Wenn aber ein Fehler bei isfile oder isdir auftritt, 
dann bricht dein Code vorzeitig ab.

von Sheeva P. (sheevaplug)


Lesenswert?

tictactoe schrieb:
> Sheeva P. schrieb:
>> Also bei mir bricht die Rekursion nicht ab, sondern macht mit dem
>> nächsten Eintrag weiter.
>
> Stimmt schon. Aber dein Code macht nur weiter, wenn der Fehler in der
> Rekursion auftritt. Wenn aber ein Fehler bei isfile oder isdir auftritt,
> dann bricht dein Code vorzeitig ab.

Wie könnten os.path.isfile() oder os.path.isdir() einen Fehler für einen 
Dateinamen produzieren, der doch gerade erst mit os.listdir() von System 
geholt worden ist? Parallele Zugriffe durch einen anderen Thread/Prozeß, 
meinetwegen, aber sonst?

von tictactoe (Gast)


Lesenswert?

Sheeva P. schrieb:
> Wie könnten os.path.isfile() oder os.path.isdir() einen Fehler für einen
> Dateinamen produzieren, der doch gerade erst mit os.listdir() von System
> geholt worden ist? Parallele Zugriffe durch einen anderen Thread/Prozeß,
> meinetwegen, aber sonst?

Nun, das ist ja schon eine Möglichkeit. Ganz oben in der Liste der 
Wahrscheinlichkeiten liegt ENOENT, weil die Datei zwischen Auslesen des 
Directories und dem Zugriff mittels isfile/isdir von einem anderen 
Prozess gelöscht worden ist.

von Sheeva P. (sheevaplug)


Lesenswert?

tictactoe schrieb:
> Sheeva P. schrieb:
>> Wie könnten os.path.isfile() oder os.path.isdir() einen Fehler für einen
>> Dateinamen produzieren, der doch gerade erst mit os.listdir() von System
>> geholt worden ist? Parallele Zugriffe durch einen anderen Thread/Prozeß,
>> meinetwegen, aber sonst?
>
> Nun, das ist ja schon eine Möglichkeit. Ganz oben in der Liste der
> Wahrscheinlichkeiten liegt ENOENT, weil die Datei zwischen Auslesen des
> Directories und dem Zugriff mittels isfile/isdir von einem anderen
> Prozess gelöscht worden ist.

Entschuldige, aber ENOENT (sowie ggf. ENOPERM) ist die von mir 
erwähnte Möglichkeit des parallelen Zugriffs durch einen anderen 
Thread/Prozeß. Mir fallen ansonsten nur sehr merkwürdige Fehler im 
Dateisystem (zum Beispiel eine fehlerhafte FUSE-Implementierung) ein.

Aber hier ging es ja nicht um konkurrierende Schreibzugriffe, sondern um 
ein konkretes Problem in einem konkreten Code, und bis zum Beweis des 
Gegenteils glaube ich, das Problem des OP gelöst und zu seiner 
Erleuchtung beigetragen zu haben. Mir war dabei leider nicht klar, daß 
ich angelegentlich auch noch alle anderen potentiell möglichen, äußerst 
unwahrscheinlichen Probleme der Welt, der Galaxie und des Universums 
hätte lösen sollen. ;-)

von Matthias (Gast)


Lesenswert?

Sheeva Plug schrieb:

>und bis zum Beweis des Gegenteils glaube ich, das Problem des OP gelöst und zu 
>seiner Erleuchtung beigetragen zu haben.

Als Beweis und Fürsprache meinerseits, könnte ich eine File meiner 
Standardausgabe hier anfügen, welche als Inhalt eine gänzlich rekursiv 
durchlaufene /dev/sda1, samt den hierauf befindlichen Dateien, 
Verzeichnissen, Links und entstandenen Fehlermeldungen abbildet.

Ich habe aber keinen an der Glocke, daher gebe ich der Gemeinschaft mein 
Ehrenwort, dass der sich im Laufe dieses Threads entwickelte Code mein 
Problem gelöst und zu der eingangs beschriebenen Magie geführt hat.

Weitere Fragestellungen waren nicht Gegenstand dieses Threads, aber 
aufgrund eines potentiell erwartbaren Popcornfaktors warne ich davor, 
diesen Thread als gelöst anzusehen.

Mal sehen, wohin uns das Ganze noch so führt. Ring frei!

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.