I don't have much experience with batch scripts, and my employer has asked me write a batch script that can be run to find and replace some text inside all matching files in the directory.
I've tried searching this and there's a tonne of resource, which I've used to get me this far:
#echo off
setlocal enableextensions disabledelayedexpansion
set "search=<employeeloginid>0"
set "replace=<employeeloginid>"
set "textFile=TimeTEQ20170103T085714L.XML"
for /f "delims=" %%i in ('type "%textFile%" ^& break ^> "%textFile%" ') do (
set "line=%%i"
setlocal enabledelayedexpansion
set "line=!line:%search%=%replace%!"
>>"%textFile%" echo(!line!
endlocal
)
This will find all occurrences of <employeeloginid>0 and replace it with <employeeloginid> inside a set file - in this case TimeTEQ20170103T085714L.XML.
I now need to tweak this to run on all files that start with TimeTEQ and end with .xml
I found this answer which shows how to do all files in a directory, but I don't know how I would tweak it to suit my needs here.
Can anyone help me please?
Simply wrap around a standard for loop, like this:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "search=<employeeloginid>0"
set "replace=<employeeloginid>"
set "textFile=TimeTEQ*.xml"
set "rootDir=."
for %%j in ("%rootDir%\%textFile%") do (
for /f "delims=" %%i in ('type "%%~j" ^& break ^> "%%~j"') do (
set "line=%%i"
setlocal EnableDelayedExpansion
set "line=!line:%search%=%replace%!"
>>"%%~j" echo(!line!
endlocal
)
)
endlocal
If you want to process matching files also in the sub-folders, use a for /R loop:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "search=<employeeloginid>0"
set "replace=<employeeloginid>"
set "textFile=TimeTEQ*.xml"
set "rootDir=."
for /R "%rootDir%" %%j in ("%textFile%") do (
for /f "delims=" %%i in ('type "%%~j" ^& break ^> "%%~j"') do (
set "line=%%i"
setlocal EnableDelayedExpansion
set "line=!line:%search%=%replace%!"
>>"%%~j" echo(!line!
endlocal
)
)
endlocal
You can surround the loop you got currently with another one going through all files in the directory that match your pattern like this:
for /f "delims=" %%n in ('dir /b ^| findstr /r "TIMETEQ.*\.xml') do (
and change the header of your current loop to:
for /f "delims=" %%i in ('type "%%~n" ^& break ^> "%%~n" ') do (
and >>"%textFile%" echo(!line! to
>>"%%~n" echo(!line!
So in total the new script should look like this:
#echo off
setlocal enableextensions disabledelayedexpansion
set "search=<employeeloginid>0"
set "replace=<employeeloginid>"
set "textFile=TimeTEQ20170103T085714L.XML"
for /f "delims=" %%n in ('dir /a-d /b ^| findstr /r "TIMETEQ.*\.xml') do (
for /f "delims=" %%i in ('type "%%~n" ^& break ^> "%%~n" ') do (
set "line=%%i"
setlocal enabledelayedexpansion
set "line=!line:%search%=%replace%!"
>>"%%~n" echo(!line!
endlocal
)
)
Explanation:
The new added outer loop goes through the output of the command dir /b ^| findstr /r "TIMETEQ.*\.xml which itself contains the result of a regex search over the output of the dir command with /b switch to show only the filenames and /a-d to execlude directories from the search.
Then I changed %filename% to the parameter of the loop -> %%~n with ~ to remove potential surrounding double quotes.
for people still searching this on google, the easy way is to open all files containing the text needed to be replaced in notepad++, press ctrl+F, click replace, type in what you want to find and replace and click replace all, then save all.
Related
Apologies for my poorly worded question and my scatterbrain workings. Essentially I want to set an unknown number of variables that are parsed from each line of a text file.
I have written batch file to create symbolic links for network shares to a C:\Volumes folder.
#echo off
echo:
set /p dest=ENTER FOLDER PATH:
set dest="%dest%"
net use %dest%
if not exist "C:\Volumes" MD "C:\Volumes"
for %%i in (%dest%) do (set "fold=%%~ni")
mklink /d "c:\VOLUMES\%fold%" "%dest%"
pause
What I want to try is the same theory but have the script point at a text file mounts.txt with a list of folder paths and for a for loop to cycle through the list make a symbolic link for each path in the list. I have toyed with counters and cannot get it working correctly. I don't think I am going about it the right way at all.
Contents of mounts.txt
\\10.19.10.238\Masters\Removed bin\Work here
\\10.19.10.241\Scanning\WIP\to process
This does not work:
#echo off
setlocal enableDelayedExpansion
set i=1
:add
Set /a "i+=1"
for /F "tokens=*" %%A in (mounts.txt) do (set dest%i%=%%A)
if exist %dest%%i% goto:add
echo %dest%
echo %dest%%i%
echo !dest!
echo !dest!%i%
pause
Nor this:
#echo off
setlocal enableDelayedExpansion
set i=0
For /F "Tokens=1* Delims=] EOL=" %%A In ('Find /N /V ""^<"mounts.txt"') Do (
set /a i=i+1
set "dest!i!=%%B"
)
For /l %%a in (1,1,4) do echo _dest%%a is !dest%%a!
For /l %%a in (1,1,4) do set dest%%a=!dest%%a!
echo !dest!
pause
I did get something like this to work to an extent but cannot figure out how to use the dest[1], dest[2] as variables in other processes further down in the script.
#echo off
setlocal enabledelayedexpansion
set counter=0
for /f "tokens=*" %%a In (mounts.txt) do (
set /a counter+=1
set "dest[!counter!]=%%a"
)
set dest[
And the list could be added to with many more. If %dest%n variables can be set, the use the same theory to set different %fold% variables based on each %dest%n then maybe the links can be set using the same process as the original script.
Any help is appreciated. Thank you.
If your question actually describes what you want to do, you don't need to set variables. Just use the lines from the text file with a for /f loop:
for /f "delims=" %%i in (mount.txt) do mklink /d "C:\VOLUMES\%%~ni" "%%i"
It's safer to quote the filename, especially if you want/need to use the FQFN. Therefore you need the usebackq option, else a quoted string will be processed as a string, not a filename:
for /f "usebackq delims=" %%i in ("C:\full path\mount.txt") do mklink /d "C:\VOLUMES\%%~ni" "%%i"
You seemingly want to dynamically set the variables as dummy array's and return results based on the line numbers. If so, use the counter you created to become the max of the for /L loop, here is an example by just echoing the results:
#echo off & set cnt=0
setlocal enabledelayedexpansion
for /F "usebackq delims=" %%i in ("mounts.txt") do (
set /a cnt+=1
set "var[!cnt!]=%%i"
)
for /L %%a in (1,1,!cnt!) do echo !var[%%a]!
Edit
As highlighted by #Compo in the comments; after the initial code block where we increment the %cnt% variable, we no longer require using delayedexpansion on the %cnt% variable specifically and can therefore use %cnt% instead of !cnt!:
#echo off & set cnt=0
setlocal enabledelayedexpansion
for /F "usebackq delims=" %%i in ("mounts.txt") do (
set /a cnt+=1
set "var[!cnt!]=%%i"
)
for /L %%a in (1,1,%cnt%) do echo !var[%%a]!
This is a courtesy example, just to show you how you could have still used find.exe, as in one of your examples, and additionally includes a different method of iterating the variables with unknown increments:
#Echo Off
SetLocal EnableExtensions
Rem The next line ensures that there are no variables defined with names beginning _
For /F "Delims==" %%G In ('"(Set _) 2>NUL"') Do Set "%%G="
Rem The next lines parse the directory paths content file and defines the incrementing variables
For /F "Tokens=1,* Delims=]" %%G In (
'"%SystemRoot%\System32\find.exe /N "\" 0< "C:\Users\roar\My Directory\mounts.txt" 2>NUL"'
) Do (
Set "_Path%%G]=%%H"
Set "_Name%%G]=%%~nxH"
)
Rem The next lines iterate the defined variables beginning _Path[ and runs commands against them
For /F "Tokens=2 Delims=[]" %%G In ('"(Set _Path[) 2>NUL"') Do (
SetLocal EnableDelayedExpansion
MkLink /D "C:\Volumes\!_Name[%%G]!" "!_Path[%%G]!"
EndLocal
)
Rem The next line undefines any previously defined variables named beginning with _
For /F "Delims==" %%G In ('"(Set _) 2>NUL"') Do Set "%%G="
Please note that is likely you may need to run this script elevated, in order for the MkLink command to create the symbolic links.
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.
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.
I could use some help, I've tried similar topics but I couldn't find exactly what I was looking for.
What I need is a batch script which loops through a directory searching for files of type .pro but don't begin with the character {.
The script then searches all the files and echoes the lines which are found between the numbers 574, and 575,.
I've got all the things done except when I try to check for file names I cant get it to work properly and it seems so simple.
My script looks like this:
#echo off
set "sPath=C:\Users\jspajic\Desktop\batskripte\test\test files"
set "ispis=C:\Users\jspajic\Desktop\batskripte\test\Opera_procesi_DataTabovi.txt"
echo %sPath% > %ispis%
pushd %sPath%
for %%f in (*.pro) do ( :: ---> I guess this part needs to be altered
setlocal enabledelayedexpansion
for /f "delims=]" %%a in ('findstr /n /B "574," "%%f"') do (set s=%%a )
if !s! neq 0 (
for /f "delims=]" %%b in ('findstr /n /B "575," "%%f"') do (set kraj=%%b)
if !kraj! neq 0 (
#set /a "ss=s+1"
#set /a "kraj=kraj+0"
#set /a "s=s+0"
if !ss! NEQ !kraj! (
#echo:
echo %%f
#set /a "counter=1"
for /F "tokens=1* delims=:" %%A in ('type "%%f" ^| findstr /n "^"') do (
if !counter! GTR !s! if !counter! LSS !kraj! echo.%%B
#set /a "counter=counter+1"
)
)
)
)
endlocal
) >> %ispis%
popd
I tried with for /f "delims=" %%a in ('dir /B *.pro^|findstr "[A-z]*\.pro" ')
or with similar ideas but I cant get it to work cause I just dont know batch scripts that well.
for %%f in (*.pro) do ( set "dummy=%%f"
setlocal enabledelayedexpansion
if "!dummy:~0,1!" neq "{" (
should solve your problem. Note that it adds an extra level of parentheses, so a matching ) needs to be added after the endlocal but you seem to be adept at doing that.
Assign the filename to a dummy variable, then see whether the 1st character of that dummy is NOT equal to "{" before executing the rest of the code.
From the prompt, set /? will show documentation about substringing - you seem to have delayed expansion worked out.
Because your character does not have a case, (upper | lower), and your search directory is already current; this can be easily catered for using EOL in your For loop:
For /F "EOL={Delims=" %%f In ('Dir/B/A-D *.pro') Do (
use for /f with dir, filtered by findstr (eliminate every token that starts with {):
for /f "delims=" %%f in ('dir /b /a-d *.pro^|findstr /vb "{"') do (
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!")