¿Cómo interpreta el comando RENAME de Windows los comodines?

78

¿Cómo interpreta el comando RENAME de Windows (REN) los comodines?

La función de AYUDA incorporada no sirve de nada: no aborda los comodines en absoluto.

La ayuda en línea de Microsoft technet XP no es mucho mejor. Aquí está todo lo que tiene que decir con respecto a los comodines:

"Puede usar comodines ( *y ?) en cualquiera de los parámetros de nombre de archivo. Si usa comodines en filename2, los caracteres representados por los comodines serán idénticos a los caracteres correspondientes en filename1".

No es de mucha ayuda, hay muchas maneras en que esa declaración puede ser interpretada.

Me las arreglé para usar con éxito los comodines en el parámetro filename2 en algunas ocasiones, pero siempre ha sido prueba y error. No he podido anticipar qué funciona y qué no. Con frecuencia he tenido que recurrir a escribir un pequeño script por lotes con un bucle FOR que analiza cada nombre para poder construir cada nuevo nombre según sea necesario. No muy conveniente

Si supiera las reglas sobre cómo se procesan los comodines, creo que podría usar el comando RENAME de manera más efectiva sin tener que recurrir al lote con tanta frecuencia. Por supuesto, conocer las reglas también beneficiaría el desarrollo por lotes.

(Sí, este es un caso en el que estoy publicando una pregunta y una respuesta combinadas. Me cansé de no conocer las reglas y decidí experimentar por mi cuenta. Creo que muchos otros pueden estar interesados ​​en lo que descubrí)

dbenham
fuente
Hay montones de buenos ejemplos de cómo cambiar el nombre con comodines aquí: lagmonster.org/docs/DOS7/z-ren1.html
Matthew Lock
55
@MatthewLock: enlace interesante, pero esas reglas y ejemplos son para MSDOS 7, no para Windows. Hay diferencias significativas Por ejemplo, MSDOS no permite agregar caracteres adicionales después *, Windows sí. Eso tiene enormes consecuencias. Aunque desearía haber sabido sobre ese sitio; Podría haber facilitado mi investigación. Las reglas MSDOS7 son significativamente diferentes de las viejas reglas de DOS antes de los nombres largos de archivos, y son un paso en la dirección de cómo Windows lo maneja. Había encontrado las reglas de DOS de nombre de archivo prelargo, y no valían para mi investigación.
dbenham
No sabía eso;)
Matthew Lock

Respuestas:

117

Estas reglas fueron descubiertas después de extensas pruebas en una máquina Vista. No se realizaron pruebas con Unicode en los nombres de archivo.

RENAME requiere 2 parámetros: una máscara de origen, seguida de una máscara de destino. Tanto sourceMask como targetMask pueden contener *y / o ?comodines. El comportamiento de los comodines cambia ligeramente entre las máscaras de origen y destino.

Nota : REN se puede usar para cambiar el nombre de una carpeta, pero los comodines no están permitidos en sourceMask o targetMask al cambiar el nombre de una carpeta. Si sourceMask coincide con al menos un archivo, se cambiará el nombre de los archivos y se ignorarán las carpetas. Si sourceMask solo coincide con carpetas y no con archivos, se genera un error de sintaxis si aparecen comodines en el origen o el destino. Si sourceMask no coincide con nada, se produce un error de "archivo no encontrado".

Además, al cambiar el nombre de los archivos, los comodines solo se permiten en la parte del nombre del archivo de la máscara de origen. Los comodines no están permitidos en la ruta que conduce al nombre del archivo.

fuenteMáscara

SourceMask funciona como un filtro para determinar qué archivos se renombran. Los comodines funcionan aquí igual que con cualquier otro comando que filtre los nombres de los archivos.

  • ?- Coincide con cualquier carácter 0 o 1 excepto . Este comodín es codicioso: siempre consume el siguiente carácter si no es un . Sin embargo, no coincidirá con nada sin fallar si al final del nombre o si el siguiente carácter es un.

  • *- Coincide con 0 o más caracteres incluidos . (con una excepción a continuación). Este comodín no es codicioso. Coincidirá tanto o tan poco como sea necesario para permitir que los caracteres posteriores coincidan.

