In che modo il comando Windows RENAME interpreta i caratteri jolly?


78

In che modo il comando RENAME (REN) di Windows interpreta i caratteri jolly?

La funzione di AIUTO integrata non è di alcun aiuto: non risolve affatto i caratteri jolly.

La guida in linea di Microsoft technet XP non è molto migliore. Ecco tutto ciò che ha da dire sui caratteri jolly:

"È possibile utilizzare i caratteri jolly ( *e ?) in entrambi i parametri del nome file. Se si utilizzano i caratteri jolly nel nome file2, i caratteri rappresentati dai caratteri jolly saranno identici ai caratteri corrispondenti nel nome file1."

Non c'è molto aiuto: ci sono molti modi in cui questa affermazione può essere interpretata.

Sono riuscito a utilizzare con successo i caratteri jolly nel parametro nomefile2 in alcune occasioni, ma è sempre stato tentativi ed errori. Non sono stato in grado di anticipare cosa funziona e cosa no. Spesso ho dovuto ricorrere alla scrittura di un piccolo script batch con un ciclo FOR che analizza ogni nome in modo da poter costruire ogni nuovo nome secondo necessità. Non molto conveniente.

Se conoscessi le regole su come vengono elaborati i caratteri jolly, immagino che potrei usare il comando RENAME in modo più efficace senza dover ricorrere al batch con la stessa frequenza. Naturalmente la conoscenza delle regole andrebbe a beneficio dello sviluppo di lotti.

(Sì, questo è un caso in cui sto pubblicando una domanda e una risposta accoppiata. Mi sono stancato di non conoscere le regole e ho deciso di sperimentare da solo. Immagino che molti altri potrebbero essere interessati a ciò che ho scoperto)


Ci sono un sacco di buoni esempi di come rinominare con i caratteri jolly qui: lagmonster.org/docs/DOS7/z-ren1.html
Matthew Lock

5
@MatthewLock - link interessante, ma tali norme e gli esempi sono per MSDOS 7, non di Windows. Ci sono differenze significative. Ad esempio, MSDOS non consente di aggiungere ulteriori caratteri dopo *, Windows lo fa. Ciò ha conseguenze enormi. Vorrei però aver saputo di quel sito; potrebbe aver semplificato la mia indagine. Le regole MSDOS7 sono significativamente diverse rispetto alle vecchie regole DOS prima dei nomi di file lunghi e sono un passo nella direzione di come Windows lo gestisce. Avevo trovato le regole DOS del nome file pre-lungo ed erano inutili per la mia indagine.
dbenham,

Non lo sapevo;)
Matthew Lock,

Risposte:


117

Queste regole sono state scoperte dopo numerosi test su una macchina Vista. Non sono stati eseguiti test con Unicode nei nomi dei file.

RENAME richiede 2 parametri: una maschera di origine, seguita da una maschera di destinazione. Sia sourceMask che targetMask possono contenere *e / o ?caratteri jolly. Il comportamento dei caratteri jolly cambia leggermente tra le maschere di origine e di destinazione.

Nota : REN può essere utilizzato per rinominare una cartella, ma i caratteri jolly non sono consentiti in sourceMask o targetMask quando si rinomina una cartella. Se sourceMask corrisponde ad almeno un file, i file verranno rinominati e le cartelle verranno ignorate. Se sourceMask corrisponde solo alle cartelle e non ai file, viene generato un errore di sintassi se i caratteri jolly vengono visualizzati nell'origine o nella destinazione. Se sourceMask non corrisponde a nulla, si verifica un errore "file non trovato".

Inoltre, quando si rinomina un file, i caratteri jolly sono consentiti solo nella parte del nome file di sourceMask. I caratteri jolly non sono consentiti nel percorso che porta al nome del file.

sourceMask

