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
30
demandé sur randomsimon 2013-08-23 00:35:15

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.

6
répondu tolanj 2013-08-23 23:41:23

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

  1. convertissez-le en unicode (utilisez le unicode() constructeur)
  2. ajouter le préfixe magic ("\?\UNC\") et
  3. S'assurer que tous les séparateurs de répertoires sont "\" (voir os.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.

4
répondu randomsimon 2015-07-17 10:47:04

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)
1
répondu stenci 2016-07-15 17:37:20