简体   繁体   English

Windows批处理文件和正则表达式:重命名所有没有给定字符串的文件夹

[英]Windows batch file and regex: rename all folders without given string

Situation: 情况:

I have many folders which are named like an email-address, eg username@domain.tld. 我有许多文件夹,其名称类似于电子邮件地址,例如username@domain.tld。 These folders has to be renamed into just the username. 必须将这些文件夹重命名为用户名。 so all characters until the "@" is my given string. 所以直到“@”的所有字符都是我给定的字符串。 (without the "@") (没有“@”)

Current foldernames: 目前的文字名称:

  • username1@domain.tld username1@domain.tld
  • username2@domain.tld username2@domain.tld

Requested foldernames: 请求的foldernames:

  • username1 USERNAME1
  • username2 USERNAME2

Problem: 问题:

  1. My Regex is wrong and even with the regex generator I can't get it right. 我的正则表达式是错误的,即使使用正则表达式生成器,我也无法做到正确。
  2. Even if I have the right Regex I do not know, how I shall rename the right way (see at for loop). 即使我有正确的正则表达式我不知道,我将如何重命名正确的方式(参见for循环)。

Thank you for every help! 谢谢你的帮助!

@ECHO off
SETLOCAL ENABLEDELAYEDEXPANSION

SET pathMail="C:\Users\Username\Desktop\Batchtest\Folderstructure"
PUSHD %pathMail%

ECHO "::Extract string from folder"
FOR /f %%G in ('DIR /b /ad ^| FINDSTR /r "[a-z].*[^@]"') DO (
    ::wrong logic!?
    REN %%G=%%G
)

POPD
:EOF

You might be able to do it with a one-liner (UNTESTED): 您可以使用单行(UNTESTED)来执行此操作:

for /f "delims=@ tokens=1*" %a in ('dir /s /ad /b *@*') do @echo ren %a@%b %~nxa

How it works: 这个怎么运作:

  1. The dir /b /ad /s *@* command produces a bare listing of full pathnames for all folders containing a @. dir /b /ad /s *@*命令为包含@的所有文件夹生成一个完整路径名的裸列表。

  2. The delims=@ option splits each line into two: the first part before the @, and the second part after the @. delims=@选项将每一行分成两行:@之前的第一部分和@之后的第二部分。

  3. The ren command renames the full pathname to the part before the @. ren命令将完整路径名重命名为@之前的部分。 The %~nxa just extracts the name (and any extension) from the full path. %~nxa只从完整路径中提取名称(和任何扩展名)。

  4. The echo command is there, just so you can see what it's going to do. echo命令就在那里,这样你就可以看到它将要做什么。 Once you're satisfied it will do the right thing, remove it. 一旦你满意,它会做正确的事情,删除它。

NB NB

If you run this from a batch file, instead of interactively, use %% instead of % . 如果从批处理文件中运行此操作,而不是以交互方式运行,请使用%%而不是%

findstr returns the entire line if it contains a match, not the matching part only, so you need to split off the @ character and everything after programmatically. findstr返回整行,如果它包含匹配,而不是匹配的部分,所以你需要在编程后拆分@字符和所有内容。

You do not need to use pushd and popd within a setlocal / endlocal block because, the latter localises the current working directory too. 您不需要在setlocal / endlocal块中使用pushdpopd ,因为后者也会对当前工作目录进行本地化。

Avoid wrong-label remarks like :: within blocks () of code, because they may cause trouble! 避免使用像:: block ()代码中的错误标签备注,因为它们可能会造成麻烦!

The following code does what you want: 以下代码执行您想要的操作:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

set "pathMail=C:\Users\Username\Desktop\Batchtest\Folderstructure"
cd /D "%pathMail%"

for /F %%G in ('
    dir /B /A:D "?*@*" ^| findstr /I /R "^[a-z][^@]*@[^@][^@]*$"
') do (
    set "ITEM=%%G"
    setlocal EnableDelayedExpansion
    call :LEN ILEN ITEM
    set "RIGHT=!ITEM:*@=!"
    call :LEN RLEN RIGHT
    for /F "delims=" %%N in ('set /A "ILEN-=!RLEN!+1"') do (
        rem Remove `ECHO` to actually rename any directories:
        ECHO ren "%%G" "!ITEM:~,%%N!"
    )
    endlocal
)
endlocal
exit /B

