Exclamation point problem when renaming files with cmd - windows

I am using the following batch to randomly name files in a folder. Initially, i had a problem non English letters. I solved this problem with chcp 65001 . But i have problem with exclamation point. If a file name has exclamation point, the batch cannot change file name.
chcp 65001
SETLOCAL EnableExtensions EnableDelayedExpansion
SET PrependOnly=0
SET Undo=0
SET TranslationFile=Translation.txt
IF NOT {%Undo%}=={1} (
ECHO You are about to randomly rename every file in the following folder:
ECHO %~dp0
ECHO.
ECHO A file named %TranslationFile% will be created which allows you to undo this.
ECHO Warning: If %TranslationFile% is lost/deleted, this action cannot be undone.
ECHO Type "OK" to continue.
SET /P Confirm=
IF /I NOT {!Confirm!}=={OK} (
ECHO.
ECHO Aborting.
GOTO :EOF
)
ECHO Original Name/Random Name > %TranslationFile%
ECHO ------------------------- >> %TranslationFile%
FOR /F "tokens=*" %%A IN ('DIR /A:-D /B') DO (
IF NOT %%A==%~nx0 (
IF NOT %%A==%TranslationFile% (
SET Use=%%~xA
IF {%PrependOnly%}=={1} SET Use=_%%A
SET NewName=!RANDOM!-!RANDOM!-!RANDOM!!Use!.sat
ECHO %%A/!NewName!>> %TranslationFile%
RENAME "%%A" "!NewName!"
)
)
)
) ELSE (
ECHO Undo mode.
IF NOT EXIST %TranslationFile% (
ECHO Missing translation file: %TranslationFile%
PAUSE
GOTO :EOF
)
FOR /F "skip=2 tokens=1,2 delims=/" %%A IN (%TranslationFile%) DO RENAME "%%B" "%%A"
DEL /F /Q %TranslationFile%
)

I only show the way for undo=0.
chcp 65001
SETLOCAL EnableExtensions EnableDelayedExpansion
ECHO Undo mode.
IF NOT EXIST %TranslationFile% (
ECHO Missing translation file: %TranslationFile%
GOTO :EOF
)
FOR /F "skip=2 tokens=1,2 delims=/" %%A IN (%TranslationFile%) DO RENAME "%%B"
The problem is in the FOR loop, you read the content of a file.
But using %%A or %%B while delayed expansion is enabled will destroy exclamation marks.
The solution is to disable delayed expansion, at least temporarily.
setlocal DisabledDelayedExpansion
FOR /F "skip=2 tokens=1,2 delims=/" %%A IN (%TranslationFile%) DO RENAME "%%B"
endlocal

Related

Batch: for loop on files issue with symbol in name