SourceMask funziona come filtro per determinare quali file vengono rinominati. I caratteri jolly funzionano qui come con qualsiasi altro comando che filtra i nomi dei file.

  • ?- Corrisponde a qualsiasi carattere 0 o 1 tranne . questo jolly è avido - consuma sempre il carattere successivo se non è un . Tuttavia non corrisponderà a nulla senza errori se alla fine del nome o se il carattere successivo è un.

  • *- Corrisponde a qualsiasi 0 o più caratteri incluso . (con un'eccezione di seguito). Questo jolly non è avido. Corrisponderà alla quantità necessaria o ridotta per consentire la corrispondenza dei caratteri successivi.

Tutti i caratteri non jolly devono corrispondere a se stessi, con alcune eccezioni di casi speciali.

  • .- Corrisponde a se stesso o può corrispondere alla fine del nome (nulla) se non rimangono più caratteri. (Nota: un nome Windows valido non può terminare con .)

  • {space}- Corrisponde a se stesso o può corrispondere alla fine del nome (nulla) se non rimangono più caratteri. (Nota: un nome Windows valido non può terminare con {space})

  • *.alla fine - Corrisponde a qualsiasi 0 o più caratteri tranne . La terminazione .può effettivamente essere una qualsiasi combinazione di .e {space}fintanto che l'ultimo carattere nella maschera è . Questa è l'unica e unica eccezione in cui *non corrisponde semplicemente alcun set di caratteri.

Le regole di cui sopra non sono così complesse. Ma c'è un'altra regola molto importante che rende la situazione confusa: sourceMask viene confrontato sia con il nome lungo che con il nome breve 8.3 (se esiste). Quest'ultima regola può rendere molto difficile l'interpretazione dei risultati, perché non è sempre ovvio quando la maschera corrisponde con il nome breve.

È possibile utilizzare RegEdit per disabilitare la generazione di nomi 8.3 brevi sui volumi NTFS, a quel punto l'interpretazione dei risultati della maschera file è molto più semplice. Rimarranno tutti i nomi brevi generati prima di disabilitare i nomi brevi.

targetMask

Nota: non ho eseguito test rigorosi, ma sembra che queste stesse regole funzionino anche per il nome target del comando COPY

TargetMask specifica il nuovo nome. Viene sempre applicato al nome lungo completo; TargetMask non viene mai applicato al nome breve 8.3, anche se sourceMask corrisponde al nome breve 8.3.

La presenza o l'assenza di caratteri jolly nella maschera di origine non ha alcun impatto sul modo in cui i caratteri jolly vengono elaborati nella maschera di destinazione.

Nella seguente discussione - crappresenta qualsiasi carattere che non è *, ?o.

TargetMask viene elaborato in base al nome della sorgente rigorosamente da sinistra a destra senza back-tracking.

  • c- Fa avanzare la posizione all'interno del nome della fonte fintanto che il carattere successivo non lo è .e si aggiunge cal nome della destinazione. (Sostituisce il personaggio che era nella fonte con c, ma non sostituisce mai .)

  • ?- Abbina il carattere successivo dal nome lungo della fonte e lo aggiunge al nome di destinazione purché il carattere successivo non lo sia. . Se il carattere successivo è .o se alla fine del nome della fonte non viene aggiunto alcun carattere al risultato e al corrente la posizione all'interno del nome della sorgente è invariata.

  • *alla fine di targetMask - Aggiunge tutti i caratteri rimanenti dall'origine alla destinazione. Se già alla fine della fonte, quindi non fa nulla.

  • *c- Corrisponde a tutti i caratteri sorgente dalla posizione corrente all'ultima occorrenza di c(corrispondenza golosa con distinzione tra maiuscole e minuscole) e aggiunge il set di caratteri corrispondente al nome di destinazione. Se cnon viene trovato, vengono aggiunti tutti i caratteri rimanenti dall'origine, seguiti da c Questa è l'unica situazione che conosco in cui la corrispondenza del modello di file di Windows è sensibile al maiuscolo / minuscolo.

  • *.- Abbina tutti i personaggi di origine dalla posizione corrente all'ultima occorrenza di .(partita golosa) e aggiunge il set di caratteri corrispondente al nome di destinazione. Se .non viene trovato, vengono aggiunti tutti i caratteri rimanenti dalla fonte, seguiti da.

  • *?- Aggiunge tutti i personaggi rimanenti dalla sorgente al bersaglio. Se già alla fine della fonte non fa nulla.

  • .senza *davanti - Fa avanzare la posizione nella fonte attraverso la prima occorrenza .senza copiare alcun carattere e si aggiunge .al nome di destinazione. Se .non viene trovato nella fonte, avanza fino alla fine della fonte e si aggiunge .al nome di destinazione.

Dopo che il targetMask è stato esaurito, tutti i finali .e {space}vengono eliminati alla fine del nome di destinazione risultante perché i nomi dei file di Windows non possono terminare con .o{space}

Alcuni esempi pratici

Sostituisci un personaggio nella prima e terza posizione prima di qualsiasi estensione (aggiunge un secondo o un terzo carattere se non esiste ancora)

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

Cambia l'estensione (finale) di ogni file

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

Aggiungi un'estensione a ogni file

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

Rimuovere eventuali estensioni extra dopo l'estensione iniziale. Si noti che è ?necessario utilizzare un'adeguata per preservare l'intero nome esistente e l'estensione iniziale.

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)

Come sopra, ma filtra i file con nome iniziale e / o estensione più lunghi di 5 caratteri in modo che non vengano troncati. (Ovviamente potrebbe aggiungere un ulteriore ?su entrambe le estremità di targetMask per preservare nomi ed estensioni fino a 6 caratteri)

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)

Cambia i caratteri dopo l'ultimo _nel nome e prova a preservare l'estensione. (Non funziona correttamente se _appare nell'estensione)

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)

