2012-09-16 13:59:11 +0000 2012-09-16 13:59:11 +0000
81
81

Wie interpretiert der Windows-Befehl RENAME Wildcards?

Wie interpretiert der Windows-Befehl RENAME (REN) Wildcards?

Die eingebaute HILFE ist keine Hilfe - sie geht überhaupt nicht auf Wildcards ein.

Die Microsoft technet XP online help ist auch nicht viel besser. Hier ist alles, was sie über Wildcards zu sagen hat:

“Sie können Wildcards (* und ?) in beiden Dateinamensparametern verwenden. Wenn Sie Platzhalter in Dateiname2 verwenden, sind die Zeichen, die durch die Platzhalter dargestellt werden, identisch mit den entsprechenden Zeichen in Dateiname1.”

Keine große Hilfe - es gibt viele Möglichkeiten, diese Anweisung zu interpretieren.

Ich habe es geschafft, bei einigen Gelegenheiten erfolgreich Platzhalter im Parameter filename2 zu verwenden, aber es war immer Versuch und Irrtum. Ich war nicht in der Lage, vorauszusehen, was funktioniert und was nicht. Häufig musste ich darauf zurückgreifen, ein kleines Batch-Skript mit einer FOR-Schleife zu schreiben, die jeden Namen parst, so dass ich jeden neuen Namen nach Bedarf erstellen kann. Nicht sehr praktisch.

Wenn ich die Regeln für die Verarbeitung von Platzhaltern kennen würde, könnte ich den RENAME-Befehl effektiver einsetzen, ohne so oft auf Batch zurückgreifen zu müssen. Natürlich wäre es auch für die Batch-Entwicklung von Vorteil, die Regeln zu kennen.

(Ja - dies ist ein Fall, in dem ich eine gepaarte Frage und Antwort poste. Ich war es leid, die Regeln nicht zu kennen, und beschloss, auf eigene Faust zu experimentieren. Ich denke, dass viele andere daran interessiert sein könnten, was ich entdeckt habe)

Antworten (4)

120
120
120
2012-09-16 14:00:21 +0000

Diese Regeln wurden nach umfangreichen Tests auf einem Vista-Rechner entdeckt. Es wurden keine Tests mit Unicode in Dateinamen durchgeführt._

RENAME erfordert 2 Parameter - eine sourceMask, gefolgt von einer targetMask. Sowohl die sourceMask als auch die targetMask können * und/oder ? Wildcards enthalten. Das Verhalten der Platzhalter ändert sich leicht zwischen Quell- und Zielmaske.

Note - REN kann zum Umbenennen eines Ordners verwendet werden, aber Platzhalter sind nicht in der sourceMask oder targetMask beim Umbenennen eines Ordners erlaubt. Wenn die sourceMask auf mindestens eine Datei zutrifft, wird die Datei bzw. werden die Dateien umbenannt und die Ordner werden ignoriert. Wenn die sourceMask nur mit Ordnern und nicht mit Dateien übereinstimmt, wird ein Syntaxfehler erzeugt, wenn Platzhalter in source oder target vorkommen. Wenn die sourceMask auf nichts zutrifft, wird eine Fehlermeldung “Datei nicht gefunden” ausgegeben.

Auch beim Umbenennen von Dateien sind Platzhalter nur im Dateinamensteil der sourceMask erlaubt. Platzhalter sind im Pfad, der zum Dateinamen führt, nicht erlaubt.

sourceMask

Die sourceMask dient als Filter, um zu bestimmen, welche Dateien umbenannt werden. Die Platzhalter funktionieren hier genauso wie bei jedem anderen Befehl, der Dateinamen filtert.

  • ? - Passt auf jedes 0- oder 1-Zeichen außer . Dieser Platzhalter ist gierig - er verbraucht immer das nächste Zeichen, wenn es kein . ist. Er passt jedoch nicht, wenn er am Namensende steht oder wenn das nächste Zeichen ein . ist

  • * - Passt auf jedes 0- oder mehr-Zeichen einschließlich . (mit einer Ausnahme unten). Dieser Platzhalter ist nicht gierig. Er passt so wenig oder so viel, wie nötig ist, damit nachfolgende Zeichen passen.

Alle Nicht-Wildcard-Zeichen müssen selbst übereinstimmen, mit ein paar Ausnahmen für Sonderfälle.

  • . - Stimmt mit sich selbst überein oder kann mit dem Ende des Namens (nichts) übereinstimmen, wenn keine weiteren Zeichen übrig bleiben. (Hinweis - ein gültiger Windows-Name kann nicht mit . enden)

  • {space} - Passt auf sich selbst oder auf das Ende des Namens (nichts), wenn keine weiteren Zeichen übrig bleiben. (Hinweis - ein gültiger Windows-Name kann nicht mit {space} enden)

  • *. am Ende - Passt auf 0 oder mehr Zeichen außer . Das abschließende . kann tatsächlich eine beliebige Kombination aus . und {space} sein, solange das allerletzte Zeichen in der Maske . ist Dies ist die einzige Ausnahme, bei der * nicht einfach auf eine beliebige Menge von Zeichen passt.