Todos los caracteres que no son comodines deben coincidir entre sí, con algunas excepciones de casos especiales.

  • .- Coincide o puede coincidir con el final del nombre (nada) si no quedan más caracteres. (Nota: un nombre de Windows válido no puede terminar con .)

  • {space}- Coincide o puede coincidir con el final del nombre (nada) si no quedan más caracteres. (Nota: un nombre de Windows válido no puede terminar con {space})

  • *.al final: coincide con 0 o más caracteres, excepto que . la terminación .puede ser una combinación de .y {space}siempre y cuando el último carácter de la máscara sea . Esta es la única excepción en la *que no coincide simplemente con ningún conjunto de caracteres.

Las reglas anteriores no son tan complejas. Pero hay una regla más importante que hace que la situación sea confusa: SourceMask se compara con el nombre largo y el nombre corto 8.3 (si existe). Esta última regla puede hacer que la interpretación de los resultados sea muy complicada, ya que no siempre es obvio cuando la máscara coincide con el nombre corto.

Es posible usar RegEdit para deshabilitar la generación de nombres cortos de 8.3 en volúmenes NTFS, en cuyo punto la interpretación de los resultados de la máscara de archivo es mucho más sencilla. Los nombres cortos que se generaron antes de deshabilitar los nombres cortos permanecerán.

targetMask

Nota: no he realizado ninguna prueba rigurosa, pero parece que estas mismas reglas también funcionan para el nombre de destino del comando COPY

TargetMask especifica el nuevo nombre. Siempre se aplica al nombre largo completo; TargetMask nunca se aplica al nombre corto 8.3, incluso si sourceMask coincide con el nombre corto 8.3.

La presencia o ausencia de comodines en sourceMask no tiene ningún impacto en cómo se procesan los comodines en targetMask.

En la siguiente discusión - crepresenta cualquier carácter que no es *, ?o.

TargetMask se procesa contra el nombre de origen estrictamente de izquierda a derecha sin retroceso.

  • c- Avanza la posición dentro del nombre de la fuente siempre que el siguiente carácter no lo sea .y anexe cal nombre de destino. (Reemplaza el personaje que estaba en origen con c, pero nunca reemplaza .)

  • ?- Coincide con el siguiente carácter del nombre largo de origen y lo agrega al nombre de destino siempre que el siguiente carácter no lo sea. . Si el siguiente carácter es .o si al final del nombre de origen no se agrega ningún carácter al resultado y al actual la posición dentro del nombre de la fuente no cambia.

  • *al final de targetMask: agrega todos los caracteres restantes desde el origen al destino. Si ya está al final de la fuente, entonces no hace nada.

  • *c- cHace coincidir todos los caracteres de origen desde la posición actual hasta la última aparición de (coincidencia codiciosa entre mayúsculas y minúsculas) y agrega el conjunto de caracteres coincidentes al nombre del objetivo. Si cno se encuentra, se agregan todos los caracteres restantes de la fuente, seguidos de c Esta es la única situación que conozco donde la coincidencia de patrones de archivos de Windows distingue entre mayúsculas y minúsculas.

  • *.- Hace coincidir todos los caracteres de origen desde la posición actual hasta la última aparición de .(coincidencia codiciosa) y agrega el conjunto de caracteres coincidentes al nombre del objetivo. Si .no se encuentra, se añaden todos los caracteres restantes de la fuente, seguidos de.

  • *?- Añade todos los personajes restantes desde el origen al destino. Si ya está al final de la fuente, entonces no hace nada.

  • .sin *delante: avanza la posición en origen a través de la primera aparición de .sin copiar ningún carácter, y se agrega .al nombre del objetivo. Si .no se encuentra en la fuente, avanza hasta el final de la fuente y se agrega .al nombre de destino.

Después de que targetMask se haya agotado, cualquier final .y {space}se recorta al final del nombre de destino resultante porque los nombres de archivo de Windows no pueden terminar con .o{space}

