leer - No se pueden encontrar archivos con nombres largos en Windows con Python
nombre archivo python (3)
En mi comentario anterior, dije que no se requiere la llamada recursiva anidada de GetShortPathName
. Descubrí que no se requiere la mayoría de las veces, pero de vez en cuando se cuelga. No pude averiguar cuándo, así que hice esta pequeña función que ha estado funcionando sin problemas durante un tiempo:
Esta es la función que uso ahora:
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)
Necesito recorrer carpetas con nombres largos de archivos en Windows.
Intenté usar os.listdir()
, pero se bloquea con nombres de ruta largos, lo cual es malo.
Intenté usar os.walk()
, pero ignora los nombres de ruta más largos que ~ 256, lo que es peor.
Probé la solución de palabra mágica descrita here , pero solo funciona con unidades mapeadas, no con nombres de ruta UNC .
Aquí hay un ejemplo con rutas cortas, que muestra que las rutas UNC no funcionan con el truco de la palabra mágica.
>>> os.listdir(''c://drivers'')
[''nusb3hub.cat'', ''nusb3hub.inf'', ''nusb3hub.sys'', ''nusb3xhc.cat'', ''nusb3xhc.inf'', ''nusb3xhc.sys'']
>>> os.listdir(''////Uni-hq-srv6//router'')
[''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-srv6//router'')
Traceback (most recent call last):
File "<pyshell#160>", line 1, in <module>
os.listdir(mw+''////Uni-hq-srv6//router'')
WindowsError: [Error 123] The filename, directory name, or volume label syntax is incorrect: u''////?//////Uni-hq-srv6//router//*.*''
¿Alguna idea sobre cómo tratar con nombres de rutas largas o con rutas de acceso UNC unicode?
Editar:
Siguiendo la sugerencia de los comentarios a continuación, creé algunas funciones de prueba para comparar Python 2.7 y 3.3, y agregué la prueba de glob.glob
y os.listdir
después de os.chdir
.
El os.chdir
no ayudó como se esperaba (ver este comment ).
El glob.glob
es el único que en Python 3.3 funciona mejor, pero solo en una condición: usar la palabra mágica y con el nombre del disco.
Aquí está el código que usé (funciona tanto en 2.7 como en 3.3). Estoy aprendiendo Python ahora, y espero que estas pruebas tengan sentido:
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-srv6/router/test'')]:
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()
Y aqui esta el resultado:
- El número 8 significa que se encontraron todos los archivos.
- El número 0 significa que ni siquiera lo intentó sin estrellarse
- Cualquier número entre 1 y 7 significa que falló a mitad de camino sin estrellarse
- La palabra
Crash
significa que se estrelló
-
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
Para localizar archivos en rutas UNC, el prefijo mágico es //?/UNC/
lugar de simplemente //?/
.
Referencia: https://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath
Entonces para acceder a //server/share/really/deep/path/etc/etc
, necesitarías
- Conviértalo a Unicode (use el constructor
unicode()
) - Agregue el prefijo mágico (
"//?//UNC/"
), y - Asegúrese de que todos los separadores de directorios sean
"/"
(consulteos.path.normpath()
)
Cadena Unicode resultante: //?/UNC/server/share/really/deep/path/etc/etc
Solo experimenté un poco (mucho menos que @stenci), pero con Python 2.7 parece funcionar bien con os.walk()
y fallar con os.listdir()
.
Advertencia: solo funciona con os.walk () si la ruta de inicio para el recorrido está dentro del límite MAX_PATH, y ninguno de los subdirectorios en la ruta de inicio tampoco lo hará. Esto se debe a que como os.walk () usa os.listdir () en el directorio superior.
Utilice el respaldo 8.3 para evitar la ruta de acceso larga; al explorar Win7 explorador, esto parece ser lo que Windows hace, es decir, cada ruta larga tiene un ''nombre verdadero'' más corto:
>>> long_unc="////K53//Users//Tolan//testing//xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx//xxxxxxxxxxxxxxxxxxxxxxxxdddddddddddddddddddddwgggggggggggggggggggggggggggggggggggxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx//esssssssssssssssssssssggggggggggggggggggggggggggggggggggggggggggggggeee"
>>> os.listdir(long_unc)
FileNotFoundError: [WinError 3]
pero puede usar win32api (pywin32) para ''construir'' una versión más corta, es decir,
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'']
claramente puedes simplemente plegar la llamada win32api.GetShortPathName en tu exploración de dir, en lugar de anidar como en mi ejemplo. Lo hice así con 3 llamadas porque si ya tienes una ruta ''demasiado larga'', entonces win32api.GetShortPathName tampoco lo soportará, pero puedes hacerlo por directorio y permanecer por debajo del límite.