Impossible de localiser les fichiers avec des noms longs sur Windows avec Python
je dois parcourir les dossiers avec des noms de fichiers longs dans Windows.
j'ai essayé en utilisant os.listdir()
, mais il s'écrase avec de longs noms de chemins, ce qui est mauvais.
j'ai essayé en utilisant os.walk()
, mais il ignore les noms de chemins plus longs que ~256, ce qui est pire.
j'ai essayé le mot magique contournement décrit ici, mais il ne fonctionne qu'avec les lecteurs mappés, pas avec chemins d'accès UNC.
Voici un exemple avec manches pathnames, qui montre que pathnames UNC ne fonctionne pas avec le tour du mot magique.
>>> os.listdir('c:drivers')
['nusb3hub.cat', 'nusb3hub.inf', 'nusb3hub.sys', 'nusb3xhc.cat', 'nusb3xhc.inf', 'nusb3xhc.sys']
>>> os.listdir('\Uni-hq-srv6router')
['2009-04-0210', '2010-11-0909', ... ]
>>> mw=u'\?'
>>> os.listdir(mw+'c:drivers')
[u'nusb3hub.cat', u'nusb3hub.inf', u'nusb3hub.sys', u'nusb3xhc.cat', u'nusb3xhc.inf', u'nusb3xhc.sys']
>>> os.listdir(mw+'\Uni-hq-srv6router')
Traceback (most recent call last):
File "<pyshell#160>", line 1, in <module>
os.listdir(mw+'\Uni-hq-srv6router')
WindowsError: [Error 123] The filename, directory name, or volume label syntax is incorrect: u'\?\Uni-hq-srv6router*.*'
avez-vous une idée de la façon de gérer les noms de chemins longs ou unicode UNC pathnames?
Edit:
suite à la suggestion des commentaires ci-dessous, j'ai créé quelques fonctions de test pour comparer Python 2.7 et 3.3, et j'ai ajouté le test de glob.glob
et os.listdir
après os.chdir
.
os.chdir
n'a pas aidé comme prévu (voir ceci ).
glob.glob
est le seul qui fonctionne mieux en Python 3.3, mais à une seule condition: utiliser le mot magique et le nom du lecteur.
voici le code que j'ai utilisé (il fonctionne à la fois sur 2.7 et 3.3). J'apprends le Python maintenant, et j'espère que ces tests auront du sens:
from __future__ import print_function
import os, glob
mw = u'\?'
def walk(root):
n = 0
for root, dirs, files in os.walk(root):
n += len(files)
return n
def walk_mw(root):
n = 0
for root, dirs, files in os.walk(mw + root):
n += len(files)
return n
def listdir(root):
try:
folders = [f for f in os.listdir(root) if os.path.isdir(os.path.join(root, f))]
files = [f for f in os.listdir(root) if os.path.isfile(os.path.join(root, f))]
n = len(files)
for f in folders:
n += listdir(os.path.join(root, f))
return n
except:
return 'Crash'
def listdir_mw(root):
if not root.startswith(mw):
root = mw + root
try:
folders = [f for f in os.listdir(root) if os.path.isdir(os.path.join(root, f))]
files = [f for f in os.listdir(root) if os.path.isfile(os.path.join(root, f))]
n = len(files)
for f in folders:
n += listdir_mw(os.path.join(root, f))
return n
except:
return 'Crash'
def listdir_cd(root):
try:
os.chdir(root)
folders = [f for f in os.listdir('.') if os.path.isdir(os.path.join(f))]
files = [f for f in os.listdir('.') if os.path.isfile(os.path.join(f))]
n = len(files)
for f in folders:
n += listdir_cd(f)
return n
except:
return 'Crash'
def listdir_mw_cd(root):
if not root.startswith(mw):
root = mw + root
try:
os.chdir(root)
folders = [f for f in os.listdir('.') if os.path.isdir(os.path.join(f))]
files = [f for f in os.listdir('.') if os.path.isfile(os.path.join(f))]
n = len(files)
for f in folders:
n += listdir_cd(f) # the magic word can only be added the first time
return n
except:
return 'Crash'
def glb(root):
folders = [f for f in glob.glob(root + '*') if os.path.isdir(os.path.join(root, f))]
files = [f for f in glob.glob(root + '*') if os.path.isfile(os.path.join(root, f))]
n = len(files)
for f in folders:
n += glb(os.path.join(root, f))
return n
def glb_mw(root):
if not root.startswith(mw):
root = mw + root
folders = [f for f in glob.glob(root + '*') if os.path.isdir(os.path.join(root, f))]
files = [f for f in glob.glob(root + '*') if os.path.isfile(os.path.join(root, f))]
n = len(files)
for f in folders:
n += glb_mw(os.path.join(root, f))
return n
def test():
for txt1, root in [('drive ', r'C:test'),
('UNC ', r'Uni-hq-srv6routertest')]:
for txt2, func in [('walk ', walk),
('walk magic word ', walk_mw),
('listdir ', listdir),
('listdir magic word ', listdir_mw),
('listdir cd ', listdir_cd),
('listdir magic word cd ', listdir_mw_cd),
('glob ', glb),
('glob magic word ', glb_mw)]:
print(txt1, txt2, func(root))
test()
Et voici le résultat:
- Le nombre 8 signifie que tous les fichiers ont été trouvés
- le chiffre 0 signifie qu'il n'a même pas essayé sans s'écraser
- N'importe quel nombre entre 1 et 7 signifie qu'il a échoué à mi-chemin sans s'écraser
- Le mot
Crash
signifie qu'il s'est écrasé
-
Python 2.7
drive walk 5
drive walk magic word 8 * GOOD *
drive listdir Crash
drive listdir magic word 8 * GOOD *
drive listdir cd Crash
drive listdir magic word cd 5
drive glob 5
drive glob magic word 0
UNC walk 6
UNC walk magic word 0
UNC listdir 5
UNC listdir magic word Crash
UNC listdir cd 5
UNC listdir magic word cd Crash
UNC glob 5
UNC glob magic word 0
Python 3.3
drive walk 5
drive walk magic word 8 * GOOD *
drive listdir Crash
drive listdir magic word 8 * GOOD *
drive listdir cd Crash
drive listdir magic word cd 5
drive glob 5
drive glob magic word 8 * GOOD *
UNC walk 6
UNC walk magic word 0
UNC listdir 5
UNC listdir magic word Crash
UNC listdir cd 5
UNC listdir magic word cd Crash
UNC glob 5
UNC glob magic word 0
3 réponses
Utilisez la fonction de repli 8.3 Pour éviter le nom de chemin long, la navigation dans l'Explorateur Win7 cela semble être ce que windows fait lui-même, c'est-à-dire que chaque chemin long a un 'vrai nom'plus court:
>>> long_unc="\\K53\Users\Tolan\testing\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\xxxxxxxxxxxxxxxxxxxxxxxxdddddddddddddddddddddwgggggggggggggggggggggggggggggggggggxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\esssssssssssssssssssssggggggggggggggggggggggggggggggggggggggggggggggeee"
>>> os.listdir(long_unc)
FileNotFoundError: [WinError 3]
mais vous pouvez utiliser win32api (pywin32) pour 'construire' une version plus courte, c'est à dire
short_unc=win32api.GetShortPathName(win32api.GetShortPathName(win32api.GetShortPathName("\\K53\Users\Tolan\testing\xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")+"\xxxxxxxxxxxxxxxxxxxxxxxxdddddddddddddddddddddwgggggggggggggggggggggggggggggggggggxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") + "\esssssssssssssssssssssggggggggggggggggggggggggggggggggggggggggggggggeee")
>>> print(short_unc)
\K53\Users\Tolan\testing\XXXXXX~1\XXXXXX~1\ESSSSS~1
>>> import os
>>> os.listdir(short_unc)
['test.txt']
clairement, vous pouvez simplement plier le win32api.Getshortpathname appelle en vous DIR exploration plutôt que de nicher comme dans mon exemple. Je l'ai fait comme ça avec 3 appels parce que si tu as déjà un ' a ' aussi long ' path puis win32api.GetShortPathName ne s'en chargera pas non plus, mais vous pouvez le faire par dir et rester en dessous de la limite.
pour localiser les fichiers sur les chemins UNC, le préfixe magique est \?\UNC\
plutôt que \?\
.
Référence: https://msdn.microsoft.com/en-us/library/aa365247(SV.85).aspx#maxpath
Donc, pour l'accès //server/share/really/deep/path/etc/etc
, vous devez
- convertissez-le en unicode (utilisez le
unicode()
constructeur) - ajouter le préfixe magic (
"\?\UNC\"
) et - S'assurer que tous les séparateurs de répertoires sont
"\"
(voiros.path.normpath()
)
chaîne unicode résultante:\?\UNC\server\share\really\deep\path\etc\etc
Je n'ai expérimenté qu'un peu (beaucoup moins que @stenci) mais avec Python 2.7 il semble que ça fonctionne bien avec os.walk()
, et à l'échec avec os.listdir()
.
mise en garde: cela ne fonctionne qu'avec os.walk () si le chemin de départ de la traversée est à L'intérieur de la limite MAX_PATH, et aucun des sous-répertoires du chemin de départ ne le pousserait au-dessus de la limite non plus. C'est parce que l'os.marche() utilise OS.listdir() sur le répertoire supérieur.
Dans mon commentaire précédent j'ai dit que le imbriquée appel récursif de GetShortPathName
n'est pas nécessaire. J'ai trouvé il n'est pas nécessaire la plupart du temps, mais de temps en temps il se bloque. Je n'ai pas pu savoir quand, alors j'ai créé cette petite fonction qui fonctionne bien depuis un certain temps:
C'est la fonction que j'utilise maintenant:
def short_name(name):
try:
return win32api.GetShortPathName(name)
except win32api.error:
dirname = os.path.dirname(name)
basename = os.path.basename(name)
short_dirname = win32api.GetShortPathName(dirname)
return win32api.GetShortPathName(os.path.join(short_dirname, basename))
try:
mtime = os.path.getmtime(name)
except FileNotFoundError:
name = short_name(name)
mtime = os.path.getmtime(name)