如何通过cmd用jrepl替换+追加?

问题描述

在Windows中,我有一个批处理文件,用于处理文本文件C:\BBB\CCC\list.txt,以确定要移动的文件。它应移动文件夹(%folder%)及其子文件夹中的所有文件,但仅在以下情况下

  • 文件名称中,未在输入(%excludeName%)中设置年份
  • 文件未在文本文件%excludeFile%)中列出。

在那个list.txt中,我有数百万行,例如:

C:\AAA\XXX\ZZZ\image_1.jpg
C:\AAA\XXX\KKK\image_2.jpg
C:\AAA\XXX\ZZZ\pdf_1.pdf

这是批处理文件,可以正常运行:

@echo off
setlocal EnableExtensions disableDelayedExpansion
rem // Define constants here:
set "folder=C:\AAA"
set "excludeFile=C:\BBB\CCC\list.txt"
set /p excludeName="Year not to delete: "
echo:
set /p rootPath="Backup folder path: " 
FOR /f %%a in ('WMIC OS GET LocalDateTime ^| find "."') DO Set _DTS=%%a
Set _date=%_DTS:~0,4%%_DTS:~4,2%%_DTS:~6,2%

if "%rootPath:~-1%"=="\" (set rootPath= %rootPath:~0,-1%)

set localPath=%rootPath%\backup_deleted_media_%_date%
echo:
rem // Change to the root directory:
pushd "%folder%" && (
    rem // Loop through all files but exclude those listed in the list file:

    for /F "delims= eol=|" %%F in ('
    dir /B /S /A:-D "*.*" ^| findstr /V /L /I /X /G:"%excludeFile%"') do (
        for /D %%I in ("%%F\..") do (

            echo.%%~nxI|findstr /C:"%excludeFile%" >nul 2>&1
            if not errorlevel 1 (
                echo Found
            ) else (
                if not exist %localPath%\%%~pF md %localPath%\%%~pF
                move %%F %localPath%\%%~pF

            )
        )
    )
)
rem // Return from currently iterated directory to root directory:
endlocal

cmd /k

我现在需要的是另一个批处理文件,用于执行相同的操作,但是:

  • folder不是C:\AAA,而是C:\EEE\AAA
  • 我必须更改{{1}中的文件的路径,用list.txt替换C:\AAA,并且必须将C:\EEE\AAA添加到该{{1}的每一行中}(因为错误.jpg及其子文件夹中的所有文件都具有该扩展名,例如list.txtC:\EEE\AAA,...),然后再执行相同的操作。我希望这些更改位于新文件image_1.jpg.jpg)中,而不是原始的pdf_1.pdf.jpg

所以我添加了:

%newExcludeFile%

我这样做是为了从文件list.txt删除创建文件set "newExcludeFile=C:\BBB\CCC\list_new.txt" set "SEARCHTEXT=\AAA\" set "REPLACETEXT=\EEE\AAA\"

%newExcludeFile%

现在,我缺少了在文件%excludeFile%中每条记录的末尾添加if exist "%newExcludeFile%" del "%newExcludeFile%" call jrepl "%sEARCHTEXT%" "%rEPLACETEXT%" /x /m /l /f "%excludeFile%" /o "%newExcludeFile%" 的部分,我在考虑是否有一种方法可以在不重复所有行的情况下进行操作取代。

解决方法

我建议您先阅读Stack Overflow页面,并提出以下问题:
How does the Windows Command Interpreter (CMD.EXE) parse scripts?

接下来,我发布该任务的简单解决方案,从输入文件C:\BBB\CCC\list_new.txt创建输出文件C:\BBB\CCC\list.txt,并在每行开头用C:\AAA替换C:\EEE\AAA并追加另外在每行.jpg的末尾,这样C:\BBB\CCC\list.txt