Die obigen Regeln sind nicht sehr komplex. Aber es gibt eine weitere sehr wichtige Regel, die die Situation verwirrend macht: Die sourceMask wird sowohl mit dem langen Namen als auch mit dem kurzen 8.3-Namen (falls vorhanden) verglichen. Diese letzte Regel kann die Interpretation der Ergebnisse sehr knifflig machen, weil es nicht immer offensichtlich ist, wenn die Maske über den kurzen Namen abgeglichen wird.

Es ist möglich, mit RegEdit die Generierung von 8.3-Kurznamen auf NTFS-Datenträgern zu deaktivieren, wodurch die Interpretation der Ergebnisse der Dateimaske viel einfacher wird. Alle kurzen Namen, die vor der Deaktivierung der kurzen Namen generiert wurden, bleiben erhalten.

targetMask

Hinweis - Ich habe keine strengen Tests durchgeführt, aber es scheint, dass die gleichen Regeln auch für den Zielnamen des COPY-Befehls gelten

Die targetMask gibt den neuen Namen an. Sie wird immer auf den vollen langen Namen angewendet; die targetMask wird nie auf den kurzen 8.3-Namen angewendet, selbst wenn die sourceMask mit dem kurzen 8.3-Namen übereinstimmte.

Das Vorhandensein oder Fehlen von Platzhaltern in der sourceMask hat keinen Einfluss darauf, wie Platzhalter in der targetMask verarbeitet werden.

In der folgenden Diskussion - c steht für jedes Zeichen, das nicht *, ? oder . ist

Die targetMask wird streng von links nach rechts gegen den Quellnamen verarbeitet, ohne Rückverfolgung.

  • c - Verschiebt die Position innerhalb des Quellnamens nur, wenn das Quellzeichen nicht . ist, und hängt immer c an den Zielnamen an. (Ersetzt das Zeichen, das in der Quelle war, durch c, aber ersetzt niemals .)

  • ? - Passt das nächste Zeichen aus dem langen Quellnamen an und hängt es an den Zielnamen an, solange das Quellzeichen nicht . ist. Wenn das nächste Zeichen . ist oder am Ende des Quellnamens steht, wird kein Zeichen zum Ergebnis hinzugefügt und die aktuelle Position innerhalb des Quellnamens bleibt unverändert.

  • * am Ende von targetMask - Hängt alle verbleibenden Zeichen aus der Quelle an das Ziel an. Wenn bereits am Ende der Quelle, dann macht es nichts.

  • *c - Passt auf alle Zeichen der Quelle ab der aktuellen Position bis zum letzten Vorkommen von c (Groß-/Kleinschreibung wird beachtet) und fügt die übereinstimmende Zeichengruppe an den Zielnamen an. Wenn c nicht gefunden wird, werden alle verbleibenden Zeichen aus der Quelle angehängt, gefolgt von c Dies ist die einzige mir bekannte Situation, in der die Groß- und Kleinschreibung bei der Mustererkennung von Windows-Dateien berücksichtigt wird.

  • *. - Passt auf alle Quellzeichen ab der aktuellen Position bis zum letzten Vorkommen von . (gierige Übereinstimmung) und fügt die übereinstimmende Menge von Zeichen an den Zielnamen an. Wenn . nicht gefunden wird, dann werden alle verbleibenden Zeichen aus der Quelle angehängt, gefolgt von .

  • *? - Hängt alle verbleibenden Zeichen aus der Quelle an das Ziel an. Wenn bereits am Ende der Quelle, dann geschieht nichts.

  • . ohne * davor - Geht die Position in der Quelle bis zum ersten Vorkommen von . vor, ohne irgendwelche Zeichen zu kopieren, und hängt . an den Zielnamen an. Wenn . in der Quelle nicht gefunden wird, dann wird bis zum Ende der Quelle weitergeschaltet und . an den Zielnamen angehängt.

Nachdem die targetMask ausgeschöpft wurde, werden alle nachfolgenden . und {space} vom Ende des resultierenden Zielnamens abgeschnitten, da Windows-Dateinamen nicht mit . oder {space} enden können

Einige praktische Beispiele

Ersetzen eines Zeichens an der ersten und dritten Position vor einer Erweiterung (fügt ein 2. oder 3. Zeichen hinzu, wenn es noch nicht vorhanden ist)

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

Ändern Sie die (endgültige) Erweiterung jeder Datei

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

Hängen Sie eine Erweiterung an jede Datei an

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

Entfernen Sie jede zusätzliche Erweiterung nach der ursprünglichen Erweiterung. Beachten Sie, dass ein adäquates ? verwendet werden muss, um den vollständigen vorhandenen Namen und die anfängliche Erweiterung zu erhalten.

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)

Gleich wie oben, aber filtert Dateien mit Anfangsnamen und/oder Erweiterungen, die länger als 5 Zeichen sind, aus, damit sie nicht abgeschnitten werden. (Offensichtlich könnte ein zusätzliches ? an beiden Enden von targetMask hinzugefügt werden, um Namen und Erweiterungen mit bis zu 6 Zeichen Länge zu erhalten)

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)

