103 votes

Comment la commande RENAME de Windows interprète-t-elle les caractères génériques ?

Comment la commande RENAME (REN) de Windows interprète-t-elle les caractères génériques ?

La fonction HELP intégrée n'est d'aucune aide - elle ne prend pas du tout en compte les caractères génériques.

El Aide en ligne Microsoft technet XP n'est pas beaucoup mieux. Voici tout ce qu'il a à dire concernant les jokers :

"Vous pouvez utiliser des caractères de substitution ( * y ? ) dans l'un ou l'autre des paramètres du nom de fichier. Si vous utilisez des caractères génériques dans le nom de fichier2, les caractères représentés par les caractères génériques seront identiques aux caractères correspondants dans le nom de fichier1."

Pas très utile - cette déclaration peut être interprétée de plusieurs façons.

J'ai réussi à utiliser des caractères génériques dans le fichier nom de fichier2 à certaines occasions, mais il a toujours fallu procéder par essais et erreurs. Je n'ai pas été en mesure d'anticiper ce qui fonctionne et ce qui ne fonctionne pas. Fréquemment, j'ai dû recourir à l'écriture d'un petit lot script avec un script qui analyse chaque nom afin que je puisse construire chaque nouveau nom selon les besoins. Pas très pratique.

Si je connaissais les règles de traitement des caractères génériques, je pense que je pourrais utiliser la commande RENAME plus efficacement sans avoir à recourir aussi souvent au batch. Bien sûr, la connaissance des règles serait également bénéfique au développement du batch.

(Oui - il s'agit d'un cas où je poste une question et une réponse jumelées. J'en ai eu assez de ne pas connaître les règles et j'ai décidé de faire ma propre expérience. Je me suis dit que beaucoup d'autres personnes pourraient être intéressées par ce que j'ai découvert).

155voto

dbenham Points 10555

Ces règles ont été découvertes après des tests approfondis sur une machine Vista. Aucun test n'a été effectué avec l'unicode dans les noms de fichiers.

RENAME requiert 2 paramètres - un masque source, suivi d'un masque cible. Le sourceMask et le targetMask peuvent tous les deux contenir * et/ou ? des caractères de substitution. Le comportement des caractères de remplacement change légèrement entre les masques source et cible.

Note - REN peut être utilisé pour renommer un dossier, mais les caractères génériques sont no autorisé dans le masque source ou le masque cible lors du renommage d'un dossier. Si le masque de source correspond à au moins un fichier, alors le(s) fichier(s) sera(ont) renommé(s) et les dossiers seront ignorés. Si le masque de source ne correspond qu'à des dossiers et non à des fichiers, une erreur de syntaxe est générée si des caractères génériques apparaissent dans la source ou la cible. Si le masque de source ne correspond à rien, une erreur "fichier non trouvé" est générée.

De plus, lors du renommage de fichiers, les caractères génériques ne sont autorisés que dans la partie nom de fichier du masque de source. Les caractères génériques ne sont pas autorisés dans le chemin d'accès menant au nom du fichier.

masque source

Le masque de source fonctionne comme un filtre pour déterminer quels fichiers sont renommés. Les caractères génériques fonctionnent ici de la même manière qu'avec toute autre commande qui filtre les noms de fichiers.

  • ? - Correspond à tout caractère 0 ou 1 sauf . Ce caractère générique est gourmand - il consomme toujours le caractère suivant s'il ne s'agit pas d'un caractère . Cependant, il ne correspondra à rien sans échec si à la fin du nom ou si le caractère suivant est un .

  • *`** - Correspond à 0 ou plusieurs caractères _y compris_.` (avec une exception ci-dessous). Ce caractère générique n'est pas gourmand. Il correspondra au minimum ou au maximum nécessaire pour permettre aux caractères suivants de correspondre.

Tous les caractères qui ne sont pas des caractères génériques doivent correspondre à eux-mêmes, à l'exception de quelques cas particuliers.

  • . - Correspond à lui-même ou peut correspondre à la fin du nom (rien) s'il ne reste plus de caractères. (Note - un nom Windows valide ne peut pas se terminer par . )

  • {space} - Correspond à lui-même ou peut correspondre à la fin du nom (rien) s'il ne reste plus de caractères. (Note - un nom Windows valide ne peut pas se terminer par {space} )

  • *`.** à la fin - Correspond à n'importe quel 0 ou plus de caractères _sauf_.La terminaison.peut en fait être n'importe quelle combinaison de.y{space}pour autant que le tout dernier caractère du masque soit.C'est la seule et unique exception où*` ne correspond pas simplement à n'importe quel ensemble de caractères.