Why does this for loop not work for files with "!" in the filename? And how can I make it recognize files with "!" or other possible symbols that may not work.
#Echo off
SETLOCAL EnableExtensions EnableDelayedExpansion
set my_dir=C:\Test
set my_ext=txt
cd /d !my_dir!
for %%F in ("*.!my_ext!") do (
for /F "tokens=1,* delims=|" %%K in ('
forfiles /M "%%~F" /C "cmd /C echo #path^|#ext"
') do (
echo "%%~K": %%L
set list=!list!%%~K;
)
)
I get a returned message like this, with the ! missing from the output.
ERROR: Files of type "C:\Test\My file name has an explanation point here.txt" not found.
Here's an example of something which may work for you:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "my_dir=C:\Test"
Set "my_ext=txt"
CD /D "%my_dir%" 2> NUL || GoTo :EOF
Set "list="
For %%G In ("*.%my_ext%") Do (Echo "%%~fG"^|%%~xG
If Not Defined list (Set "list=%%~fG") Else (
For /F "Tokens=1*Delims==" %%H In ('Set list'
) Do Set "list=%%I;%%~fG"))
SetLocal EnableDelayedExpansion
Echo(!list!
EndLocal
Pause
If you wanted each of your filepaths to be doublequoted, then a couple of small changes are all you need:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Set "my_dir=C:\Test"
Set "my_ext=txt"
CD /D "%my_dir%" 2> NUL || GoTo :EOF
Set "list="
For %%G In ("*.%my_ext%") Do (Echo "%%~fG"^|%%~xG
If Not Defined list (Set "list="%%~fG"") Else (
For /F "Tokens=1*Delims==" %%H In ('Set list'
) Do Set "list=%%I;"%%~fG""))
SetLocal EnableDelayedExpansion
Echo(!list!
EndLocal
Pause
It is important to note, especially because you're using full paths for each of them, that there is a limit to the size of a user defined environment variable of 32767 characters. This means that depending upon the number of matching files in %my_dir%, you could exceed that maximum. In both examples, you can obviously remove the Echo "%%~fG"^|%%~xG part, if you didn't really require it.

How to process a list of file/folder names in a FOR loop which can contain also an exclamation mark?

I write batch script to find contents for file in folder. Contents are in text file and have special characters like exclamation mark.
How do I get FILENAME and FOLDERNAME which contain exclamation mark.
#ECHO off
SETLOCAL EnableDelayedExpansion
set /p SRC="Enter source folder link: "
set /p DST="Enter destination folder link: "
FOR /F "delims=" %%a IN ('DIR /b /s /a-d "%SRC%"') do (
Set "CODE=%%~na"
Set "EXT=%%~xa"
findstr /c:"!CODE!" "%SRC%\Content.txt">nul
IF "!errorlevel!" EQU "0" (
for /F "tokens=2,3" %%c in ('findstr /c:"!CODE!" "%SRC%\Content.txt"') do (
ECHO !CODE!
Set "NEWNAME=%%c"
Set "FOLDERNAME=%%d"
Set "NEWNAME=!NEWNAME:_= !"
Set "FOLDERNAME=!FOLDERNAME:_= !"
IF not exist "%DST%\!FOLDERNAME!" md "%DST%\!FOLDERNAME!"
mklink "%DST%\!FOLDERNAME!\!NEWNAME!!EXT!" "%%a"
)
)
)
Endlocal
Exit
PS: Source folder has many files.
One solution is using a subroutine to avoid usage of delayed environment variable expansion:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
:GetSource
set "SRC="
set /P SRC="Enter source folder link: "
if not defined SRC goto GetSource
set "SRC=%SRC:"=%"
if not defined SRC goto GetSource
:GetDestination
set "DST="
set /P DST="Enter destination folder link: "
if not defined DST goto GetDestination
set "DST=%DST:"=%"
if not defined DST goto GetDestination
for /F "eol=| delims=" %%I in ('dir /A-D /B /S "%SRC%" 2^>nul') do (
if exist "%SRC%\Content.txt" for /F "tokens=2,3" %%A in ('%SystemRoot%\System32\findstr.exe /C:"%%~nI" "%SRC%\Content.txt" 2^>nul') do (
set "NEWNAME=%%~A"
set "FOLDERNAME=%%~B"
call :MakeLink "%%I"
)
)
endlocal
exit /B
:MakeLink
echo %~n1
set "NEWNAME=%NEWNAME:_= %"
set "FOLDERNAME=%FOLDERNAME:_= %"
if not exist "%DST%\%FOLDERNAME%" md "%DST%\%FOLDERNAME%"
mklink "%DST%\%FOLDERNAME%\%NEWNAME%%~x1" %1
goto :EOF
Open a command prompt window and run call /? for help explaining how to use the command CALL with enabled command extensions to run a block in same batch file like a subroutine. See also Where does GOTO :EOF return to?
I have not studied your code, but I'd assume that enabling the delayed expansion after setting the variable names would be more appropriate:
#Echo Off
SetLocal DisableDelayedExpansion
Set /P "SRC=Enter source folder link: "
Set /P "DST=Enter destination folder link: "
For /D /R %%A In (*) Do For /F "Tokens=2-3" %%B In (
'FindStr/C:"%%~nxA" "%SRC%\Content.txt" 2^>Nul') Do (Echo %%~nA
Set "NEW=%%B"
Set "FLD=%%C"
SetLocal EnableDelayedExpansion
If Not Exist "%DST%\!FLD:_= !\" MD "%DST%\!FLD:_= !" 2>Nul && (
MkLink "%DST%\!FLD:_= !\!NEW:_= !%%~xA" "%%A")
Endlocal)
Exit /B
I would strongly suggest you perform some proper verification of the user input prior to performing tasks using them, i.e. before the For loop.

rename files in batch script

I have folder with files named:
-backup- powerpoint1.ppt
-backup- powerpoint2.ppt
-backup- powerpoint3.ppt
I need to rename those files in the same folder to:
powerpoint1.ppt
powerpoint2.ppt
powerpoint3.ppt
I need a batch script to accomplish that.
I have searched online and came up with this so far but the values are resolving properly:
#echo off
setlocal EnableDelayedExpansion
set a=1
for /f "delims=" %%i in ('dir /b *.pptx') do (
set "dt=%%i"
echo.!dt!
set sh=!dt!
echo.!sh!
set "str=%sh:~12,4%"
echo "%%i" "%str%"
set /a a+=1
)
You code does not work as expected, because you missed applying delayed expansion for the interim variables sh and str. Also the extracted string portion !sh:~12,4! is not correct, it should read !sh:~9! in order to remove the -backup- prefix. This is the corrected code:
#echo off
setlocal EnableDelayedExpansion
set a=1
for /f "delims=" %%i in ('dir /b *.pptx') do (
set "dt=%%i"
echo/!dt!
set sh=!dt!
echo/!sh!
set "str=!sh:~9!"
ECHO ren "%%i" "!str!"
set /a a+=1
)
Actually I would not extract certain character positions, but I would remove the constant prefix -backup- by sub-string replacement. And you do not need the interim variables sh and str, neither do you need the counter a. So I would do it like this:
#echo off
setlocal EnableDelayedExpansion
for /f "delims=" %%i in ('dir /b *.pptx') do (
set "dt=%%i"
set "dt=!dt:*-backup- =!"
ECHO ren "%%i" "!dt!"
)
endlocal
After having verified the correct output, remove the upper-case ECHO command from the code to actually rename files.
If the unwanted parts in your file names are space separated a simple:
#Echo off
For /F "delims=" %%A in ('Dir /B/A-D "-backup- powerpoint*"'
) Do For /f "tokens=2" %%B in ("%%A") Do Echo Ren "%%A" "%%B"
Should do (independent of the extension).
Ren "-backup- powerpoint1.ppt" "powerpoint1.ppt"
Ren "-backup- powerpoint2.ppt" "powerpoint2.ppt"
Ren "-backup- powerpoint3.ppt" "powerpoint3.ppt"

Move files into folders based on their names

I have some files in the form:
filename1 1.ext
filename1 2.ext
filename1 3.ext
...
filename2 1.ext
filename2 100.ext
...
filename20 1.ext
filename20 15.ext
(etc.)
...where filename can contain spaces.
And I want to move them to folder filename1, filename2, etc., respectively.
I know I can do a for loop for %%i in (*.ext) do and remove the extension with set folder=%%~ni. So what I am missing is how to remove everything after the space just before the number, and get only filename1, for example.
I also know I can split variable folder, but in this case I do not know by at which character I need to split, although I know it will be a space followed by a number.
So basically, I want something like this:
#echo off
set folder=
for %%i in (*.ext) do set folder=%%~ni & set folder=getfoldernamefromvariablefoldersomehow & mv %%i %folder%
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
FOR /f "delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*.ext" '
) DO (
CALL :sub1 "%%a" %%a
)
GOTO :EOF
:sub1
SET "filename=%~1"
:subloop
SHIFT
SET "numname=%~1"
IF NOT "%~2"=="" GOTO subloop
CALL SET "dirname=%%filename: %numname%=%%
ECHO( MD "%sourcedir%\%dirname%" 2>nul
ECHO( MOVE "%sourcedir%\%filename%" "%sourcedir%\%dirname%\%numname%"
GOTO :eof
You would need to change the setting of sourcedir to suit your circumstances.
The required MD commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MD to MD to actually create the directories.
The required MOVE commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MOVE to MOVE to actually move the files. Append >nul to suppress report messages (eg. 1 file moved)
Perform a directory list of the required files in basic form without directorynames. Send the full fulename in quotes and without to the subroutine sub1.
In the subroutine, save the source filename in filename then shift each parameter supplied until there is no second parameter; the value in numname must then be the last or required filename.
Remove numname with a leading space from filename to get the required subdirectoryname, make that subdirectory and move the file.
[edit in the light of comment]
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
FOR /f "delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*.ext" '
) DO (
CALL :sub1 "%%a" %%a
)
GOTO :EOF
:sub1
SET "filename=%~1"
SET "destdirname=%~2"
:subloop
SHIFT
SET "numname=%~1"
IF NOT "%~2"=="" GOTO subloop
CALL SET "dirname=%%filename: %numname%=%%
ECHO( MD "%sourcedir%\%destdirname%" 2>nul
ECHO( MOVE "%sourcedir%\%filename%" "%sourcedir%\%destdirname%\%numname%"
GOTO :eof
It's difficult to scry your intentions when you give no example.
destdirname is set to the second parameter on entering sub1 which will be the first group of characters before the first space.
the md does not need to be gated since the 2>nul will suppress the directory exists error message.
Here is a script that does what you want. It splits off the last SPACE followed by numerals from the file name and uses the remaining string as the name of the destination directory of the movement.
This approach handles all valid characters for file names properly, even ^, &, %, !, ( and ). It can even handle file names that contain SPACE plus numerals plus .ext again correctly.
So here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=."
set "_TARGET=."
for /F "eol=| delims=" %%F in ('
dir /B "%_SOURCE%\*.ext" ^| findstr /R /I /C:" [0123456789][0123456789]*\.ext$"
') do (
set "FILE=%%F"
call :SPLIT LAST REST "%%F"
setlocal EnableDelayedExpansion
2> nul mkdir "!_TARGET!\!REST!"
ECHO move /Y "!_SOURCE!\!FILE!" "!_TARGET!\!REST!"
endlocal
)
endlocal
exit /B
:SPLIT rtn_last rtn_rest val_string
setlocal DisableDelayedExpansion
set "RES=" & set "STR=%~3"
:LOOP
for /F "tokens=1,* delims= " %%I in ("%STR%") do (
if "%%J"=="" (
set "RES=%%I"
) else (
set "STR=%%J"
goto :LOOP
)
)
set "STR=%~3|"
call set "STR=%%STR: %RES%|=%%"
(
endlocal
set "%~1=%RES%"
set "%~2=%STR:^^=^%"
)
exit /B
After having tested the script, remove the upper-case ECHO command to actually move any files. Unless you remove the /Y option from the move command, files become overwritten without prompt. To suppress summary messages (like 1 file(s) moved.), add > nul to the move command line. Note that any prompt was also hidden then in case you removed the /Y option.
Thank to all of you for your comments. Finally, i was able to get a solution:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "_source=C:\Users\kurok_000\Downloads"
set "_target=C:\Users\kurok_000\YandexDisk\Mangas"
for /f "eol=| delims=" %%f in ('dir /b "%_source%\*.7z"') do (
call :fixNames "%%f" %_source%
)
for /f "eol=| delims=" %%f in ('dir /b "%_source%\*.7z" ^| findstr /r /i /c:" [0123456789][0123456789]*\.7z$"') do (
set "file=%%f"
call :split last rest "%%f"
setlocal EnableDelayedExpansion
2> nul mkdir "!_target!\!rest!"
move /y "!_source!\!file!" "!_target!\!rest!" >nul
echo moved %%f to !_target!\!rest!
endlocal
)
endlocal
exit /b
:split rtn_last rtn_rest val_string
setlocal DisableDelayedExpansion
set "res=" & set "str=%~3"
:loop
for /f "tokens=1,* delims= " %%i in ("%str%") do (
if "%%j"=="" (
set "res=%%i"
) else (
set "str=%%j"
goto :loop
)
)
:quit
set "str=%~3|"
call set "str=%%str: %res%|=%%"
(
endlocal
set "%~1=%res%"
set "%~2=%str:^^=^%"
)
exit /b
:fixNames _file _folder
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "FILE=%1"
set "FILE=%file:~1,-1%"
set "folder=%2"
for /F "tokens=1,* delims=0123456789" %%A in ("%FILE%") do (
set filename=!FILE:%%B=!%%~xB
)
if not "%filename%"=="%FILE%" (rename "!folder!\!FILE!" "!filename!")

How can I edit this so it works with files that have spaces?

#echo off
set /A Counter=0
setlocal enabledelayedexpansion
for %%D in ("e:\test test\") do (
for /f %%F in ('dir /a-d /b %%D*.*') do (
ECHO.
ECHO Current file is: %%F
set src=%%F
set dest="e:\test test\space locate\%%F"
if not exist !dest! move !src! !dest!
if exist !dest! (
ECHO.
ECHO ERROR: "%%F" already exists
set /A Counter+=1
)
ECHO source file is !src!
ECHO destination is !dest!
)
)
echo.
echo %Counter% files not moved.
You probably just need to put quotes (") around all your filenames.
I'm talking about this sort of thing:
if not exist "!dest!" move "!src!" "!dest!"
That's just a suggestion, I don't have time to actually try to debug it right now.
Edit in response to comment:
for by default uses spaces as delimiters. You should say for /f "delims=" instead of just for /f in order to tell it not to do that.

Resources