Algunos ejemplos prácticos

Sustituya un personaje en la primera y tercera posición antes de cualquier extensión (agrega un segundo o tercer carácter si aún no existe)

ren  *  A?Z*
  1        -> AZ
  12       -> A2Z
  1.txt    -> AZ.txt
  12.txt   -> A2Z.txt
  123      -> A2Z
  123.txt  -> A2Z.txt
  1234     -> A2Z4
  1234.txt -> A2Z4.txt

Cambiar la extensión (final) de cada archivo

ren  *  *.txt
  a     -> a.txt
  b.dat -> b.txt
  c.x.y -> c.x.txt

Agregar una extensión a cada archivo

ren  *  *?.bak
  a     -> a.bak
  b.dat -> b.dat.bak
  c.x.y -> c.x.y.bak

Elimine cualquier extensión adicional después de la extensión inicial. Tenga en cuenta que ?debe usarse adecuado para preservar el nombre completo existente y la extensión inicial.

ren  *  ?????.?????
  a     -> a
  a.b   -> a.b
  a.b.c -> a.b
  part1.part2.part3    -> part1.part2
  123456.123456.123456 -> 12345.12345   (note truncated name and extension because not enough `?` were used)

Igual que el anterior, pero filtre los archivos con nombre inicial y / o extensión de más de 5 caracteres para que no se trunquen. (Obviamente, podría agregar un adicional ?en cualquier extremo de targetMask para preservar nombres y extensiones de hasta 6 caracteres de largo)