Les règles ci-dessus ne sont pas si complexes. Mais il existe une autre règle très importante qui rend la situation confuse : Le masque de source est comparé à la fois au nom long et au nom court 8.3 (s'il existe). Cette dernière règle peut rendre l'interprétation des résultats très délicate, car il n'est pas toujours évident de savoir si le masque correspond au nom court.

Il est possible d'utiliser RegEdit pour désactiver la génération de noms courts 8.3 sur les volumes NTFS, auquel cas l'interprétation des résultats du masque de fichier est beaucoup plus simple. Tous les noms courts qui ont été générés avant la désactivation des noms courts seront conservés.

masque cible

Note - Je n'ai pas fait de tests rigoureux, mais il semble que ces mêmes règles fonctionnent également pour le nom de la cible de la commande COPY.

Le masque cible spécifie le nouveau nom. Il est toujours appliqué au nom long complet ; le masque cible n'est jamais appliqué au nom court 8.3, même si le masque source correspond au nom court 8.3.

La présence ou l'absence de caractères génériques dans le masque source n'a aucune incidence sur le traitement des caractères génériques dans le masque cible.

Dans la discussion suivante - c représente tout caractère qui n'est pas * , ? o .

Le masque cible est traité par rapport au nom source, strictement de gauche à droite, sans retour en arrière.

  • c - Avance la position dans le nom de la source seulement si le caractère de la source n'est pas . et ajoute toujours c au nom de la cible. (Remplace le caractère qui était dans la source par c mais ne remplace jamais . )

  • ? - Fait correspondre le caractère suivant du nom long source et l'ajoute au nom cible pour autant que le caractère source ne soit pas . Si le caractère suivant est . ou si elle se trouve à la fin du nom de la source, aucun caractère n'est ajouté au résultat et la position actuelle dans le nom de la source reste inchangée.

  • *``** à la fin de targetMask - Ajoute tous les caractères restants de la source à la cible. S'il est déjà à la fin de la source, il ne fait rien.

  • *`c** - Recherche tous les caractères source à partir de la position actuelle jusqu'à la dernière occurrence dec(correspondance gourmande sensible à la casse) et ajoute le jeu de caractères correspondant au nom de la cible. Sicn'est pas trouvé, alors tous les caractères restants de la source sont ajoutés, suivis dec` C'est la seule situation que je connaisse où le filtrage des fichiers Windows est sensible à la casse.

  • *`.** - Correspond à tous les caractères de la source à partir de la position actuelle jusqu'à l'emplacement de l'objet. _dernier_ apparition de.(greedy match) et ajoute le jeu de caractères correspondant au nom de la cible. Si.n'est pas trouvé, alors tous les caractères restants de la source sont ajoutés, suivis de.`

  • *`?`** - Ajoute tous les caractères restants de la source à la cible. S'il est déjà à la fin de la source, il ne fait rien.

  • . sans *`** à l'avant - Avance la position à l'avant par le biais de la source _premièrement_ apparition de.sans copier aucun caractère, et ajoute.au nom de la cible. Si.n'est pas trouvé dans la source, puis avance jusqu'à la fin de la source et ajoute.` au nom de la cible.

Une fois que le masque cible a été épuisé, tout message de fin de ligne . y {space} sont coupés à la fin du nom de la cible résultante parce que les noms de fichiers Windows ne peuvent pas se terminer avec . o {space}

Quelques exemples pratiques

Substitue un caractère en 1ère et 3ème positions avant toute extension (ajoute un 2ème ou 3ème caractère s'il n'existe pas encore)

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

Changer l'extension (finale) de chaque fichier

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

Ajouter une extension à chaque fichier

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

Supprimez toute extension supplémentaire après l'extension initiale. Notez qu'une ? doit être utilisé pour préserver le nom complet existant et l'extension initiale.

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)

Idem que ci-dessus, mais filtre les fichiers dont le nom initial et/ou l'extension dépassent 5 caractères afin qu'ils ne soient pas tronqués. (On pourrait évidemment ajouter un ? à chaque extrémité de targetMask pour préserver les noms et extensions jusqu'à 6 caractères)

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)

Changez les caractères après le dernier _ en nom et tenter de préserver l'extension. (Ne fonctionne pas correctement si _ apparaît dans l'extension)

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)

Tout nom peut être décomposé en composants qui sont délimités par . Les caractères ne peuvent être ajoutés ou supprimés qu'à la fin de chaque composant. Les caractères ne peuvent pas être supprimés ou ajoutés au début ou au milieu d'un composant tout en préservant le reste avec des caractères génériques. Les substitutions sont autorisées partout.

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 les noms courts sont activés, un masque de source d'au moins 8 caractères est nécessaire. ? pour le nom et au moins 3 ? pour l'extension correspondra à tous les fichiers car il correspondra toujours au nom court 8.3.

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

Quirk/bug utile ? pour la suppression des préfixes de nom

