我有一些文件
afjj.txt
agk.png
beta.tt
Ritj.rar
我使用此脚本按字母顺序将文件移动到自动生成的文件夹中
a
|
|----> afjj.txt
|----> agk.png
b
|----> beta.tt
R
|----> Ritj.rar
我使用此代码
@echo off
setlocal
for %%i in (*) do (
set name=%%i
setlocal enabledelayedexpansion
if not "!name!" == "%0" (
set first=!name:~0,1!
md !first! 2>nul
if not "!first!" == "!name!" move "!name!" "!first!\!name!"
)
)
有什么问题?如果我双击这个批次,批次不起作用,不移动。
但是这个批处理可以从命令行运行。
为什么?
注意:我使用 Windows Server 2016
P.S:从命令行我使用这个命令并且可以工作,但如果我直接双击.bat
则不行move.bat "Z:\# 2020\Alfa\test"
第一个错误是命名批处理文件
move.bat
。将批处理文件命名为Windows命令从来都不是一个好主意,因为这通常会引起麻烦。另请参阅:SS64.com - Windows CMD 命令的 A-Z 索引。
第二个错误是在循环内使用
setlocal enabledelayedexpansion
而没有在与 endlocal
一样频繁执行的同一循环内使用相应的 setlocal
。请阅读这个答案,了解有关命令SETLOCAL和ENDLOCAL的详细信息。命令 SETLOCAL 在循环的每次迭代中将多个数据压入堆栈,并且在每次循环迭代时,数据永远不会从同一循环中的堆栈中弹出。随着越来越多的数据被推入堆栈,结果迟早会出现堆栈溢出,具体取决于要处理的文件数量。
第三个错误是期望当前目录始终是批处理文件的目录。这种期望常常无法实现。
第四个错误是使用循环迭代文件列表,这些文件列表在每次执行 FOR 循环体中的命令时都会永久更改。相关代码中的循环通常适用于以 NTFS 作为文件系统的存储介质,但不适用于使用 FAT32 或 exFAT 作为文件系统的存储介质。
第五个错误是期望
%0
始终扩展为当前执行的批处理文件的名称(带文件扩展名,但没有文件路径),如果使用完全限定的文件名(驱动器 + 路径 + 名称)执行批处理文件,则不正确。 + 扩展名),或仅使用文件名而不带文件扩展名,或使用相对路径。
第六个错误是在创建子文件夹时未将文件夹名称括在双引号中,这会导致文件名异常以“&”开头。
第七个错误是没有考虑处理以点开头的正确文件名,例如
.htaccess
,在这种情况下,第二个字符必须用作子文件夹的名称,但第二个字符也是点。这是一种非常罕见的情况,但也有可能文件名以一个或多个空格开头。在这种情况下,也应使用文件名的第一个非空格字符,因为 Windows 默认情况下会阻止创建名称仅为空格字符的文件夹。
解决方案是使用以下带有注释的批处理文件,其名称为
MoveToFolders.cmd
或 MyMove.bat
。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Get folder path of batch file assigned to an environment
rem variable. This folder path ends always with a backslash.
set "FilesFolder=%~dp0"
rem Optionally support calling of this batch file with another folder
rem path without checking if the passed string is really a folder path.
if not "%~1" == "" set "FilesFolder=%~1"
rem Replace all / by \ as very often people use / as directory separator
rem which is wrong because the directory separator is \ on Windows.
set "FilesFolder=%FilesFolder:/=\%"
rem The folder path should end always with a backslash even on folder
rem path is passed as an argument to the batch file on calling it.
if not "%FilesFolder:~-1%" == "\" set "FilesFolder=%FilesFolder%\"
rem Get a list of files in specified folder including hidden files loaded
rem into the memory of running command process which does not change on
rem the iterations of the loop below. Then iterate over the files list and
rem move the files to a subfolder with first none dot and none space character
rem of file name as folder name with the exception of the running batch file.
for /F "eol=| delims=" %%i in ('dir "%FilesFolder%" /A-D /B 2^>nul') do if /I not "%FilesFolder%%%i" == "%~f0" (
set "FileName=%%i"
set "FirstPart="
for /F "eol=| delims=. " %%j in ("%%i") do set "FirstPart=%%j"
if defined FirstPart (
setlocal EnableDelayedExpansion
set "TargetFolderName=%FilesFolder%!FirstPart:~0,1!"
md "!TargetFolderName!" 2>nul
if exist "!TargetFolderName!\" move "%FilesFolder%!FileName!" "!TargetFolderName!\"
endlocal
)
)
rem Restore the previous execution environment.
endlocal
批处理文件也可以使用文件夹路径作为参数来启动,以处理此文件夹中的文件,而无需检查传递的参数字符串是否确实引用了现有文件夹。
如果对如何验证传递的参数字符串是否确实引用现有文件夹感兴趣,请仔细阅读如何在字符串中有括号时用子字符串替换字符串的答案。
要了解所使用的命令及其工作原理,请打开命令提示符窗口,执行以下命令,并完整、仔细地阅读每个命令显示的帮助页面。
call /?
dir /?
echo /?
endlocal /?
for /?
if /?
md /?
move /?
rem /?
set /?
setlocal /?
这是一个稍微修改过的脚本。请注意,我删除了
!first!
变量,因为它不是必需的。我还建立了一项安全措施,如果无法 pushd
到您传递给 %~1
的给定目录,它将不会继续移动。否则,它可能会将文件移动到您不希望它移动文件的路径中。即您启动脚本的工作目录。
@echo off
setlocal enabledelayedexpansion
pushd "%~1" && echo( || goto :end
for %%i in (*.test) do (
set "name=%%~ni"
if not "%%~nxi" == "%~nx0" (md !name:~0,1!)2>nul
if not "!name:~0,1!" == "%%~i" move "%%~i" "!name:~0,1!\%%~i"
)
popd
goto :eof
:end
echo The directory you entered does not exist. Exited script..
pause
注意,通过上述操作,您还可以将目录拖到批处理文件中,批处理文件将处理该目录。
或者,如果您打算双击,而不使用
cmd
中的参数。
@echo off
setlocal enabledelayedexpansion
for %%i in (*.test) do (
set "name=%%~ni"
if not "%%~nxi" == "%~nx0" (md !name:~0,1!)2>nul
if not "!name:~0,1!" == "%%~i" move "%%~i" "!name:~0,1!\%%~i"
)
pause
略有不同。
如果双击此脚本将在当前目录上运行,或者在不指定路径的情况下在命令行中运行。
但它也将允许您提供一条通往它的路径。
@( SETLOCAL ENABLEDELAYEDEXPANSION
ECHO OFF
SET "_PATH=%~1"
IF NOT DEFINED _PATH SET "_PATH=%CD%"
)
CALL :Main
( Endlocal
Exit /B )
:Main
For /F "Tokens=*" %%_ in ('
DIR /B/A-D-S-H-L "%path%"
') DO (
IF /I "%%_" NEQ "%~nx0" (
SET "_TmpName=%%_"
IF NOT EXIST "%_Path%\!_TmpName:~0:1!\" MD "%_Path%\!_TmpName:~0:1!\"
MOVE /Y "%_Path%\!_TmpName!" "%_Path%\!_TmpName:~0:1!\!!_TmpName!"
)
)
GOTO :EOF
仅基于以字母字符开头的文件名,这里有一个更简单的方法:
@Echo Off
SetLocal EnableExtensions
PushD "%~1" 2> NUL
If /I "%~dp0" == "%CD%\" (Set "Exclusion=%~nx0") Else Set "Exclusion="
For %%G In (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) Do (
%SystemRoot%\System32\Robocopy.exe . %%G "%%G*" /Mov /XF "%Exclusion%" 1> NUL 2>&1
RD %%G 2> NUL
)
PopD