Zeichen nach dem letzten _ im Namen ändern und versuchen, die Erweiterung zu erhalten. (Funktioniert nicht ordnungsgemäß, wenn _ in der Erweiterung vorkommt)

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)

Jeder Name kann in Komponenten aufgeteilt werden, die durch . begrenzt sind& Zeichen können nur am Ende jeder Komponente angehängt oder gelöscht werden. Zeichen können nicht am Anfang oder in der Mitte einer Komponente gelöscht oder angehängt werden, während der Rest mit Platzhaltern erhalten bleibt. Substitutionen sind überall erlaubt.

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

Wenn kurze Namen aktiviert sind, dann wird eine sourceMask mit mindestens 8 ? für den Namen und mindestens 3 ? für die Erweiterung mit allen Dateien übereinstimmen, da sie immer mit dem kurzen 8.3 Namen übereinstimmt.

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

Nützlicher Quirk/Bug? zum Löschen von Namenspräfixen

Dieser SuperUser-Beitrag beschreibt, wie ein Satz von Schrägstrichen (/) verwendet werden kann, um führende Zeichen aus einem Dateinamen zu löschen. Für jedes zu löschende Zeichen ist ein Schrägstrich erforderlich. Ich habe das Verhalten auf einem Windows 10-Rechner bestätigt.

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

Diese Technik funktioniert nur, wenn sowohl die Quell- als auch die Zielmaske in doppelte Anführungszeichen eingeschlossen sind. Alle folgenden Formen ohne die erforderlichen Anführungszeichen schlagen mit diesem Fehler fehl: 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

Das / kann nicht verwendet werden, um Zeichen in der Mitte oder am Ende eines Dateinamens zu entfernen. Es kann nur führende (Präfix-) Zeichen entfernen. Beachten Sie auch, dass diese Technik nicht mit Ordnernamen funktioniert.

Technisch gesehen funktioniert das / nicht als Platzhalter. Vielmehr führt es eine einfache Zeichensubstitution durch, aber nach der Substitution erkennt der Befehl REN, dass / in einem Dateinamen nicht gültig ist, und entfernt die führenden /-Schrägstriche aus dem Namen. REN gibt einen Syntaxfehler aus, wenn es / in der Mitte eines Zielnamens erkennt.

Möglicher RENAME-Fehler - ein einziger Befehl kann die gleiche Datei zweimal umbenennen!

Starten in einem leeren Testordner:

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

Ich glaube, dass die sourceMask *1* zuerst mit dem langen Dateinamen übereinstimmt, und die Datei wird in das erwartete Ergebnis 223456789.123.x umbenannt. RENAME sucht dann weiter nach weiteren zu verarbeitenden Dateien und findet die neu benannte Datei über den neuen kurzen Namen von 223456~1.X. Die Datei wird dann erneut umbenannt, was das Endergebnis 223456789.123.xx ergibt.

Wenn ich die 8.3-Namensgenerierung deaktiviere, liefert RENAME das erwartete Ergebnis.

Ich habe noch nicht alle Auslösebedingungen herausgefunden, die vorhanden sein müssen, um dieses merkwürdige Verhalten hervorzurufen. Ich hatte die Befürchtung, dass es möglich sein könnte, einen nicht enden wollenden rekursiven RENAME zu erzeugen, aber es ist mir nicht gelungen, einen solchen zu erzeugen.

Ich glaube, dass alle der folgenden Bedingungen erfüllt sein müssen, um den Fehler zu verursachen. Jeder fehlerhafte Fall, den ich gesehen habe, hatte die folgenden Bedingungen, aber nicht alle Fälle, die die folgenden Bedingungen erfüllten, waren fehlerhaft.

  • Kurze 8.3-Namen müssen aktiviert sein
  • Die sourceMask muss mit dem ursprünglichen langen Namen übereinstimmen.
  • Die anfängliche Umbenennung muss einen kurzen Namen erzeugen, der auch mit der sourceMask übereinstimmt
  • Der anfänglich umbenannte kurze Name muss später sortiert werden als der ursprüngliche kurze Name (falls er existierte?)
4
4
4
2014-12-16 10:13:11 +0000

Ähnlich wie bei exebook, ist hier eine C#-Implementierung, um den Zieldateinamen aus einer Quelldatei zu erhalten.

Ich habe 1 kleinen Fehler in den Beispielen von dbenham gefunden:

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

Hier ist der Code:

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

Und hier ist eine NUnit-Testmethode zum Testen der Beispiele:

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

    }
1
1
1
2014-04-09 17:07:37 +0000

Vielleicht kann jemand dies nützlich finden. Dieser JavaScript-Code basiert auf der Antwort von dbenham oben.

Ich habe sourceMask nicht sehr oft getestet, aber targetMask stimmt mit allen von dbenham angegebenen Beispielen überein.

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)
}
1
1
1
2016-10-13 01:27:15 +0000

Ich habe es geschafft, folgenden Code in BASIC zu schreiben, um Platzhalter für Dateinamen zu maskieren:

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