replace - 如何通过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
- 我必须更改
list.txt
替换C:\AAA
为的文件的路径,C:\EEE\AAA
并且必须添加.jpg
到其中的每一行list.txt
(因为错误地,其中的所有文件C:\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%"
现在我错过了.jpg
在文件中每条记录的末尾追加的部分%newExcludeFile%
,我在想是否有一种方法可以做到这一点,而无需在替换后再次迭代所有行。
解决方案
我建议先阅读 Stack Overflow 页面并提出以下问题:
Windows 命令解释器 (CMD.EXE) 如何解析脚本?
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%"
仅此而已。
- FOR with option
/F
从 file 中逐行读取C:\BBB\CCC\list.txt
。 - 由于 option ,每个非空行都使用反斜杠作为字符串分隔符拆分为子字符串
delims=\
。 - 第一个子字符串是驱动器号和冒号,由于选项而被忽略
tokens=2*
,除了从行尾字符开始查找,在这种情况下,整行也将被忽略。 - 第一个子字符串始终是,因此可以在此用例中保留
C:
默认值。eol=;
由于行尾字符没有被忽略的行,因为没有以分号开头的行,因此没有第一个以 . 开头的子字符串;
。 - 第二个子字符串位于根据分配
AAA
给指定循环变量的每一行上。I
tokens=2
- 但真正令人感兴趣的是每一行之后的剩余部分,
C:\AAA\
根据*
aftertokens=2
到 next 但一个循环变量分配而无需进一步行分割J
。
也可以使用FOR命令行:
(for /F "usebackq tokens=1,2* delims=\" %%I in ("%excludeFile%") do echo %%I\EEE\AAA\%%K.jpg)>"%newExcludeFile%"
此变体将驱动器号和冒号(第一个子字符串)从源文件复制到目标文件。
我是JREPL.BAT的粉丝,但是这个批处理/JScript 混合对于这项任务来说并不是真正必要的。
但是,这里的jrepl.bat
命令行使用 for /F
.
call jrepl.bat "(\\AAA\\.*)$" "\EEE$1.jpg" /F "%excludeFile%" /O "%newExcludeFile%"
它运行正则表达式搜索
\AAA\
每个反斜杠必须用一个反斜杠转义的字符串,因为反斜杠是搜索正则表达式中的转义字符,并且- 有
.*$
0 个或更多字符直到行尾 - 在用
(
和定义的标记组内)
将每个找到的字符串替换为
- 字符串
\EEE
(反斜杠没有被 JScript 异常转义)和 - 使用找到的字符串进行反向引用
$1
以保留它并 - 附上
.jpg
。
接下来,我想让这个答案的所有读者知道问题中发布的批处理文件的几行中编码不好的地方以及原因。
建议修改
if "%rootPath:~-1%"=="\" (set rootPath= %rootPath:~0,-1%)
set localPath=%rootPath%\backup_deleted_media_%_date%
至
if "%rootPath:~-1%" == "\" set "rootPath=%rootPath:~0,-1%"
set "localPath=%rootPath%\backup_deleted_media_%_date%"
在这种情况下,命令IF的参数是用 100% 正确的字符串比较语法指定的,正如我在 Windows 批处理文件中与NEQ、LSS、GTR 等等效的符号的回答中广泛描述的那样
- 第一个参数是第一个字符串
"%rootPath:~-1%"
; - 空格作为参数分隔符;
- 第二个参数是比较运算符
==
; - 空格作为参数分隔符;
- 第三个参数是第二个字符串
"\"
。
在调试批处理文件时可以看出,Windows 命令处理器 在执行命令IFif "%rootPath:~-1%"=="\"
之前自动更正缺少的空格==
到空格。因此,最好将字符串比较条件 100% 正确地写入批处理文件中,并在.if "%rootPath:~-1%" == "\"
==
在改进的命令行中,在命令SET=
的参数字符串中删除了右边的空格,因为不应该在开头用空格重新定义,正如我在使用 'set 后为什么没有字符串输出与 'echo %var%' 的回答中详细描述的那样var = text' 在命令行上?rootPath
两个SET命令的参数字符串另外包含在"
其中,也适用于包含 & 字符的路径,否则在路径中将被解释为 AND 运算符,以便在命令SET&
之后执行附加命令。请参阅我使用 Windows 批处理文件在单行上使用多个命令的答案,以了解其含义不在双引号参数字符串中。&
另请参阅我对两个几乎相同的批处理脚本之一中的语法错误的回答:“)”无法在此处进行语法处理,它描述了几个非常常见的语法问题。问题 1没有按照在命令提示符窗口中&()[]{}^=;!'+,`~
运行时 Windows 命令处理器输出的帮助所描述的文件/文件夹参数字符串包含空格或这些字符之一的要求,将文件/文件夹参数字符串括在双引号中。cmd /?
两条命令行
if not exist %localPath%\%%~pF md %localPath%\%%~pF
move %%F %localPath%\%%~pF
如果localPath
是例如用字符串定义的,也是非常有问题的C:\Temp\Test & Development
。
命令IF旨在在条件为真时运行一个命令。如果只需要在真实条件下执行单个命令,则不应使用(
,)
尽管始终可以仅使用一个命令定义命令块。这是批处理文件编码的第二个常见语法问题。
ASCII 表中有很多字符对于cmd.exe
处理批处理文件及其内部命令FOR都没有特殊含义,但是批处理文件编写的初学者倾向于使用小集合中的字符作为具有特殊含义的循环变量likea
或F
which 在用作循环变量时必须非常小心。
该命令popd
应始终在成功执行后使用pushd
,尤其是在pushd
将具有 UNC 路径的网络资源访问分配给驱动器号时。否则,在重复执行批处理文件时可能会发生最终使用所有驱动器号的情况。
最好的做法是尽可能使用可执行文件的完全限定文件名,以使批处理文件独立于环境变量PATH
,PATHEXT
并避免 Windows 命令处理器不必要的文件系统访问以查找通常仅使用其文件指定的文件名称如find
orfindstr
或wmic
。
这是发布的批处理文件代码,修复了所有或多或少的小问题:
@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
在大多数内部FOR循环中还进行/C:"%excludeFile%"
了更正。/C:"%excludeName%"
注意:此批处理文件未经我测试,因为我从未执行过它!
推荐阅读
- cookies - Jsoup 和 cookie - 我无法登录到所有子站点
- java - 计算正方形的角度距离
- c# - ResourceManager.GetString 在 .NET Core 中值为“”时返回“”
- c++ - 尝试从 C++ 访问 UWP 的 EmailMessage 类会导致“REGDB_E_CLASSNOTREG 类未注册”错误
- python - Pandas - 列中引号(“)符号后的单词可以大写吗?
- asp.net - 如何在剃须刀页面中自动增加属性
- c++ - 读取某些文件时出现分段错误,似乎与文件大小无关
- android - Xamarin 按钮从屏幕上掉下来
- amazon-s3 - 如何在写入 Redshift DW 之前从 S3 存储桶转换数据?
- wordpress - WordPress 循环限制