ren  ?????.?????.*  ?????.?????
  a      ->  a
  a.b    ->  a.b
  a.b.c  ->  a.b
  part1.part2.part3  ->  part1.part2
  123456.123456.123456  (Not renamed because doesn't match sourceMask)

Cambie los caracteres después del último _nombre e intente preservar la extensión. (No funciona correctamente si _aparece en la extensión)

ren  *_*  *_NEW.*
  abcd_12345.txt  ->  abcd_NEW.txt
  abc_newt_1.dat  ->  abc_newt_NEW.dat
  abcdef.jpg          (Not renamed because doesn't match sourceMask)
  abcd_123.a_b    ->  abcd_123.a_NEW  (not desired, but no simple RENAME form will work in this case)

Cualquier nombre se puede dividir en componentes que están delimitados por . caracteres. Solo se pueden agregar o eliminar del final de cada componente. Los caracteres no pueden eliminarse ni agregarse al principio o al medio de un componente mientras se conserva el resto con comodines. Se permiten sustituciones en cualquier lugar.

ren  ??????.??????.??????  ?x.????999.*rForTheCourse
  part1.part2            ->  px.part999.rForTheCourse
  part1.part2.part3      ->  px.part999.parForTheCourse
  part1.part2.part3.part4   (Not renamed because doesn't match sourceMask)
  a.b.c                  ->  ax.b999.crForTheCourse
  a.b.CarPart3BEER       ->  ax.b999.CarParForTheCourse

Si los nombres cortos están habilitados, una máscara de origen con al menos 8 ?para el nombre y al menos 3 ?para la extensión coincidirá con todos los archivos porque siempre coincidirá con el nombre corto 8.3.

ren ????????.???  ?x.????999.*rForTheCourse
  part1.part2.part3.part4  ->  px.part999.part3.parForTheCourse


¿Capricho / error útil? para eliminar prefijos de nombre

Esta publicación de SuperUser describe cómo /se puede usar un conjunto de barras diagonales ( ) para eliminar los caracteres iniciales del nombre de un archivo. Se requiere una barra oblicua para cada carácter que se va a eliminar. He confirmado el comportamiento en una máquina con Windows 10.

ren "abc-*.txt" "////*.txt"
  abc-123.txt        --> 123.txt
  abc-HelloWorld.txt --> HelloWorld.txt

Esta técnica solo funciona si las máscaras de origen y destino están encerradas entre comillas dobles. Todos los siguientes formularios sin las comillas requeridas fallan con este error:The syntax of the command is incorrect

REM - All of these forms fail with a syntax error.
ren abc-*.txt "////*.txt"
ren "abc-*.txt" ////*.txt
ren abc-*.txt ////*.txt

No /se puede utilizar para eliminar ningún carácter en el medio o al final de un nombre de archivo. Solo puede eliminar los caracteres iniciales (prefijo).

Técnicamente, el /no funciona como comodín. Por el contrario, se trata de una simple sustitución de caracteres, pero luego de la sustitución, el comando REN reconoce que /no es válido en un nombre de archivo y elimina las /barras diagonales del nombre. REN da un error de sintaxis si detecta /en el medio de un nombre de destino.


Posible error de RENAME: ¡un solo comando puede cambiar el nombre del mismo archivo dos veces!

Comenzando en una carpeta de prueba vacía:

C:\test>copy nul 123456789.123
        1 file(s) copied.

C:\test>dir /x
 Volume in drive C is OS
 Volume Serial Number is EE2C-5A11

 Directory of C:\test

09/15/2012  07:42 PM    <DIR>                       .
09/15/2012  07:42 PM    <DIR>                       ..
09/15/2012  07:42 PM                 0 123456~1.123 123456789.123
               1 File(s)              0 bytes
               2 Dir(s)  327,237,562,368 bytes free

C:\test>ren *1* 2*3.?x

C:\test>dir /x
 Volume in drive C is OS
 Volume Serial Number is EE2C-5A11

 Directory of C:\test

09/15/2012  07:42 PM    <DIR>                       .
09/15/2012  07:42 PM    <DIR>                       ..
09/15/2012  07:42 PM                 0 223456~1.XX  223456789.123.xx
               1 File(s)              0 bytes
               2 Dir(s)  327,237,562,368 bytes free

REM Expected result = 223456789.123.x

Creo que sourceMask *1*primero coincide con el nombre de archivo largo, y el archivo cambia de nombre al resultado esperado de 223456789.123.x. RENAME continúa buscando más archivos para procesar y encuentra el archivo recién nombrado a través del nuevo nombre corto de 223456~1.X. El archivo se renombra nuevamente dando el resultado final de 223456789.123.xx.

Si deshabilito la generación de nombres 8.3, entonces RENAME da el resultado esperado.

No he resuelto completamente todas las condiciones de activación que deben existir para inducir este comportamiento extraño. Me preocupaba que pudiera ser posible crear un RENAME recursivo interminable, pero nunca pude inducir uno.

Creo que todo lo siguiente debe ser cierto para inducir el error. Todos los casos con errores que vi tenían las siguientes condiciones, pero no todos los casos que cumplían las siguientes condiciones tenían errores.

  • Los nombres cortos de 8.3 deben estar habilitados
  • SourceMask debe coincidir con el nombre largo original.
  • El cambio de nombre inicial debe generar un nombre corto que también coincida con la máscara de origen
  • El nombre corto renombrado inicial debe clasificarse más tarde que el nombre corto original (si existiera)
dbenham
fuente
66
Qué respuesta tan minuciosa ... +1.
meder omuraliev
Tremendamente elaborado!
Andriy M
13
En base a esto, Microsoft debería agregar "Para uso, consulte superuser.com/a/475875 " en REN /?.
efotinis
44
@CAD: esta respuesta es contenido 100% original que Simon incluyó en su sitio a mi solicitud. Mire la parte inferior de esa página SS64 y verá que Simon me da crédito por el trabajo.
dbenham
2
@ JacksOnF1re - Nueva información / técnica agregada a mi respuesta. En realidad, puede eliminar su Copy of prefijo utilizando una oscura técnica de barra diagonal:ren "Copy of *.txt" "////////*"
dbenham
4

Similar a exebook, aquí hay una implementación de C # para obtener el nombre de archivo de destino de un archivo fuente.

Encontré 1 pequeño error en los ejemplos de dbenham:

 ren  *_*  *_NEW.*
   abc_newt_1.dat  ->  abc_newt_NEW.txt (should be: abd_newt_NEW.dat)

Aquí está el código:

    /// <summary>
    /// Returns a filename based on the sourcefile and the targetMask, as used in the second argument in rename/copy operations.
    /// targetMask may contain wildcards (* and ?).
    /// 
    /// This follows the rules of: http://superuser.com/questions/475874/how-does-the-windows-rename-command-interpret-wildcards
    /// </summary>
    /// <param name="sourcefile">filename to change to target without wildcards</param>
    /// <param name="targetMask">mask with wildcards</param>
    /// <returns>a valid target filename given sourcefile and targetMask</returns>
    public static string GetTargetFileName(string sourcefile, string targetMask)
    {
        if (string.IsNullOrEmpty(sourcefile))
            throw new ArgumentNullException("sourcefile");

        if (string.IsNullOrEmpty(targetMask))
            throw new ArgumentNullException("targetMask");

        if (sourcefile.Contains('*') || sourcefile.Contains('?'))
            throw new ArgumentException("sourcefile cannot contain wildcards");

        // no wildcards: return complete mask as file
        if (!targetMask.Contains('*') && !targetMask.Contains('?'))
            return targetMask;

        var maskReader = new StringReader(targetMask);
        var sourceReader = new StringReader(sourcefile);
        var targetBuilder = new StringBuilder();


        while (maskReader.Peek() != -1)
        {

            int current = maskReader.Read();
            int sourcePeek = sourceReader.Peek();
            switch (current)
            {
                case '*':
                    int next = maskReader.Read();
                    switch (next)
                    {
                        case -1:
                        case '?':
                            // Append all remaining characters from sourcefile
                            targetBuilder.Append(sourceReader.ReadToEnd());
                            break;
                        default:
                            // Read source until the last occurrance of 'next'.
                            // We cannot seek in the StringReader, so we will create a new StringReader if needed
                            string sourceTail = sourceReader.ReadToEnd();
                            int lastIndexOf = sourceTail.LastIndexOf((char) next);
                            // If not found, append everything and the 'next' char
                            if (lastIndexOf == -1)
                            {
                                targetBuilder.Append(sourceTail);
                                targetBuilder.Append((char) next);

                            }
                            else
                            {
                                string toAppend = sourceTail.Substring(0, lastIndexOf + 1);
                                string rest = sourceTail.Substring(lastIndexOf + 1);
                                sourceReader.Dispose();
                                // go on with the rest...
                                sourceReader = new StringReader(rest);
                                targetBuilder.Append(toAppend);
                            }
                            break;
                    }

                    break;
                case '?':
                    if (sourcePeek != -1 && sourcePeek != '.')
                    {
                        targetBuilder.Append((char)sourceReader.Read());
                    }
                    break;
                case '.':
                    // eat all characters until the dot is found
                    while (sourcePeek != -1 && sourcePeek != '.')
                    {
                        sourceReader.Read();
                        sourcePeek = sourceReader.Peek();
                    }

                    targetBuilder.Append('.');
                    // need to eat the . when we peeked it
                    if (sourcePeek == '.')
                        sourceReader.Read();

                    break;
                default:
                    if (sourcePeek != '.') sourceReader.Read(); // also consume the source's char if not .
                    targetBuilder.Append((char)current);
                    break;
            }

        }

        sourceReader.Dispose();
        maskReader.Dispose();
        return targetBuilder.ToString().TrimEnd('.', ' ');
    }

Y aquí hay un método de prueba de NUnit para probar los ejemplos:

    [Test]
    public void TestGetTargetFileName()
    {
        string targetMask = "?????.?????";
        Assert.AreEqual("a", FileUtil.GetTargetFileName("a", targetMask));
        Assert.AreEqual("a.b", FileUtil.GetTargetFileName("a.b", targetMask));
        Assert.AreEqual("a.b", FileUtil.GetTargetFileName("a.b.c", targetMask));
        Assert.AreEqual("part1.part2", FileUtil.GetTargetFileName("part1.part2.part3", targetMask));
        Assert.AreEqual("12345.12345", FileUtil.GetTargetFileName("123456.123456.123456", targetMask));

        targetMask = "A?Z*";
        Assert.AreEqual("AZ", FileUtil.GetTargetFileName("1", targetMask));
        Assert.AreEqual("A2Z", FileUtil.GetTargetFileName("12", targetMask));
        Assert.AreEqual("AZ.txt", FileUtil.GetTargetFileName("1.txt", targetMask));
        Assert.AreEqual("A2Z.txt", FileUtil.GetTargetFileName("12.txt", targetMask));
        Assert.AreEqual("A2Z", FileUtil.GetTargetFileName("123", targetMask));
        Assert.AreEqual("A2Z.txt", FileUtil.GetTargetFileName("123.txt", targetMask));
        Assert.AreEqual("A2Z4", FileUtil.GetTargetFileName("1234", targetMask));
        Assert.AreEqual("A2Z4.txt", FileUtil.GetTargetFileName("1234.txt", targetMask));

        targetMask = "*.txt";
        Assert.AreEqual("a.txt", FileUtil.GetTargetFileName("a", targetMask));
        Assert.AreEqual("b.txt", FileUtil.GetTargetFileName("b.dat", targetMask));
        Assert.AreEqual("c.x.txt", FileUtil.GetTargetFileName("c.x.y", targetMask));

        targetMask = "*?.bak";
        Assert.AreEqual("a.bak", FileUtil.GetTargetFileName("a", targetMask));
        Assert.AreEqual("b.dat.bak", FileUtil.GetTargetFileName("b.dat", targetMask));
        Assert.AreEqual("c.x.y.bak", FileUtil.GetTargetFileName("c.x.y", targetMask));

        targetMask = "*_NEW.*";
        Assert.AreEqual("abcd_NEW.txt", FileUtil.GetTargetFileName("abcd_12345.txt", targetMask));
        Assert.AreEqual("abc_newt_NEW.dat", FileUtil.GetTargetFileName("abc_newt_1.dat", targetMask));
        Assert.AreEqual("abcd_123.a_NEW", FileUtil.GetTargetFileName("abcd_123.a_b", targetMask));

        targetMask = "?x.????999.*rForTheCourse";

        Assert.AreEqual("px.part999.rForTheCourse", FileUtil.GetTargetFileName("part1.part2", targetMask));
        Assert.AreEqual("px.part999.parForTheCourse", FileUtil.GetTargetFileName("part1.part2.part3", targetMask));
        Assert.AreEqual("ax.b999.crForTheCourse", FileUtil.GetTargetFileName("a.b.c", targetMask));
        Assert.AreEqual("ax.b999.CarParForTheCourse", FileUtil.GetTargetFileName("a.b.CarPart3BEER", targetMask));

    }
asombroso
fuente
Gracias por el error en mi ejemplo. He editado mi respuesta para arreglarlo.
dbenham
1

He logrado escribir este código en BASIC para enmascarar los nombres de archivo comodín:

REM inputs a filename and matches wildcards returning masked output filename.
FUNCTION maskNewName$ (path$, mask$)
IF path$ = "" THEN EXIT FUNCTION
IF INSTR(path$, "?") OR INSTR(path$, "*") THEN EXIT FUNCTION
x = 0
R$ = ""
FOR m = 0 TO LEN(mask$) - 1
    ch$ = MID$(mask$, m + 1, 1)
    q$ = MID$(path$, x + 1, 1)
    z$ = MID$(mask$, m + 2, 1)
    IF ch$ <> "." AND ch$ <> "*" AND ch$ <> "?" THEN
        IF LEN(q$) AND q$ <> "." THEN x = x + 1
        R$ = R$ + ch$
    ELSE
        IF ch$ = "?" THEN
            IF LEN(q$) AND q$ <> "." THEN R$ = R$ + q$: x = x + 1
        ELSE
            IF ch$ = "*" AND m = LEN(mask$) - 1 THEN
                WHILE x < LEN(path$)
                    R$ = R$ + MID$(path$, x + 1, 1)
                    x = x + 1
                WEND
            ELSE
                IF ch$ = "*" THEN
                    IF z$ = "." THEN
                        FOR i = LEN(path$) - 1 TO 0 STEP -1
                            IF MID$(path$, i + 1, 1) = "." THEN EXIT FOR
                        NEXT
                        IF i < 0 THEN
                            R$ = R$ + MID$(path$, x + 1) + "."
                            i = LEN(path$)
                        ELSE
                            R$ = R$ + MID$(path$, x + 1, i - x + 1)
                        END IF
                        x = i + 1
                        m = m + 1
                    ELSE
                        IF z$ = "?" THEN
                            R$ = R$ + MID$(path$, x + 1, LEN(path$))
                            m = m + 1
                            x = LEN(path$)
                        ELSE
                            FOR i = LEN(path$) - 1 TO 0 STEP -1
                                'IF MID$(path$, i + 1, 1) = z$ THEN EXIT FOR
                                IF UCASE$(MID$(path$, i + 1, 1)) = UCASE$(z$) THEN EXIT FOR
                            NEXT
                            IF i < 0 THEN
                                R$ = R$ + MID$(path$, x + 1, LEN(path$)) + z$
                                x = LEN(path$)
                                m = m + 1
                            ELSE
                                R$ = R$ + MID$(path$, x + 1, i - x)
                                x = i + 1
                            END IF
                        END IF
                    END IF
                ELSE
                    IF ch$ = "." THEN
                        DO WHILE x < LEN(path$)
                            IF MID$(path$, x + 1, 1) = "." THEN
                                x = x + 1
                                EXIT DO
                            END IF
                            x = x + 1
                        LOOP
                        R$ = R$ + "."
                    END IF
                END IF
            END IF
        END IF
    END IF
NEXT
DO WHILE RIGHT$(R$, 1) = "."
    R$ = LEFT$(R$, LEN(R$) - 1)
LOOP
R$ = RTRIM$(R$)
maskNewName$ = R$
END FUNCTION
Eoredson
fuente
44
¿Puedes aclarar cómo responde esto a lo que se hizo en la pregunta?
Fixer1234
Replica la función que REN usa para la coincidencia de comodines, como el procesamiento de REN * .TMP * .DOC, según cómo se llame a la función antes de cambiar el nombre de los nombres de archivo.
Eoredson
1

Quizás alguien pueda encontrar esto útil. Este código JavaScript se basa en la respuesta de dbenham anterior.

No probé sourceMaskmucho, pero targetMaskcoincide con todos los ejemplos dados por dbenham.

function maskMatch(path, mask) {
    mask = mask.replace(/\./g, '\\.')
    mask = mask.replace(/\?/g, '.')
    mask = mask.replace(/\*/g, '.+?')
    var r = new RegExp('^'+mask+'$', '')
    return path.match(r)
}

function maskNewName(path, mask) {
    if (path == '') return
    var x = 0, R = ''
    for (var m = 0; m < mask.length; m++) {
        var ch = mask[m], q = path[x], z = mask[m + 1]
        if (ch != '.' && ch != '*' && ch != '?') {
            if (q && q != '.') x++
            R += ch
        } else if (ch == '?') {
            if (q && q != '.') R += q, x++
        } else if (ch == '*' && m == mask.length - 1) {
            while (x < path.length) R += path[x++]
        } else if (ch == '*') {
            if (z == '.') {
                for (var i = path.length - 1; i >= 0; i--) if (path[i] == '.') break
                if (i < 0) {
                    R += path.substr(x, path.length) + '.'
                    i = path.length
                } else R += path.substr(x, i - x + 1)
                x = i + 1, m++
            } else if (z == '?') {
                R += path.substr(x, path.length), m++, x = path.length
            } else {
                for (var i = path.length - 1; i >= 0; i--) if (path[i] == z) break
                if (i < 0) R += path.substr(x, path.length) + z, x = path.length, m++
                else R += path.substr(x, i - x), x = i + 1
            }
        } else if (ch == '.') {
            while (x < path.length) if (path[x++] == '.') break
            R += '.'
        }
    }
    while (R[R.length - 1] == '.') R = R.substr(0, R.length - 1)
}
exebook
fuente