Qualsiasi nome può essere suddiviso in componenti delimitati da . Personaggi che possono essere aggiunti o eliminati solo dalla fine di ciascun componente. I caratteri non possono essere eliminati o aggiunti all'inizio o al centro di un componente preservando il resto con caratteri jolly. Le sostituzioni sono consentite ovunque.

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

Se i nomi brevi sono abilitati, allora un sourceMask con almeno 8 ?per il nome e almeno 3 ?per l'estensione corrisponderà a tutti i file perché corrisponderà sempre al nome breve 8.3.

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


Quirk / bug utili? per eliminare i prefissi dei nomi

Questo post di SuperUser descrive come utilizzare una serie di barre ( /) per eliminare i caratteri iniziali da un nome file. È richiesta una barra per ogni carattere da eliminare. Ho confermato il comportamento su un computer Windows 10.

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

Questa tecnica funziona solo se le maschere di origine e di destinazione sono racchiuse tra virgolette doppie. Tutti i seguenti moduli senza virgolette richieste non riescono con questo errore: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

Non /può essere usato per rimuovere alcun carattere nel mezzo o alla fine di un nome di file. Può solo rimuovere i caratteri iniziali (prefisso).

Tecnicamente /non funziona come jolly. Piuttosto sta effettuando una semplice sostituzione del carattere, ma dopo la sostituzione, il comando REN riconosce che /non è valido in un nome di file e rimuove le /barre iniziali dal nome. REN genera un errore di sintassi se viene rilevato /nel mezzo di un nome di destinazione.


Possibile bug RENAME - un singolo comando può rinominare lo stesso file due volte!

A partire da una cartella di prova vuota:

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

Credo che sourceMask *1*corrisponda prima al nome del file lungo e il file viene rinominato con il risultato previsto di 223456789.123.x. RENAME continua quindi a cercare altri file da elaborare e trova il file appena nominato tramite il nuovo nome breve di 223456~1.X. Il file viene quindi rinominato nuovamente dando il risultato finale di 223456789.123.xx.

Se disabilito la generazione del nome 8.3, RENAME dà il risultato atteso.

Non ho elaborato completamente tutte le condizioni di innesco che devono esistere per indurre questo strano comportamento. Temevo che potesse essere possibile creare un RENAME ricorsivo senza fine, ma non sono mai stato in grado di indurlo.

Credo che tutto ciò che segue debba essere vero per indurre il bug. Ogni caso con problemi che ho visto presentava le seguenti condizioni, ma non tutti i casi che soddisfacevano le seguenti condizioni erano stati risolti.

  • I nomi brevi 8.3 devono essere abilitati
  • SourceMask deve corrispondere al nome lungo originale.
  • La ridenominazione iniziale deve generare un nome breve che corrisponda anche a sourceMask
  • Il nome breve rinominato iniziale deve essere ordinato più tardi del nome breve originale (se esisteva?)

6
Che risposta completa .. +1.
meder omuraliev,

Tremendamente elaborato!
Andriy M

13
In base a ciò, Microsoft dovrebbe semplicemente aggiungere "Per l'utilizzo, consultare superuser.com/a/475875 " in REN /?.
efotinis,

4
@CAD: questa risposta è un contenuto originale al 100% che Simon ha incluso nel suo sito su mia richiesta. Guarda in fondo a quella pagina SS64 e vedrai che Simon mi dà credito per il lavoro.
dbenham,

2
@ JacksOnF1re - Nuove informazioni / tecnica aggiunte alla mia risposta. Puoi effettivamente cancellare il tuo Copy of prefisso usando un'oscura tecnica di taglio in avanti:ren "Copy of *.txt" "////////*"
dbenham,

4

Simile a exebook, ecco un'implementazione C # per ottenere il nome file di destinazione da un file sorgente.

Ho trovato 1 piccolo errore negli esempi di dbenham:

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

Ecco il codice:

    /// <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('.', ' ');
    }

Ed ecco un metodo di test NUnit per testare gli esempi:

    [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));

    }

Grazie per la testa in sospeso per l'errore nel mio esempio. Ho modificato la mia risposta per risolverlo.
dbenham,

1

Sono riuscito a scrivere questo codice in BASIC per mascherare i nomi dei file jolly:

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

4
Puoi chiarire come questo risponde a ciò che è stato posto nella domanda?
fixer 1234

Replica la funzione utilizzata da REN per la corrispondenza dei caratteri jolly, ad esempio l'elaborazione di REN * .TMP * .DOC a seconda di come viene chiamata la funzione prima di rinominare i nomi dei file.
eoredson,

1

Forse qualcuno può trovare questo utile. Questo codice JavaScript si basa sulla risposta di dbenham sopra.

Non ho testato sourceMaskmolto, ma targetMaskcorrisponde a tutti gli esempi forniti da 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)
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.