Let's say I have a couple of images and I need to rename them and on every iteration add an incremented number.
For this situation I have three images no matter how they name is and I want to rename them like this.
1239.jpg => file1.jpg
file.jpg => file2.jpg
image.jpg => file3.jpg
My commands executed in a command prompt window for this task are:
setlocal EnableDelayedExpansion
set filename=file
set counter=1
for /f "usebackq delims=*" %i in ('dir /b *.jpg') do (set /a counter+=1 ren "%i" "%filename%!counter!.jpg")
But this results in the error message Missing operator.
Can anyone help me with this?
The commands SETLOCAL and ENDLOCAL can be used only in a batch file. Please read this answer for details about the commands SETLOCAL and ENDLOCAL. These two commands do nothing on being executed in a command prompt window. It is necessary to start cmd.exe with option /V:ON to use delayed expansion in a command prompt window as explained by the help output on running cmd /? in a command prompt window.
The usage of usebackq requires enclosing the command line to be executed in ` instead of ' as usual. usebackq is mainly used for processing the lines of a text file of which name is specified in the round brackets enclosed in ".
The following command line with the two commands SET and REN is not of valid syntax. The command SET interprets everything after /a as arithmetic expression to evaluate. In this case the expression misses an operator between 1 and ren whereby ren would be interpreted here as name of an environment variable and not as command to execute next after set.
(set /a counter+=1 ren "%i" "%filename%!counter!.jpg")
The valid command line would be:
set /A "counter+=1" & ren "%i" "%filename%!counter!.jpg"
Enclosing the arithmetic expression in double quotes makes it clear for command SET where the arithmetic expression starts and where it ends. The conditional execution operator & is interpreted by Windows command processor before executing the command SET and results in execution of command REN after command SET even on SET would fail to evaluate the arithmetic expression.
A file renaming task done with Windows command processor is no easy to achieve if
the file extension of the files should not change and
files with any name including those with one or more &()[]{}^=;!'+,`~ should be supported and
there can be already files in the directory with one of the new file names.
For testing the batch file below I created first in a directory following files:
file.jpg
file1.jpg
file2.jpg
file3.jpg
file 4.jpg
File8.jpg
hello!.jpg
image.jpg
The directory was on a FAT32 drive. The file systems FAT16, FAT32 and exFAT return a list of matching directory entries not sorted by name as NTFS which means the list output by command DIR in the main FOR loop in code below is in an unsorted and therefore unpredictable order.
It would be of course possible to append the DIR option /ON to get the list of file names ordered by DIR according to name, but in fact that is not real help in this case, especially because of DIR makes a strict alphabetical sort and not an alphanumeric sort.
A strict alphabetic sort returns a list of ten file names as file1.jpg, file10.jpg, file2.jpg, file3.jpg, ..., file9.jpg while an alphanumeric sort returns a list of ten file names as file1.jpg, file2.jpg, file3.jpg, ..., file9.jpg, file10.jpg.
So here is the commented batch file for this file rename task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "FileName=file"
rem The user can run this batch file with a folder path in which all *.jpg
rem files should be renamed with an incremented number. Otherwise the
rem directory of the batch file is search for *.jpg files to rename.
if not "%~1" == "" (
pushd "%~1" || exit /B
) else (
pushd "%~dp0" || exit /B
)
set "FileCount=0"
set "DelayedLoopCount=0"
set "DelayedRenameCount=0"
rem Remove all existing environment variables in local environment of which
rem name starts with DelayedRename_ whereby the underscore is very important
rem because there is also the environment variable DelayedRenameCount.
for /F "delims==" %%I in ('set DelayedRename_ 2^>nul') do set "%%I="
rem Get a captured list of all *.jpg files in current directory and then
rem rename one file after the other if that is possible on no other file
rem has by chance already the new file name for the current file.
for /F "eol=| delims=" %%I in ('dir *.jpg /A-D /B 2^>nul') do call :RenameFile "%%I"
goto DelayedRenameLoop
:RenameFile
set /A FileCount+=1
set "NewName=%FileName%%FileCount%%~x1"
rem Has the file case-sensitive already the right name?
if %1 == "%NewName%" goto :EOF
rem Is the new file name the same as the current name
rem with exception of the case of one or more letters?
if /I %1 == "%NewName%" (
echo Rename %1 to "%NewName%"
ren %1 "%NewName%"
goto :EOF
)
rem Is there no other file which has already the new name?
if not exist "%NewName%" (
echo Rename %1 to "%NewName%"
ren %1 "%NewName%"
goto :EOF
)
rem Another file or folder has already the new name. Remember the name
rem of this file and the new file name with an environment variable for
rem a delayed rename after all other files have been renamed as far as
rem possible.
set /A DelayedRenameCount+=1
set "DelayedRename_%DelayedRenameCount%=|%~1|%NewName%"
goto :EOF
rem It could happen that "file15.jpg" should be renamed to "file3.jpg"
rem while "file3.jpg" exists already which should be renamed to "file12.jpg"
rem while "file12.jpg" exists already which should be renamed to "file20.jpg".
rem This extra loop is used for such worst case scenarios which is executed
rem in a loop until all files have been renamed with a maximum of 50 loop
rem runs in case of one file cannot be renamed and therefore blocking
rem renaming of another file. An endless running loop should be avoided.
rem A file cannot be renamed if a folder has by chance the new file name.
rem A file cannot be renamed if an application has opened the file with
rem a sharing access mode preventing the rename of the file as long as
rem being opened by this application.
:DelayedRenameLoop
if %DelayedRenameCount% == 0 goto EndBatch
for /F "tokens=1-3 delims=|" %%I in ('set DelayedRename_ 2^>nul') do if not exist "%%K" (
echo Rename "%%J" to "%%K"
ren "%%J" "%%K"
set "%%I"
set /A DelayedRenameCount-=1
)
set /A DelayedLoopCount+=1
if not %DelayedLoopCount% == 50 goto DelayedRenameLoop
:EndBatch
popd
endlocal
This batch file output on execution:
Rename "file3.jpg" to "file4.jpg"
Rename "file 4.jpg" to "file5.jpg"
Rename "File8.jpg" to "file6.jpg"
Rename "hello!.jpg" to "file7.jpg"
Rename "image.jpg" to "file8.jpg"
Rename "file2.jpg" to "file3.jpg"
Rename "file1.jpg" to "file2.jpg"
Rename "file.jpg" to "file1.jpg"
The files in the directory were finally:
file1.jpg
file2.jpg
file3.jpg
file4.jpg
file5.jpg
file6.jpg
file7.jpg
File8.jpg
What about the last file?
It has the file name File8.jpg instead of file8.jpg although executed was ren "image.jpg" "file8.jpg". Well, FAT32 is a bit problematic regarding to updates of the file allocation table on a table entry changes only in case of one or more letters.
The solution is using this batch file with two extra FOR loops with # as loop variable and optimized by removing the comments.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "FileName=file"
if not "%~1" == "" (pushd "%~1" || exit /B) else (pushd "%~dp0" || exit /B)
set "FileCount=0"
set "DelayedLoopCount=0"
set "DelayedRenameCount=0"
for /F "delims==" %%I in ('set DelayedRename_ 2^>nul') do set "%%I="
for /F "eol=| delims=" %%I in ('dir *.jpg /A-D /B 2^>nul') do call :RenameFile "%%I"
goto DelayedRenameLoop
:RenameFile
set /A FileCount+=1
set "NewName=%FileName%%FileCount%%~x1"
if %1 == "%NewName%" goto :EOF
if /I %1 == "%NewName%" (
echo Rename %1 to "%NewName%"
ren %1 "%NewName%"
goto :EOF
)
if not exist "%NewName%" (
echo Rename %1 to "%NewName%"
ren %1 "%NewName%"
for %%# in ("%NewName%") do if not "%%~nx#" == "%NewName%" ren "%%~nx#" "%NewName%"
goto :EOF
)
set /A DelayedRenameCount+=1
set "DelayedRename_%DelayedRenameCount%=|%~1|%NewName%"
goto :EOF
:DelayedRenameLoop
if %DelayedRenameCount% == 0 goto EndBatch
for /F "tokens=1-3 delims=|" %%I in ('set DelayedRename_ 2^>nul') do if not exist "%%K" (
echo Rename "%%J" to "%%K"
ren "%%J" "%%K"
for %%# in ("%%K") do if not "%%~nx#" == "%%K" ren "%%~nx#" "%%K"
set "%%I"
set /A DelayedRenameCount-=1
)
set /A DelayedLoopCount+=1
if not %DelayedLoopCount% == 50 goto DelayedRenameLoop
:EndBatch
popd
endlocal
The result of this enhanced batch file is even on FAT32:
file1.jpg
file2.jpg
file3.jpg
file4.jpg
file5.jpg
file6.jpg
file7.jpg
file8.jpg
The reason for using | as string separator on execution of
set "DelayedRename_%DelayedRenameCount%=|%~1|%NewName%"
resulting, for example, in execution of
set "DelayedRename_1=|file.jpg|file1.jpg"
set "DelayedRename_2=|file1.jpg|file2.jpg"
set "DelayedRename_3=|file2.jpg|file3.jpg"
is that the vertical bar is not allowed in a file folder name. So it is a very good character to separate the name of the environment variable with the equal sign appended from current file name and from new file name. This makes it possible to use later delims=| for renaming the file and deleting the environment variable.
See also the Microsoft documentations:
Naming Files, Paths, and Namespaces
Using command redirection operators
The equal sign is allowed in a file name. It is even possible that a *.jpg file has as file name =My Favorite Picute=.jpg which is another reason for using | to get executed for example
set "DelayedRename_4=|=My Favorite Picute=.jpg|file9.jpg"
which later results in assigned DelayedRename_4= to loop variable I, =My Favorite Picute=.jpg to loop variable J and file9.jpg to loop variable K in the FOR loop doing the delayed file renames.
Note: Each FOR loop with '...' in the round brackets results
in starting in background one more command process with %ComSpec% /c '...' and
capturing the output written to handle STDOUT like the output of the cmd.exe internal commands DIR and SET
while cmd.exe processing the batch file waits until started cmd.exe terminated (closed) itself after execution of the command line
and then processing the captured lines one after the other by FOR with ignoring empty lines and lines starting with the defined end of line character after doing the string delimiting which is the reason why eol=| is used on main FOR loop as a file name can start with default end of line character ; and which of course should not be ignored here.
The redirection operator > must be escaped with caret character ^ on those FOR command lines to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir or set command line in the separate command process started in background.
The batch file does not use delayed expansion as this would cause troubles on a file name having one or more exclamation marks which would be interpreted as beginning/end of a delayed expanded environment variable reference on command lines like ren "%%J" "%%K". Therefore a subroutine is used for the main file rename loop on which it is necessary to access the two incremented counter values.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
dir /?
echo /?
endlocal /?
exit /?
goto /?
if /?
popd /?
pushd /?
rem /?
ren /?
set /?
setlocal /?
I suggest further to look on:
Microsoft documentation for the Windows Commands
SS64.com - A-Z index of Windows CMD commands
Where does GOTO :EOF return to?
Single line with multiple commands using Windows batch file
Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Why is no string output with 'echo %var%' after using 'set var = text' on command line?
Related
I have some files
afjj.txt
agk.png
beta.tt
Ritj.rar
I use this script to move files in alphabetically order into autogenerated folders
a
|
|----> afjj.txt
|----> agk.png
b
|----> beta.tt
R
|----> Ritj.rar
To do I use this code
#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!"
)
)
What is problem? If I double-click on this batch, batch doesn't work, not move.
But this batch works from command line.
Why?
NOTE: I use Windows Server 2016
P.S: from command line I use this command and works but not if I double click directly on .bat
move.bat "Z:\# 2020\Alfa\test"
The first mistake is naming the batch file move.bat. It is never a good idea to give a batch file the name of a Windows command because hat cause usually troubles. See also: SS64.com - A-Z index of Windows CMD commands.
The second mistake is using setlocal enabledelayedexpansion inside the loop without a corresponding endlocal also within same loop executed as often as setlocal. Please read this answer for details about the commands SETLOCAL and ENDLOCAL. The command SETLOCAL pushes several data on stack on every iteration of the loop and the data are never popped from stack in same loop on each loop iteration. The result is sooner or later a stack overflow depending on the number of files to process as more and more data are pushed on stack.
The third mistake is the expectation that the current directory is always the directory of the batch file. This expectation is quite often not fulfilled.
The fourth mistake is using a loop to iterate over a list of files which permanently changes on each execution of the commands in body of FOR loop. The loop in code in question works usually on storage media with NTFS as file system, but does not work on storage media using FAT32 or exFAT as file system.
The fifth mistake is the expectation that %0 expands always to name of the currently executed batch file with file extension, but without file path which is not true if the batch file is executed with full qualified file name (drive + path + name + extension), or with just file name without file extension, or using a relative path.
The sixth mistake is not enclosing the folder name on creation of the subfolder in double quotes which is problematic on file name starting unusually with an ampersand.
The seventh mistake is not taking into account to handle correct file names starting with a dot like .htaccess in which case the second character must be used as name for the subfolder, except the second character is also a dot. It is very uncommon, but also possible that file name starts with one or more spaces. In this case also the first none space character of file name should be used as Windows by default prevents the creation of a folder of which name is just a space character.
The solution is using following commented batch file with name MoveToFolders.cmd or 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
The batch file can be started also with a folder path as argument to process the files in this folder without checking if the passed argument string is really referencing an existing folder.
Please read very carefully the answers on How to replace a string with a substring when there are parentheses in the string if there is interest on how to verify if a passed argument string really references an existing folder.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
dir /?
echo /?
endlocal /?
for /?
if /?
md /?
move /?
rem /?
set /?
setlocal /?
Here is a slightly amended script. Notice I removed the !first! variable as it is not required. I also built in a safety measure, if it is unable to pushd to the given directory you passed to %~1 it will not continue the move. Else it might move files in a path you do not want it to move files. i.e the working dir you started the script in.
#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
Note, with the above you can also drag a directory to the batch file, which will process that directory.
Or if you plan on double clicking, without parameters from 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
A slightly different take.
This script will work on the current directory if double clicked, or run at the command line without a path specified.
But it will also allow you to provide a path to it as well.
#( 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
Based upon only filenames beginning with alphabetic characters, here's a somewhat simpler methodology:
#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
I have a folder with four to five text files in it.
My overall aim is the following: Create one big file which has the content of the separate files, but in the right order.
I can use the time-stamp of each file to start with the oldest file up to the youngest.
My process right now looks like this:
Order the files in this folder by date.
Create a temporary file and write the content from the separate files into this file.
Output the temporary file.
In code I do something like this:
set temp_concat=%temp_dir%\temp_concat.log
echo %temp_concat%
echo aiu_logs > %temp_concat%
for /f "delims=" %%? in ('dir /b /o:d %Folder%*') do (
for /f "delims=" %%K in (%Folder%%%?) do (
echo %%K >>%temp_concat%
)
)
The above code seems to work as my temp_concat is very large.
However, this takes much much longer than expected. I have to wait about 40 seconds just to merge three files in my case.
Is there some better way of merging some amount of files, but keep them in the correct order by date?
This batch file uses the suggestion posted by Sqashman to use a FOR loop to create the arguments string for command COPY used to concatenate the file contents into a single file in the order of oldest modified file first and newest modified file last.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "Folder=%~dp0"
if not "%~1" == "" set "Folder=%~1"
set "Folder=%Folder:/=\%"
if not "%Folder:~-1%" == "\" set "Folder=%Folder%\"
set "ResultsFile=%Folder%Results.log"
del "%ResultsFile%" 2>nul
set "Arguments="
for /F "eol=| delims=" %%I in ('dir /A-D-H /B /O:D "%Folder%*" 2^>nul') do if not "%%~fI" == "%~f0" set "Arguments=!Arguments! + "%%I""
if defined Arguments (
echo aiu_logs>"%ResultsFile%"
copy /B "%ResultsFile%"%Arguments% "%ResultsFile%" >nul
)
endlocal
The batch file as is does not work if either the folder path or one of the file names contains one or more exclamation marks ! because of an enabled delayed environment variable expansion.
Further the command line length is limited and so this batch file does not work on too many files must be concatenated depending on length of the file path of each file and the length of the file names.
A better solution would be using following batch file:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "Folder=%~dp0"
if not "%~1" == "" set "Folder=%~1"
set "Folder=%Folder:/=\%"
pushd "%Folder%" 2>nul
if errorlevel 1 goto EndBatch
set "ResultsFile=Results.log"
del "%ResultsFile%" 2>nul
set "Arguments="
for /F "eol=| delims=" %%I in ('dir /A-D-H /B /O:D * 2^>nul') do if not "%%~fI" == "%~f0" call set "Arguments=%%Arguments%% + "%%I""
if defined Arguments (
echo aiu_logs>"%ResultsFile%"
copy /B "%ResultsFile%"%Arguments% "%ResultsFile%" >nul
)
popd
:EndBatch
endlocal
A folder path with one or more exclamation marks is no problem anymore. Also the file names can contain ! because of delayed expansion is not used by this batch file which is a bit slower than the first batch file.
The folder with the files to concatenate is made the current directory by this batch file. For that reason more file names can be specified as arguments on COPY command line in comparison to first batch file because of the file names are specified without path. But the number of file contents which can be merged with this batch file is nevertheless limited by the maximum length of a Windows command line respectively the maximum length of an environment variable value.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
copy /?
dir /?
echo /?
endlocal /?
for /?
goto /?
if /?
popd /?
pushd /?
set /?
setlocal /?
Read also the Microsoft article about Using command redirection operators for an explanation of > and 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background with %ComSpec% /c and the DIR command line between the two ' appended as further arguments.
The second FOR /F does not contain a command. It contains a filename. I have not tested this, but perhaps:
set temp_concat=%temp_dir%\temp_concat.log
echo %temp_concat%
echo aiu_logs > "%temp_concat%"
for /f "delims=" %%? in ('dir /b /o:d "%Folder%"') do (
if not "%%~f?" == "%~f0" (
type %%? >>"%temp_concat%"
)
)
This will concatenate all files in the "%Folder%" directory. Paths should be quoted in case there are special characters in them.
The batch file code used by me:
for /r %%f in (*) do (
echo %%~nf >>testy.txt
)
for /f "tokens=*" %%a in (testy.txt) do (
set _temp=%%a
for /f "tokens=30*" %%g in (_temp) do (
echo large=%%g>>length.txt
)
)
It should create length.txt having characters at 30th place and above, but it is failing in the 3rd for loop which should create length.txt.
This can be done with following batch file on no file name of a non-hidden file contains an exclamation mark.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
(for %%I in (*) do (
set "FileName=%%~nI"
if not defined FileName set "FileName=%%~xI"
if not "!FileName:~30!" == "" echo %%~nxI
))>length.txt
rem Delete length.txt if being an empty file.
for %%I in (length.txt) do if %%~zI == 0 del length.txt
endlocal
The next batch file is slower, but processes correct also non-hidden files with a ! in file name.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
(for %%I in (*) do (
set "FileName=%%~nI"
if not defined FileName set "FileName=%%~xI"
setlocal EnableDelayedExpansion
if not "!FileName:~30!" == "" echo %%~nxI
endlocal
))>length.txt
rem Delete length.txt if being an empty file.
for %%I in (length.txt) do if %%~zI == 0 del length.txt
endlocal
Both batch files handle the name of a file like .htaccess as file with file name being .htaccess and having no file extension while Windows command processor handles such files as file name being empty and file extension is .htaccess.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
del /?
echo /?
endlocal /?
for /?
if /?
rem /?
set /?
setlocal /?
Some things are much easier done in PowerShell
To be on topic wrapped in a batch file
:: Q:\Test\2018\11\19\SO_53379317.cmd
Echo off
Set Len=30
For /f "delims=" %%A in ('
powershell -NoP -C "(Get-ChildItem '%CD%' -File| Where-Object {$_.Name.Length -gt %Len%}).Name.SubString(%Len%)"
') Do Echo %%A
In case you want the length of drive, path, name and extension replace .Name with .FullName
The file name without extansion would be .BaseName
THe -File parameter requires PSv3+ (present in Windows 8+)
As long as your directory has standard filenames with extensions, you could probably use something far more simple:
#Where .:"??????????????????????????????*.*">"length.txt"
This should output all files in the current directory containing 30 or more characters in their filenames, (ignoring the extension).
I have files named as RabcdYYMMKKACCOUNT.TXT in the Subfolders of a folder where YYMM is year, month this will change. KK is another identifier, I want all the files to be renamed to MSFKKDNB.ABC, the KK is the identifier in the input file.
Below is the one i tried and the result of it:
FOR /R %%f IN (*account.txt) DO REN "%%f" *dnb.abc
R00531706AUAccount.txt is renamed to R00531706AUAccount.txtdnb.abc
but the output should be MSFAUDNB.abc
This could be done for example with:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /R %%I in (???????????account.txt) do (
set "FileName=%%~nI"
set "NewFileName=MSF!FileName:~9,2!DNB.abc"
if not exist "%%~dpI!NewFileName!" (
ren "%%~fI" "!NewFileName!" 2>nul
if not exist "%%~dpI!NewFileName!" echo Failed to rename file: "%%~fI"
) else (
echo Cannot rename file: "%%~fI"
)
)
endlocal
The file name of found account text file is assigned to environment variable FileName.
The new name for the file is created by concatenating the fixed parts MSF and DNB.abc with the 2 characters to keep from file name using string substitution and delayed expansion.
Next it is checked if a file with new name does not already exist. Is this the case the file renaming is done otherwise an error message is output.
After renaming the file it is checked if that was successful. A slightly different error is output if renaming failed for example because of a sharing violation.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
for /?
if /?
ren /?
set /?
setlocal /?
Read also the Microsoft article about Using Command Redirection Operators.
Try this:
#Echo Off
For %%A In ("*account.txt") Do (Set "_=%%~nA"
SetLocal EnableDelayedExpansion
Ren "%%A" "MSF!_:~-9,2!DNB.abc"
EndLocal)
I would probably do it the following way, provided that the files to rename are located in immediate sub-directories (YYMM) of the given root directory and nowhere else:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "_ROOT=." & rem // (specify path to the root directory)
for /D %%D in ("%_ROOT%\????") do (
for %%F in ("%_ROOT%\%%~nxD\R??????????Account.txt") do (
set "FDIR=%%~nxD" & set "FILE=%%~nxF"
setlocal EnableDelayedExpansion
ECHO ren "!_ROOT!\!FDIR!\!FILE!" "MSF!FILE:~9,2!DNB.abc"
endlocal
)
)
endlocal
exit /B
If you want to check whether both the sub-directory name and the year/month portion of the file names are purely numeric, you could use the following script:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "_ROOT=." & rem // (specify path to the root directory)
for /F "delims= eol=|" %%D in ('
dir /B /A:D "%_ROOT%\????" ^| ^
findstr "^[0123456789][0123456789][0123456789][0123456789]$"
') do (
for /F "delims= eol=|" %%F in ('
dir /B /A:-D "%_ROOT%\%%~nxD\R??????????Account.txt" ^| ^
findstr "^R....[0123456789][0123456789][0123456789][0123456789].."
') do (
set "FDIR=%%~nxD" & set "FILE=%%~nxF"
setlocal EnableDelayedExpansion
ECHO ren "!_ROOT!\!FDIR!\!FILE!" "MSF!FILE:~9,2!DNB.abc"
endlocal
)
)
endlocal
exit /B
If you want to check whether the sub-directory name matches the year/month (YYMM) portion of the file names, replace the pattern R??????????Account.txt by R????%%~nxD??Account.txt (for both scripts).
After having verified the correct output of either script, remove the upper-case ECHO commands to actually rename any files!
Basically, both scripts use sub-string expansion to extract the identifier part (KK) from the file names. Since there are variables set and read in the same block of code, delayed expansion is required for that. The second approach does not list the sub-directories and files by standard for loops, it uses the dir command, findstr to filter their names and a for /F loop to capture the resulting output for both sub-directories and files.
our system is going to be migrated from Linux to Windows machine so I'm preparing a batch file equivalent to our existing script. I already have created the batch file but I need to unwrap first the file before processing its next line of codes.
Example. Here is a one-liner wherein the delimiter is "{".
Note: Delimiter can be any or variable character except element delimiter ("~" in this case).
ISA~00~ ~00~ ~ZZ~SAMSUNGSND ~14~181087842 ~130214~2300~U~00401~000000003~0~T~>{GS~FA~181087842TEST~SYNNTEST~20130214~2300~810~X~004010{ST~997~131250001{AK1~SC~1809{AK9~A~1~1~1{SE~4~131250001{GE~1~810{IEA~1~000000001
I need it to be unwrapped like this (equivalent to tr "{" "\n" < FileName.txt ):
ISA~00~ ~00~ ~ZZ~SAMSUNGSND ~14~181087842 ~130214~2300~U~00401~000000003~0~T~>
GS~FA~181087842TEST~SYNNTEST~20130214~2300~810~X~004010
ST~997~131250001
AK1~SC~1809
AK9~A~1~1~1
SE~4~131250001
GE~1~810
IEA~1~000000001
EDIT:
Once unwrapped, I need to search fixed values of third field if equal to "1145837" under GS segment (2nd line) and replace it with "1119283" (which is equivalent to sed '/^GS/ s/1145837/1119283/').
Below is my batch file. I need the code to be inserted somewhere inside :WriteToLogFile subroutine
#echo on
::This ensures the parameters are resolved prior to the internal variable
SetLocal EnableDelayedExpansion
rem Get current date and time as local time.
for /f "delims=" %%a in ('wmic OS Get localdatetime ^| %SystemRoot%\System32\Find.exe "."') do set dt=%%a
rem Reformat the date and time strong to wanted format.
set "YYYY=%dt:~0,4%"
set "MM=%dt:~4,2%"
set "DD=%dt:~6,2%"
set "HH=%dt:~8,2%"
set "Min=%dt:~10,2%"
set "Sec=%dt:~12,2%"
set "TimeStamp=%YYYY%-%MM%-%DD%_%HH%-%Min%-%Sec%"
rem Define name of the list file containing current date and time in name.
set "ListFile=FLIST_%TimeStamp%.lst"
rem Change directory (and drive).
cd /D "C:\VP"
rem Create the list file which is good here as the list of files changes
rem while running this batch file and therefore it is better to work with
rem a list file instead of running a FOR directly for each file in the
rem directory. The list file is not included in this list as it either does
rem not exist at all or it has wrong file extension as only *.txt files are
rem listed by command DIR. The log file EDI.log has also wrong file extension.
dir *.txt /A:-D /B /O:D >"C:\VP\TEST\%ListFile%"
rem It might be useful to delete the log file from a previous run.
if exist EDI.log del EDI.log
rem Process each file in the list file.
cd /D "C:\VP\TEST"
for /F "delims=" %%F in ( %ListFile% ) do call :ProcessFile "%%F"
cd /D "C:\VP"
::rem Delete the list file as not needed anymore. It could be also kept.
::del %ListFile%
rem Exit batch file.
endlocal
goto :EOF
:ProcessFile
rem The parameter passed from first FOR is the file name in double quotes.
set "FileName=%~1"
rem Ignore the files CNtable.txt and Dupfile.txt in same directory.
rem Command goto :EOF just exits here from subroutine ProcessFile.
if "%FileName%"=="CNtable.txt" goto :EOF
if "%FileName%"=="Dupfile.txt" goto :EOF
if "%FileName%"=="VanPointAS2in.bat" goto :EOF
if "%FileName%"=="VP.bat" goto :EOF
rem Get 7th, 9th and 14th element from first line of current file.
cd /D "C:\VP"
for /f "usebackq tokens=7,9,14 delims=~*^" %%a in ( "%FileName%" ) do (
set "ISAsender=%%a"
set "ISAreceiver=%%b"
set "ISActrlnum=%%c"
goto WriteToLogFile
)
:WriteToLogFile
rem Remove all spaces as ISAsender and ISAreceiver have
rem usually spaces appended at end according to example
rem text. Then write file name and the 3 values to log file.
set "ISAsender=%ISAsender: =%"
set "ISAreceiver=%ISAreceiver: =%"
set "ISActrlnum=%ISActrlnum: =%"
echo %FileName%,%ISAsender%,%ISAreceiver%,%ISActrlnum%>>"C:\VP\TEST\EDI.log"
set "FLAG=N"
if "%ISAsender%"=="APPLESND" (
if "%ISAreceiver%"=="MANGO" (
set "FLAG=Y"
set "VW=AP"
call :DupCheck
echo %errorlevel%>>"C:\VP\TEST\EDI.log"
if errorlevel 1 move /Y "%FileName%" "APPLE"
echo Moved %FileName% to directory APPLE.
)
)
if "%ISAsender%"=="APPLESND" (
if "%ISAreceiver%"=="MANGOES" (
set "FLAG=Y"
set "VW=AP"
call :DupCheck
echo %errorlevel%>>"C:\VP\TEST\EDI.log"
if errorlevel 1 move /Y "%FileName%" "APPLE"
echo Moved %FileName% to directory APPLE.
)
)
if "%ISAsender%"=="SAMSUNGSND" (
if "%ISAreceiver%"=="MANGO" (
set "FLAG=Y"
set "VW=SS"
call :DupCheck
echo %errorlevel%>>"C:\VP\TEST\EDI.log"
if errorlevel 1 move /Y "%FileName%" "SAMSUNG"
echo Moved %FileName% to directory SAMSUNG.
)
)
rem Move to directory BYPASS if all else not satisfied.
if "%FLAG%"=="N" (
move /Y "%FileName%" "BYPASS"
echo Moved %FileName% to directory BYPASS
)
rem Exit the subroutine WriteToLogFile.
goto :EOF
:DupCheck
rem Check for ISA control number in file %VW%_table.txt.
%SystemRoot%\System32\Findstr.exe /X /M /C:%ISActrlnum% "C:\VP\TEST\%VW%_table.txt" >nul
if errorlevel 1 goto NewControl
rem This ISA control number is already present in file %VW%_table.txt.
echo Duplicate control %ISActrlnum% found in file %FileName%.
echo %ISActrlnum%,%FileName%>>"C:\VP\TEST\Dupfile.txt"
move /Y "%FileName%" "DUPLICATES"
echo Moved %FileName% to directory DUPLICATES.
rem Exit the subroutine DupCheck.
goto :EOF
:NewControl
echo %ISActrlnum%>>"C:\VP\TEST\%VW%_table.txt"
Any help is appreciated.
Manipulating text files with native batch commands is rather tricky, and quite slow. Most tasks can be done, but it requires quite a few advanced batch techniques to make the solution robust.
You will probably be most happy with GnuWin32 - a free collection of unix utilities for Windows. You could then manipulate file content with familiar tools.
Another good alternative (my favorite - no surprise since I wrote it) is to use REPL.BAT - a hybrid JScript/batch utility that performs a regex search/replace operation on stdin and writes the result to stdout. It is pure script that will run natively on any Windows machine from XP forward. Full documentation is embedded within the script.
I recommend replacing your line delimiter with \r\n rather than \n, as that is the Windows standard for newlines.
Assuming REPL.BAT is in your current directory, or somewhere within your PATH, then the following will make your needed changes:
set "file=fileName.txt"
type "fileName.txt" | repl "{" "\r\n" lx >"%file%.new"
move /y "%file%.new" "%file%" >nul
:GS_replace
<"%file%" call repl "^(GS~.*)1145837" "$11119283" >"%file%.new"
set "rtn=%errorlevel%"
move /y "%file%.new" "%file%" >nul
if %rtn% equ 0 goto GS_replace
I'm concerned that your string of numeric digits could be embedded within a larger number, leading to an unwanted substitution. You might want to refine your search term to prevent this.
The following would only replace an entire field:
:GS_replace
<"%file%" call repl "^(GS~(?:.*~)*)1145837(~|$)" "$11119283$2" >"%file%.new"
set "rtn=%errorlevel%"
move /y "%file%.new" "%file%" >nul
if %rtn% equ 0 goto GS_replace
The following would only replace an entire number that may be embedded within a larger alpha-numeric string:
:GS_replace
<"%file%" call repl "^(GS~(?:.*\D)*)1145837(\D|$)" "$11119283$2" >"%file%.new"
set "rtn=%errorlevel%"
move /y "%file%.new" "%file%" >nul
if %rtn% equ 0 goto GS_replace
In your comment below, you say you want to restrict the number change to the 3rd field of GS lines. (This is quite different than what you stated in your original question.) This is much simpler - no loop is required:
type "%file%" | repl "^(GS~(.*?~){2})1145837(~|$)" "$11119283$2" >"%file%.new"
move /y "%file%.new" "%file%" >nul