:LEN output input
setlocal EnableDelayedExpansion
set /A "SLEN=0"
if "%~2"=="" goto :END
set "STRING=!%~2!"
if not defined STRING goto :END
set /A "SLEN=1"
for %%I in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
    if not "!STRING:~%%I!"=="" (
        set /A "SLEN+=%%I"
        set "STRING=!STRING:~%%I!"
    )
)
:END
if "%~1"=="" (
    echo(%SLEN%
    endlocal
) else (
    endlocal & set "%~1=%SLEN%"
)
exit /B

What happens: 怎么了:

  • cd replaces pushd / popd as setlocal / endlocal suffices as aforementioned; 如上所述, cdpushd / popd替换为setlocal / endlocal就足够了;
  • findstr is used to return only directory whose names start with a letter, followed by any number of characters not containing @ , followed by the @ character, followed by at least one character other than @ ; findstr用于仅返回名称以字母开头的目录,后跟任意数量的不包含@的字符,后跟@字符,后跟至少一个除@以外的字符;
  • the subroutine :LEN is called to determine the length (number of characters) of the current directory name, the result is stored in variable ILEN ; 子程序:LEN调用:LEN来确定当前目录名的长度(字符数),结果存储在变量ILEN ;
  • everything up to and including the @ is removed from the directory name (by substring replacement syntax), the result is held by variable RIGHT ; 从目录名中删除包含@所有内容(通过子字符串替换语法),结果由变量RIGHT ;
  • the subroutine :LEN is called to determine the length of the string in RIGHT , the result is given to variable RLEN ; 子程序:LEN调用:LEN来确定RIGHT字符串的长度,结果赋予变量RLEN ;
  • the difference between the two length numbers minus 1 is calculated by set /A , the result is used for getting the string portion before the @ symbol (by substring expansion syntax); 两个长度数减1之间的差值由set /A计算,结果用于获取@符号之前的字符串部分(通过子字符串扩展语法);

The code looks a bit complicated, especially because of toggling delayed expansion, computing string lengths rather than using intelligent substring replacement syntax and passing variable names rather than string values to the subroutine. 代码看起来有点复杂,特别是因为切换延迟扩展,计算字符串长度而不是使用智能子字符串替换语法并将变量名称而不是字符串值传递给子例程。 However, all this has been done to avoid trouble with directory names which contain special characters like ! 但是,所有这些都是为了避免使用包含特殊字符的目录名称的麻烦! , % , = , etc. %=

The great advantace of this approach is that it works even for symbols that consist of more than just a single character (supposing the dir and findstr patterns are adapted accordingly). 这种方法的巨大优势在于它甚至可以用于由不止一个字符组成的符号(假设dirfindstr模式相应地进行了调整)。


If it is guaranteed that the characters ^ and % cannot occur in the directory names, the following code works fine and is a bit simpler ( " would also be a harmful character but this is prohibited for file/dir. names anyway): 如果保证在目录名中不能出现字符^% ,则以下代码可以正常工作并且更简单一些( "也可能是有害字符,但无论如何都禁止使用file / dir。”):

@echo off
setlocal EnableExtensions DisableDelayedExpansion

set "pathMail=C:\Users\Username\Desktop\Batchtest\Folderstructure"
cd /D "%pathMail%"

for /F %%G in ('
    dir /B /A:D "?*@*" ^| findstr /I /R "^[a-z][^@]*@[^@][^@]*$"
') do (
    call :SUB ITEM "%%G"
    rem Remove `ECHO` to actually rename any directories:
    call ECHO ren "%%G" "%%ITEM%%"
)
endlocal
exit /B

:SUB output %input%
set "LEFT=%~2"
if not defined LEFT goto :EOF
set "LEFT=%LEFT:@=" & rem "%"
if "%~1"=="" (
    echo "%LEFT%"
) else (
    set "%~1=%LEFT%"
)
goto :EOF

The core function is the substring replacement hack set "LEFT=%LEFT:@=" & rem "%" in the subroutine :SUB , which removes the @ sign and everything after. 核心函数是子程序中的子串替换黑客set "LEFT=%LEFT:@=" & rem "%" :SUB ,它删除了@符号及其后的所有内容。 Credits for this method go to Aacini -- see this post . 这种方法的积分转到Aacini - 请看这篇文章


For the sake of completeness, here is a simple solution that works only if the symbol consists of a single character ( @ ): 为了完整起见,这是一个简单的解决方案,仅当符号由单个字符( @ )组成时才有效:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

set "pathMail=C:\Users\Username\Desktop\Batchtest\Folderstructure"
cd /D "%pathMail%"

for /F "eol=| delims=" %%G in ('
    dir /B /A:D "?*@*" ^| findstr /I /R "^[a-z][^@]*@[^@][^@]*$"
') do (
    for /F "eol=| delims=@" %%N in ("%%G") do (
        rem Remove `ECHO` to actually rename any directories:
        ECHO ren "%%G" "%%N"
    )
)
endlocal
exit /B

It is an improved variant of Klitos Kyriacou's approach . 它是Klitos Kyriacou方法的改进版本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM