Last some days i am stuck with my requirment.first i am explaining my logic.i am calculating the a specific line no of a file with some logic.then passing that to a function :copy_text.in this function i am copying data.txt file to output file with adding one more line (line i passed as parameter) inside the file.my issue that the code is now working fine,but taking 7 minute to copy 40k records.if i will comment the function :line_count,it is taking just 30 secs to process but i can not able to add that line in my output file.
i can not change this enabledelayedexpansion and disabledelayedexpansion parameters because i am facing lots of issue at the time of printing values from file.because my file value contains ! and % characters,and also the length of my each line is more then 3000 chars.
Exp:OtherInformation: 74.49% subsidiary of Sources: tps://pro/search/xyz/card/index.html?code=!121212#!121;
Only my problem is there in counting the lines of file to add the text in that particular line no.please suggest me how i will use this part to run the script quick.
#ECHO Off
echo.%time%
setlocal enabledelayedexpansion
set /a hold_list_line_no=0 (This value will come from different logic)
::>>>>>>>>>>>>>>>>>>>>>>>>
call :copy_text !hold_list_line_no!
endlocal
echo.%time%
pause
exit 0;
:copy_text
setlocal disabledelayedexpansion
set /a line=%~1
FOR /F "delims=" %%c IN (C:\Users\1519499\Desktop\data.txt) DO (
call :line_count
echo %%c>>output.txt
)
:line_count
SET /A i+=1
if %i% equ %line% (
type add_this.txt>>C:\Users\1519499\Desktop\output.txt
)
Try this:
#ECHO Off
echo %time%
setlocal enabledelayedexpansion
set /a hold_list_line_no=0 (This value will come from different logic)
::>>>>>>>>>>>>>>>>>>>>>>>>
call :copy_text !hold_list_line_no!
endlocal
echo %time%
pause
exit 0
:copy_text
set /a line=%~1, i=0
(FOR /F "delims=" %%c IN (C:\Users\1519499\Desktop\data.txt) DO (
SET /A i+=1
if !i! equ %line% type add_this.txt
setlocal disabledelayedexpansion
echo %%c
endlocal
)) > output.txt
This method avoids the call command and >> append redirection, that are inherently slow... You may also empty the environment before the copy_text loop:
:copy_text
setlocal enabledelayedexpansion
for /F "delims==" %%a in ('set') do set "%%a="
Related
I am working on a script which iterates over every file in a specific folder and reads some information from, and numbers each.
So I am running over the files with a for-loop and that is working correctly. Now I added a variable i which should increment on each iteration of the loop.
I used set /a i=0 and inside the for-loop set /a i+=1 and this Set command does print the number to console. My problem now is that the set command prints the number, but when I echo the number with echo %i% it will always print 0 and not the increasing value. I also tried echo !i! but that does not work at all. It just prints !i! in the console.
I also added a pause command to the end of the script, but that gets ignored entirely.
This is my batch script:
#echo off
setlocal EnableDelayedExpansion
set /a i=0
for /r %%n in (Links\*.lnk) do (
set /a i+=1
echo.
echo [Button!i!Back]
get.bat "%%n"
)
pause
This is an example of the output:
45
[Button!i!Back]
###HudIcons\VLC media player.ico
D:\Programme\VideoLAN\VLC\vlc.exe
I also just realized, that for the first time the loop runs, the !i! does work correctly and prints the number, but not afterwards.
I know that I should probably not be calling the other batch file like this, but that is temporary.
Any ideas why this is behaving so weird?
Perhaps it would be easier for you without the Set /A incrementing method, and therefore no need for delayed expansion. The alternative methodology could involve using findstr.exe to provide the counting:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
For /F "Tokens=1,* Delims=:" %%G In ('Dir /B /S /A:-D "Links\*.lnk" ^
2^> NUL ^| %SystemRoot%\System32\findstr.exe /EILN ".lnk"') Do (Echo=
Echo [Button%%GBack]
Call "get.bat" "%%H")
Pause
You use percentages symbol to call a variable %Variable%
and to echo it echo %variable%
to set one set variable=value Hope this helps you.
I used the following code, but set Content is blank in my case. Please help. Thanks.
set content=
for /f "delims=" %%i in (fileA.txt) do set content=%%i
for /f "delims=" %%i in (FileA.txt) do set content=%content% %%i
ECHO %content%> result.txt
FileA.txt
test
A
Testing
B
Expected Output:
test A
Testing B
You need a single for command to process all lines and this simple logic: if it is the first line read, store it; else show the stored first line and the second one AND delete the first line, so the same logic be used in all line pairs:
#echo off
setlocal EnableDelayedExpansion
set "firstLine="
(for /F "delims=" %%a in (FileA.txt) do (
if not defined firstLine (
set "firstLine=%%a"
) else (
echo !firstLine! %%a
set "firstLine="
)
)) > result.txt
Your two for work independently (the second starts when the first is finished).
Your first loop gets the last line of the file and then the second adds every line of the textfile to the variable (there is a limit for variable length and you will soon reach it with this method).
The empty variable at the end is due to lack of using delayed expansion.
Work with a single for and an alternating flag instead:
#echo off
setlocal enabledelayedexpansion
set flag=0
(for /f "delims=" %%i in (FileA.txt) do (
if !flag!==0 (
<nul set /p ".=%%i "
) else (
echo %%i
)
set /a "flag=(flag+1) %% 2"
))>result.txt
Note: due to batch/cmd limitations, this may have some problems (line length, special characters
We need '#echo off' statement to not to print code on every execution of the program and only echo statements, 'rem' is to mention the line is a comment. 'SETLOCAL EnableExtensions EnableDelayedExpansion' is need to enable ! statements to resolve the variables.
#echo off
rem this for loop reads the file FileA.txt line by line by specifying delims= (nothing)
rem then checks the condition if the line is even line or not, if odd then adding it to myVar variable
rem if even then printing both earlier odd with the current even line to the result.txt file.
set myVar=
set nummod2=0
set /a i=0
rem creating an empty file on everytime the program runs
copy /y nul result.txt
SETLOCAL EnableExtensions EnableDelayedExpansion
for /f "delims=" %%a in (FileA.txt) do (
set /a i=i+1
set /a nummod2=i%%2
if !nummod2!==0 (
echo !myVar! %%a
) else (
set myVar=%%a
)
) >> result.txt;
echo 'Done with program execution. Result saved to result.txt in the same folder of this batchfile'
rem pause
In below code i am tring to fetch the line no of string "AXX0000XXXA" from file data.txt,then fetching line by line and printing target.txt file,in between if the line reach the find line no i am adding one more line from file temp.txt.The code is working fine with the less nos of records(tested with 150 lines-File Size 100 kb),but when i am processing with 50K records(File Size 25MB) it is taking more then 25 minutes to process.could you please help me how i will process same in less time.
#echo off
setlocal enabledelayedexpansion
for /f "delims=:" %%a in ('findstr /n "AXX0000XXXA" "C:\Users\23456\Desktop\data.txt"') do (set find_line=%%a)
set /a counter=0
for /f "usebackq delims=" %%b in (`"findstr /n ^^ C:\Users\23456\Desktop\data.txt"`) do (
set curr_line=%%b
set /a counter=!counter!+1
if !counter! equ !find_line! (
type temp.txt >> target.txt
)
call :print_line curr_line
)
endlocal
:print_line
setlocal enabledelayedexpansion
set line=!%1!
set line=!line:*:=!
echo !line!>>target.txt
endlocal
Your code uses three Batch file constructs that are inherently slow: call command, >> append redirection and setlocal/endlocal, and these constructs are executed once per each file line! It would be faster to include the subroutine into the original code to avoid the call and setlocal commands, and an echo !line!>>target.txt command imply open the file, search for the end, append the data and close the file, so it is faster to use this construct: (for ...) > target.txt that just open the file once. An example of a code with such changes is in Compo's answer.
This is another method to solve this problem that may run faster when the search line is placed towards the beginning of the file:
#echo off
setlocal enabledelayedexpansion
for /f "delims=:" %%a in ('findstr /n "AXX0000XXXA" "C:\Users\23456\Desktop\data.txt"') do (set /A find_line=%%a-1)
call :processFile < "C:\Users\23456\Desktop\data.txt" > target.txt
goto :EOF
:processFile
rem Duplicate the first %find_line%-1 lines
for /L %%i in (1,1,%find_line%) do (
set /P "line="
echo !line!
)
rem Insert the additional line
type temp.txt
rem Copy the rest of lines
findstr ^^
exit /B
This should create target.txt with content matching data.txt except for an inserted line taken from tmp.txt immediately above the line matching the search string, AXX0000XXXA.
#Echo Off
Set "fSrc=C:\Users\23456\Desktop\data.txt"
Set "iSrc=temp.txt"
Set "sStr=AXX0000XXXA"
Set "fDst=target.txt"
Set "iStr="
Set/P "iStr="<"%iSrc%" 2>Nul
If Not Defined iStr Exit/B
Set "nStr="
For /F "Delims=:" %%A In ('FindStr/N "%sStr%" "%fSrc%" 2^>Nul') Do Set "nStr=%%A"
If Not Defined nStr Exit/B
( For /F "Tokens=1*Delims=:" %%A In ('FindStr/N "^" "%fSrc%"') Do (
If "%%A"=="%nStr%" Echo %iStr%
Echo %%B))>"%fDst%"
I have made it easy for you to change your variable data, you only need to alter lines 3-6.
I have assumed that this was your intention, your question was not clear, please accept my apologies if I have assumed incorrectly.
Let me start off by saying I am very new to this, and what little code I have cobbled together I found on this site.
In the end I need a batch that when ran will grab each folder name in a parent dir. and copy it to a text file named label1, label2, ect.
I started with pulling the lines from a directory list in a text file. I can get it to echo the last line to a file using Seth's code from this post
Windows Batch file to echo a specific line number
I made some modifications to try to put it in a loop and now I get nothing out.
If anyone can help me it would be much appreciated. Here is my code so far.
set /a "x=1"
set /a "lines=91"
:while1
if %x% leq %lines% (
for /f "tokens=*" %%a in ('findstr /n .* "Y:\Test\foldernametest.txt"') do (
set "FullLine=%%a"
for /f "tokens=1* delims=:" %%b in ("%%a") do (
setlocal enabledelayedexpansion
set "LineData=!FullLine:*:=!"
if "%%b" equ "%1" echo(!LineData!
echo title=!linedata! > Lable%x%.dat
set /a "x= x+1"
endlocal
goto :while1
)
)
setlocal enabledelayedexpansion
set /a x=1
set /a lines=91
:while1
if %x% leq %lines% (
for /f "tokens=*" %%a in ('findstr /n .* "Y:\Test\foldernametest.txt"') do (
for /f "tokens=1* delims=:" %%b in ("%%a") do (
if "%%b" equ "%x%" (
echo(%%c
echo title=%%c > Lable%x%.dat
set /a x= x+1
goto while1
)
)
)
endlocal
I'm reasonably sure this will work, as would
setlocal enabledelayedexpansion
set /a x=1
set /a lines=91
:while1
if %x% leq %lines% (
for /f "tokens=1* delims=:" %%a in ('findstr /n .* "Y:\Test\foldernametest.txt"') do if %%a==%x% (
echo(%%b
echo title=%%b > Lable%x%.dat
set /a x= x+1
goto while1
)
)
endlocal
The issue with your code is that endlocal terminates a setlocal and all of the environment changes that have taken place since the setlocal are backed out - the environment is restored to what it was when the setlocal was executed.
The consequence is that with your code, you are incrementing x (a grand name for a variable) and then the increment is backed out when the endlocal is executed.
So - put the entire routine in a setlocal/endlocal bracket. This has other advanteages - like if you execute a setlocal immediately after #echo off, then when the routine terminates, the environment is returned to its original state - it does not accumulate changes (normally additions of variables) as more and more batches are run.
Some of the other changes I've made are cosmetic. the quotes in a set /a are superfluous and so is the colon in a goto (with the sole exception of goto :eof)
Another problem you have was %1 (meaning "the first parameter to the routine") where you probably meant "%x%".
In the first code fragment, the output of the findstr is assigned to %%a and the inner for assigns that part of the findstr before the delimiter to %%b and that after to %%c. You evidently want to pick the line %%b equal to %x% so the code makes the comparison and if equal, outputs %%c (rest of line) and title=%%c to the file made from Lable and the line number. (You've spelled label incorrectly); then increments x and tries again.
The second piece of code is a simplification of the first. The line is read from the file and numbered, then split directly on the colon; %%a gets the number, %%b the rest of the line, so if %%a is the same as the number %x% then we want to do something (no quotes required, since %%a is a simple numeric string and x will also be numeric because it's never assigned to a string containing separators or empty).
The thing-to-be-done is to echo the line from the file (in %%b, bump the line number and start again...
I want a batch file which will find out which is the second latest folder created/modified in a directory.
I found this article but no matter how much i tried i could not understand how it works
#echo off
set "root_dir=c:\somewhere"
pushd "%root_dir%"
set "bl1="
set "bl2="
setlocal enableDelayedExpansion
for /f "tokens=* delims=" %%# in ('dir /b /a:-d /o:d') do (
set "bl2=!bl1!"
set "bl1=%%#"
)
echo %bl2%
endlocal
If i use it as it is then i can get the second latest folder but this script is supposedly able to get which ever latest folder you need , be it 1st or nth.
Could someone please tell me what modifications need to be done to the script to accomplish that. Also how exactly this script works
In your approach, the latest folder is already available in variable bl1; add echo %bl1% at the end before endlocal to display it. Retrieving the nth folder is simply not possible in a flexible way with that script as you would need to define another variable (say bl3, bl4,..., bln) within the loop.
However, you could reverse the sort order of the output of the dir command by changing the /O option, so it returns the latest (most recent) item first. Then let an index number count the iterations of the loop, and if that index equals the predefined number n, store the currently iterated item:
#echo off
setlocal EnableDelayedExpansion
rem // Define N here to get Nth-latest folder:
set /A LATEST=2
set /A INDEX=0
for /F "eol=| delims=" %%# in ('dir /B /A:D /O:-D "C:\somewhere"') do (
set /A INDEX+=1
if !INDEX! EQU !LATEST! (
set "ITEM=%%#"
)
)
if defined ITEM echo %LATEST%th-latest folder: %ITEM%
endlocal
exit /B
Update
Here is a modified script with the following improvements:
Exclamation marks ! in folder names are no longer lost due to toggling delayed expansion;
the target directory can be provided as the first command line argument; if omitted, the current directory is used;
the number n can be given as the second command line argument; if omitted, the user is prompted for it (this addresses elzooilogico's comment); n defaults to 1 for empty input;
the display output is improved to avoid something weird like 1th-latest, 2th-latest and 3th-latest; instead, The latest, 2nd-latest and 3rd-latest is returned, respectively;
So this is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem /* define location path and folder pattern as 1st command line argument;
rem /* Define number N as 2nd command line argument to get Nth-latest folder. */
set "LATEST=%~2"
set /A LATEST+=0
if %LATEST% LEQ 0 (set /P LATEST="Enter N [1]: " & set /A LATEST+=0)
if %LATEST% LEQ 0 set /A LATEST=1
set /A INDEX=0
for /F "eol=| delims=" %%# in ('dir /B /A:D /O:-D "%~1"') do (
set /A INDEX+=1
setlocal EnableDelayedExpansion
if !INDEX! EQU !LATEST! (
endlocal
set "ITEM=%%#"
goto :SKIP & rem // break loop after having retrieved Nth-latest folder;
) else endlocal
)
:SKIP
setlocal EnableDelayedExpansion
if defined ITEM (
if %LATEST% EQU 1 (echo The latest file: !ITEM!) else (
if %LATEST% EQU 2 (echo 2nd-latest file: !ITEM!) else (
if %LATEST% EQU 3 (echo 3rd-latest file: !ITEM!) else (
echo %LATEST%th-latest file: !ITEM!)))
)
endlocal
endlocal
exit /B
To achieve a similar result as with the simple script on top of this answer, you need to call this script by the following command line, supposing it has been saved as Nth-latest.bat:
Nth-latest.bat "C:\somewhere" 2