中的行
C:\AAA\XXX\ZZZ\image_1.jpg
C:\AAA\XXX\KKK\image_2.jpg
C:\AAA\XXX\ZZZ\pdf_1.pdf

成为文件C:\BBB\CCC\list_new.txt

C:\EEE\AAA\XXX\ZZZ\image_1.jpg.jpg
C:\EEE\AAA\XXX\KKK\image_2.jpg.jpg
C:\EEE\AAA\XXX\ZZZ\pdf_1.pdf.jpg

此任务可以通过以下方式完成:

@echo off
set "excludeFile=C:\BBB\CCC\list.txt"
set "newExcludeFile=C:\BBB\CCC\list_new.txt"
(for /F "usebackq tokens=2* delims=\" %%I in ("%excludeFile%") do echo C:\EEE\AAA\%%J.jpg)>"%newExcludeFile%"

这就是全部。

    具有选项/F
  • FOR 从文件C:\BBB\CCC\list.txt中读取另一行。
  • 由于选项delims=\,使用反斜杠作为字符串定界符将每个非空行分成多个子字符串。
  • 第一个子字符串是驱动器号和冒号,由于选项tokens=2*而被忽略,只是从行尾字符开始,在这种情况下,整行也将被忽略。
  • 第一个子字符串始终为C:,因此在此用例中可以保留默认的eol=;。没有行由于行尾字符而被忽略,因为没有行以分号开头,因此没有第一个子字符串以;开头。
  • 第二个子字符串在每行AAA上,并根据I分配给指定的循环变量tokens=2
  • 但是真正令人感兴趣的是每行上C:\AAA\之后的剩余部分,该部分没有根据*之后的tokens=2分配给下一行,而是循环变量{{1} }。

也可以使用 FOR 命令行:

J

此变体将驱动器号和冒号(第一个子字符串)从源文件复制到目标文件。

我是JREPL.BAT的粉丝,但是此批处理/ JScript混合对于此任务并不是真正必需的。
但是,这是使用(for /F "usebackq tokens=1,2* delims=\" %%I in ("%excludeFile%") do echo %%I\EEE\AAA\%%K.jpg)>"%newExcludeFile%" 的命令行与使用jrepl.bat的命令行相同的功能。

for /F

它运行正则表达式搜索

  • 字符串call jrepl.bat "(\\AAA\\.*)$" "\EEE$1.jpg" /F "%excludeFile%" /O "%newExcludeFile%" ,其中每个反斜杠必须再转义一个反斜杠,因为反斜杠是搜索正则表达式中的转义字符,并且
  • \AAA\表示0个或更多字符,直到行尾为止
  • 在由.*$(定义的标记组中

将找到的每个字符串替换为

  • 字符串)(JScript异常未转义反斜杠)和
  • 使用\EEE反向引用找到的字符串来保留它,并且
  • 附加了$1

接下来,我想让所有答复的读者知道问题中张贴的批处理文件中几行编码不好的原因。

建议修改

.jpg

if "%rootPath:~-1%"=="\" (set rootPath= %rootPath:~0,-1%)

set localPath=%rootPath%\backup_deleted_media_%_date%

在这种情况下,针对字符串比较的命令 IF 的参数指定了100%正确的语法,这在我对Symbol equivalent to NEQ,LSS,GTR,etc. in Windows batch files的回答中对

  • 第一个参数第一个字符串 if "%rootPath:~-1%" == "\" set "rootPath=%rootPath:~0,-1%" set "localPath=%rootPath%\backup_deleted_media_%_date%"
  • 空格作为参数分隔符;
  • 第二个参数比较运算符 "%rootPath:~-1%"
  • 空格作为参数分隔符;
  • 第三个参数第二个字符串 ==

debugging the batch file上可以看到Windows命令处理器在执行命令 IF之前会自动纠正"\",其中if "%rootPath:~-1%"=="\"==之间有空格。因此,最好在批处理文件中将字符串比较条件写成100%正确,并在if "%rootPath:~-1%" == "\"周围加上空格。


在改进的命令行中,在命令 SET 的参数字符串中删除了==的空格权,因为=不应像前面详细描述的那样重新定义空格根据我对Why is no string output with 'echo %var%' after using 'set var = text' on command line?

的回答

两个 SET 命令的参数字符串另外包含在rootPath中,以同样适用于包含与号字符的路径,否则path中的"将被解释为AND运算符,以便在命令 SET 之后执行其他命令。有关&的含义,请参见我在single line with multiple commands using Windows batch file上的答案,该含义不在双引号参数字符串中。

另请参阅我在syntax error in one of two almost-identical batch scripts: ")" cannot be processed syntactically here上的回答,该回答描述了几种非常常见的语法问题问题1 没有按照Windows命令处理器输出的帮助,将文件/文件夹参数字符串包含空格或这些字符之一&的文件/文件夹参数字符串中的双引号引起来在命令提示符窗口中运行&()[]{}^=;!'+,`~上。

两条命令行

cmd /?
例如,如果if not exist %localPath%\%%~pF md %localPath%\%%~pF move %%F %localPath%\%%~pF 是用字符串localPath定义的,那么

也是很成问题的。


命令 IF 设计为在条件为true的情况下运行一个命令。如果只需要在真实条件下执行单个命令,则不应该使用C:\Temp\Test & Development(,尽管始终可以仅使用一个命令来定义命令块。这是批处理文件编码中的第二种常见语法问题


ASCII table中有很多字符,对于)处理批处理文件及其内部命令 FOR 而言,它们没有特殊意义,但对于成批的初学者来说,文件编写倾向于使用小集合中的字符作为循环变量,它们具有特殊含义,例如cmd.exea,在用作循环变量时必须非常小心。


在成功执行F之后,应始终使用命令popd,特别是如果pushd将具有UNC路径的网络资源访问权限分配给驱动器号时,尤其要使用该命令。否则,在重复执行批处理文件时可能会发生所有驱动器号最终都被使用。


最好使用可执行文件的标准文件名,以使批处理文件独立于环境变量pushdPATH,并避免Windows命令对文件系统的不必要访问处理器来查找通常仅使用其文件名指定的文件,例如PATHEXTfindfindstr


以下是有问题的批处理文件代码,已修复了几乎所有小问题:

wmic

在大多数内部 FOR 循环中,@echo off setlocal EnableExtensions DisableDelayedExpansion rem // Define constants here: set "folder=C:\AAA" set "excludeFile=C:\BBB\CCC\list.txt" set /P "excludeName=Year not to delete: " echo: set /P "rootPath=Backup folder path: " for /F %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime ^| %SystemRoot%\System32\find.exe "."') do set "_DTS=%%I" set "_date=%_DTS:~0,4%%_DTS:~4,2%%_DTS:~6,2%" if "%rootPath:~-1%" == "\" set "rootPath=%rootPath:~0,-1%" set "localPath=%rootPath%\backup_deleted_media_%_date%" echo: rem // Change to the root directory: pushd "%folder%" && ( rem // Loop through all files but exclude those listed in the list file: for /F "eol=| delims=" %%I in ('dir /B /S /A:-D "*.*" ^| %SystemRoot%\System32\findstr.exe /V /L /I /X /G:"%excludeFile%"') do ( for /D %%J in ("%%I\..") do ( echo.%%~nxJ|%SystemRoot%\System32\findstr.exe /C:"%excludeName%" >nul if errorlevel 1 ( if not exist "%localPath%\%%~pI" md "%localPath%\%%~pI" move "%%I" "%localPath%\%%~pI" ) else echo Found ) ) popd ) rem // Return from currently iterated directory to root directory: endlocal %ComSpec% /K 已更正为/C:"%excludeFile%"

注意:我从未测试过该批处理文件,因为我从未执行过该文件!