Ce poste de SuperUser décrit comment un ensemble de barres obliques ( / ) peut être utilisé pour supprimer les caractères de tête (à l'exception de . ) à partir d'un nom de fichier. Une barre oblique est nécessaire pour chaque caractère à supprimer. J'ai confirmé ce comportement sur une machine Windows 10.

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

Malheureusement, en menant / ne peut supprimer . dans un nom. La technique ne peut donc pas être utilisée pour supprimer un préfixe qui contient . . Par exemple :

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

Cette technique ne fonctionne que si les masques source et cible sont placés entre guillemets. Tous les formulaires suivants qui ne contiennent pas les guillemets requis échouent avec cette erreur : 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

El / ne peut pas être utilisé pour supprimer les caractères au milieu ou à la fin d'un nom de fichier. Elle ne peut supprimer que les caractères de tête (préfixe). Notez également que cette technique ne fonctionne pas avec les noms de dossiers.

Techniquement, le / ne fonctionne pas comme un joker. Il s'agit plutôt d'une simple substitution de caractères à la suite du caractère c règle de masque de cible. Mais ensuite, après la substitution, la commande REN reconnaît que / n'est pas valide dans un nom de fichier, et supprime l'élément de tête du nom de fichier. / les barres obliques du nom. REN donne une erreur de syntaxe s'il détecte / au milieu d'un nom de cible.

Bug possible de RENAME - une seule commande peut renommer le même fichier deux fois !

En commençant par un dossier de test vide :

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

Je crois que le masque de source *1* correspond d'abord au nom de fichier long, et le fichier est renommé avec le résultat attendu de 223456789.123.x . RENAME continue ensuite à chercher d'autres fichiers à traiter et trouve le fichier nouvellement nommé via le nouveau nom court de 223456~1.X . Le fichier est ensuite renommé à nouveau, ce qui donne le résultat final suivant 223456789.123.xx .

Si je désactive la génération de noms 8.3, le RENAME donne le résultat attendu.

Je n'ai pas encore déterminé toutes les conditions de déclenchement qui doivent exister pour induire ce comportement étrange. Je craignais qu'il soit possible de créer un RENAME récursif sans fin, mais je n'ai jamais pu en provoquer un.

Je pense que tous les éléments suivants doivent être vrais pour induire le bug. Tous les cas de bogues que j'ai vus présentaient les conditions suivantes, mais tous les cas qui remplissaient les conditions suivantes n'étaient pas des bogues.

  • Les noms courts 8.3 doivent être activés
  • Le masque source doit correspondre au nom long original.
  • Le renommage initial doit générer un nom court qui correspond également au masque de la source.
  • Le nom court initial renommé doit être trié après le nom court original (s'il a existé).

5voto

amrunning Points 41

Similaire à exebook, voici une implémentation C# pour obtenir le nom du fichier cible à partir d'un fichier source.

J'ai trouvé une petite erreur dans les exemples de dbenham :

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

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

Et voici une méthode de test NUnit pour tester les exemples :

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

    }

1voto

exebook Points 300

Peut-être que quelqu'un peut trouver cela utile. Ce code JavaScript est basé sur la réponse de dbenham ci-dessus.

Je n'ai pas testé sourceMask très bien, mais targetMask correspond à tous les exemples donnés par 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)
}

1voto

eoredson Points 131

J'ai réussi à écrire ce code en BASIC pour masquer les noms de fichiers joker :

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

-1voto

Je dois faire une annotation :

"ren" n'est PAS égal à "rename", du moins pas en 2021 sur Windows 10 / Server 2016.

La commande rename fonctionne ... mais ren a un bug

l'étoile est supprimée du nom de la cible au cas où elle serait combinée avec des variables d'environnement comme %random% ou %date%.

%random% est pris une seule fois lors de l'utilisation de la commande ren... ren .txt %random% .txt sera effectivement "gourmand", l'étoile est mangée. Ren produira des choses comme 12521.txt avec le premier fichier et ensuite arrêtera de dire que le fichier en double existe...

Exemple : J'ai

johndoe.txt

janedoe.txt

avec ren

ren "*.txt" "%random%*.txt"

Un nom de fichier en double existe, ou le fichier est introuvable.

Les spectacles de Dir

12533.txt

janedoe.txt

with rename (%random% is now different):

rename "*.txt" "%random%*.txt"

echo

johndoe31008.txt

janedoe31008.txt

La commande renommer ayant exactement les mêmes paramètres fonctionne.

Lorsque ma femme me dira que je dois être puni, je déposerai une demande d'assistance auprès de Microsoft. J'y suis éligible, mais la dernière fois que j'ai fait cela, j'ai eu une expérience très désagréable en faisant cela au niveau "professionnel" à partir de MSDN. Je viens également de l'industrie du logiciel, mais pas de Microsoft, mais je peux prédire que cette demande recevra la plus faible priorité et la plus faible gravité, alors je ne le fais pas.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X