Using a filename variable in a for loop - batch-file

I have a directory with a file in it (actually it has a lot of files, but I figured getting one to work was the first step before building the loop to hit each file), that needs to be edited and saved with a similar filename. Instead of manually typing in the filename, I'd like to use a variable containing the filename.
Sample Input Data
FileX.txt
Text line
Text line
Text line
Desired Output Data
FileX2.txt
1 Text line
2 Text line
3 text line
I can manage this with one file at a time, I'm struggling to write one script to look through the contents of a folder and do this to each text file within the folder.
I'm on windows 7 and this is what I have so far:
#echo off
setLocal EnableDelayedExpansion
Set N=0
REM THE BELOW LINE IS THE VARIABLE I'M TRYING TO SET AND THEN HAVE PASSED AS THE FOR LOOP PARAMETER
Set F="FILENAME"
REM IF I SKIP A VARIABLE AND HARDCODE THE FILENAME IN THE BELOW LINE, IT WORKS FOR THE ONE FILE
for /f "tokens=* delims= " %%a in (FILENAMEVARIABLE.txt) do (
Set /a N=!N!+1
echo !N! %%a, >> !F!.txt
)
The opening statement of the for loop, is where I can't get the variable to take. In FILENAMEVARIABLE.txt I have tried %%F, %F, !F!, and %%~nxf none of which manage to call the correct file to start the loop. Any ideas what I'm doing wrong here?

Here's what I think you were trying to do:
#Echo Off
SetLocal EnableDelayedExpansion
For %%A In (*.txt) Do (Set "N=0"
(For /F "UseBackQ Tokens=*" %%B In ("%%A") Do (Set /A N +=1
Echo !N! %%B))>"%%~nA2%%~xA")
You could also use FindStr:
#Echo Off
For /F "Tokens=1-2* Delims=:" %%A In ('FindStr /N "^" *.txt 2^Nul'
) Do >>"%%~nA2%%~xA" Echo %%B %%C
In both of the above examples I have assumed that the source directory and script directory are the same.

Related

How to create a batch file on Windows to write out strings from a text file based on a substring and new line?

folks!
I'm not sure if this is a very simple task or a very complicated one, but either way I'm struggling with it. Suppose I have a text file like this:
11111FOO
11111BAR
22222ZOOM
33333FOO
11111CAR
I want to figure out a command line in windows that I can plop into a batch file that will pull out text strings from this file and push them to a new file. I would pass in the leading string to search for, and it would take everything from the end of that string to the next new line.
So using the above example, if I said the leading string was 11111, I would get a new text file that looked like this:
FOO
BAR
CAR
Everything else would be ignored.
Thanks!
If there are no potential poison characters in the input file then perhaps this would suffice:
#(For /F "UseBackQ Delims=" %%A In ("input.txt") Do #(Set "an=%%A"
For /L %%B In (%%A 1 %%A) Do #Call Echo %%an:*%%B=%%))>"output.txt"
Magoo's additions:
#Echo Off
(For /F "UseBackQ Delims=" %%A In ("q46858215.txt") Do (Set "an=%%A"
For /L %%B In (%%A 1 %%A) Do Call Echo %%an:*%%B=%%))>"Output1.txt"
If "%~1"=="" GoTo :Next
(For /F "Delims=" %%A In ('FindStr/BLC:"%~1" "q46858215.txt"') Do (
Set "an=%%A"
For /L %%B In (%%A 1 %%A) Do Call Echo %%an:*%%B=%%))>"Output2.txt"
:Next
(For /F "UseBackQ Delims=" %%A In ("q46858215.txt") Do (Set "an=%%A"
For /L %%B In (%%A 1 %%A) Do Echo %%B))>"Output3.txt"
I used a file named q46858215.txt containing OP's data for my testing.
Produces three output files:
Output1.txt : Compo's original
Output2.txt : filtered to isolate lines beginning with (first parameter to routine)
Output3.txt : Compo's original in reverse, showing the numbers isolated

Batch to delete the first line of a text file without creating a new file

Is there any solution to this question? I see a lot of questions about deleting first line of a text file but all of them need create a new text file. I need this because my text file is constantly updating with new lines (by a second batch file) so if the script creates a new text file it can accidently delete some new lines with the old text file.
Can i delete first line of a text file without creating a new one? If not, why?
Using only batch.
Blank lines shouldn't be preserved (If possible).
Especial characters like ! can be deleted.
IMO this question makes no sense. There is no way to avoid to preserve the new file contents in a place different than the original file; this is true even in any advanced programming language, that would require to read from second line to end of file and copy each line to the beginning of the file. However, at end of the process it would be necessary to truncate the file in order to eliminate the last bytes in the file (with the number of bytes that the first line had).
A Batch file certainly can not perform this type of process, so the lines of the file (from second one up to the end of file) must necessarily be stored in a place different than the original file. One of the answers store the lines in memory variables, but this method is inefficient specially if the file is large.
So, if the question is: "what tricks can be used in order to not use a new file to eliminate the first line in a data file?", then this is a more efficient method:
#echo off
for /F "skip=1 delims=" %%a in ('type input.txt ^& del input.txt') do >> input.txt echo %%a
If you want to preserve empty lines, use this method instead:
#echo off
for /F "skip=1 tokens=1* delims=:" %%a in ('findstr /N "^" input.txt ^& del input.txt') do >> input.txt echo(%%b
You should note that when the for /F command start execution it blocks the file for an exclusive access, so any attempt to modify the file while the for /F is reading it would be avoided with "access denied" error.
If you don't care about preserving blank lines, !, (, ), or ^, you can run the input file through a for loop, storing each line in a separate variable, then merging the variables with a newline character at the end of each one.
#echo off
setlocal enabledelayedexpansion
cls
set counter=0
for /f "delims=" %%A in (input.txt) do (
set line[!counter!]=%%A
set /a counter+=1
)
set /a counter-=2
set LF=^
for /L %%A in (1,1,!counter!) do set sheet=!sheet!!line[%%A]!!LF!
set /a counter+=1
for /L %%A in (!counter!,1,!counter!) do set sheet=!sheet!!line[%%A]!
echo !sheet!>input.txt
However, if you want to preserve blank lines and special characters, there are a few tricks you can throw in, but the overall idea is the same.
#echo off
setlocal enabledelayedexpansion
cls
:: findstr /n puts line numbers at the start of each line, which will allow us to preserve blank lines
for /f "tokens=1 delims=:" %%A in ('findstr /n "^" input.txt') do set line_counter=%%A
::set /p preserves special characters
<input.txt (
for /L %%A in (1,1,!line_counter!) do set /p line[%%A]=
)
set /a line_counter-=1
set LF=^
:: Do NOT delete the two blank lines above this line.
for /L %%A in (2,1,!line_counter!) do set sheet=!sheet!!line[%%A]!!LF!
set /a line_counter+=1
for /L %%A in (!line_counter!,1,!line_counter!) do set sheet=!sheet!!line[%%A]!
echo !sheet!>input.txt
In general a new file is needed because the requested operation requires that you READ and WRITE. So with only 1 file you would be modifying the file that you are reading! If you are simply concerned about the resultant file being a different file, that can be resolved by deleting the original and renaming the new file when done. The PUSHD and POPD are optional... use if applicable. ** Untested **
pushd "YourFolderName"
set "FileName=YourInFileName.txt"
set "TempFile=SomeTempFileName.txt"
if exist "%TempFile% del /f /q "%TempFile%"
more "%FileName%" +1 > "%TempFile%"
del /f /q "%FileName%"
ren "%TempFile%" "%FileName%"
popd

Updating a variable in a batch file

I have done some researching on this topic but everything I have tried doesn't work properly. I just want to add a counter to certain aspects of my batch file. The count.txt file contains:
Counters started on 2-18-15
opened: 0
actions: 0
The script that I have written so far is:
setlocal enabledelayedexpansion
for /F "usebackq tokens=2" %%r in (`findstr opened: counter.txt`) do (
echo %%r
set opened=%%r
set /a opened=!opened!+1
echo opened= !opened!
)
I would like to just edit the number of opened times and not change anything else in the file. I know this is rememdial but I am still very remedial in my batch abilities.
I have specified ! in place of %, using some debugging it is pulling the correct variable and adding 1 to it, the current issue is that it is not saving over that variable afterwards.
You need to use delayed expansion to properly display variables set inside of code blocks.
setlocal enabledelayedexpansion
for /F "usebackq tokens=2" %%r in (`findstr /C:"opened:" counter.txt`) do (
set opened=%%r
set /a opened=!opened!+1
echo opened=!opened!
)
You never write the information back to the file - you are simply echoing the value to the screen.
Batch does not have a mechanism to modify a value in a file directly. You must write a new file with the updated information, and then replace the old file with the new.
Assuming the order of the lines does not matter, I would write a batch script as follows:
#echo off
setlocal
for /f "tokens=2" %%N in ('findstr /bc:"opened: " counter.txt') do set /a opened=%%N+1
>counter.txt.new (
findstr /vbc:"opened: " counter.txt
echo opened: %opened%
)
move /y counter.txt.new counter.txt >nul
After running the script once, your counter.txt file would look like:
Counters started on 2-18-15
actions: 0
opened: 1
You might want to preserve the order of the rows. You could do so with a pure batch script, but I rarely use batch to modify text files. Instead, I would use my JREPL.BAT utility - it is faster, more reliable, and simpler (assuming you understand regular expressions and rudimentary JScript)
call jrepl "^(opened:\s*)(\d+)$" "$1+(Number($2)+1)" /j /f test.txt /o -
#ECHO OFF
SETLOCAL
(
for /F "delims=" %%a in (q28585447.txt) do (
for /F "tokens=1,2" %%q in ("%%a") do (
IF "%%q"=="opened:" (CALL :REPLACE %%r) ELSE (ECHO %%a)
)
)
)>newfile.txt
GOTO :EOF
:REPLACE
SET /a newvalue=%1+1
ECHO(opened: %newvalue%
GOTO :eof
I used a file named q28585447.txt containing your data for my testing.
Produces newfile.txt To replace your file, use move /y newfile.txt q28585447.txt (after modifying throughout for your filename, of course.
Simply, it reads each line from the data file to %%a then tokenises %%a using the default separators into %%q and %%r If %%q is the target string, then %%r contains the value to be incremented, otherwise regurgitate the line in %%a.
The subroutine :replace simlpy adds 1 to the parameter passed and reconstructs the target line.
By surrounding the entire for...%%a loop in parentheses and redirecting to a file, all of the output echoed is redirected to that file.

Batch random line for a .txt file

I'm writing a batch file and I wanted to display a splash in the main screen. I wanted the batch file to pick a random line from a text file and ECHO it. The file name would be splashes.txt.
And in it would be like:
More addicting the lemons
Apples.
This is a splash
Test
The batch file would pick a random quote based on its line.
Like if the patch file picked line 2 it would ECHO "Apples."
Note that I'm using Windows 8.
Any ideas?
While I agree with others that you should attempt this on your own, this is a relatively straight-forward script which should serve as a good working example for learning:
#ECHO OFF
SETLOCAL EnableDelayedExpansion EnableExtensions
REM Source file.
REM The first line on this file should be blank as it will never be selected.
REM Additionally, this file should have no empty lines on the end.
SET TextFile=text.txt
REM Determine the number of lines.
SET NumLines=0
FOR /F "usebackq tokens=* delims=" %%A IN (`TYPE %TextFile%`) DO SET /A NumLines=!NumLines!+1
REM Pick a random line.
SET /A RandomLine=(%RANDOM% %% %NumLines) + 1
REM Prevent skipping all the lines.
IF "%RandomLine%"=="%NumLines%" SET RandomLine=1
REM Print the random line.
FOR /F "usebackq tokens=* skip=%RandomLine% delims=" %%A IN (`TYPE %TextFile%`) DO (
ECHO %%A
REM We are done. Stop the script.
GOTO Finish
)
:Finish
ENDLOCAL
So close - but not quite. SKIP will always be at least 1 (since SKIP=0 is invalid) hence the first line in the file can never be selected.
This is a file I derived from the above with a few tickles. I've also changed the filename because of the way I work. I'm using q27829742.txt containing the lines posted.
#ECHO OFF
SETLOCAL
SETLOCAL EnableDelayedExpansion EnableExtensions
REM Source file.
REM The first line on this file should be blank as it will never be selected.
REM Additionally, this file should have no empty lines on the end.
SET "TextFile=q27829742.txt"
REM Determine the number of lines.
FOR /f %%a IN ('type "%textfile%"^|find /c /v ""') DO SET /a numlines=%%a
REM Pick a random line.
SET /A RandomLine=(%RANDOM% %% %NumLines%)
REM Prevent skipping all the lines.
IF "%RandomLine%"=="0" (SET "RandomLine=") ELSE (SET "RandomLine=skip=%randomline%")
REM Print the random line.
FOR /F "usebackq tokens=* %RandomLine% delims=" %%A IN (`TYPE %TextFile%`) DO (
ECHO %%A
REM We are done. Stop the script.
GOTO Finish
)
:Finish
ENDLOCAL
The find /v /c method counts lines in the file (find files lines that don't match (/v) "" (so that means all lines) and count them (/c) - simply more efficient.
Pick-random-number : removing the + 1 produces a result 0..(numlines-1) which is the actual number of lines to skip.
Problem there is that skip=0 is invalid, so construct a string which is eother empty (for 0) or "skip=..." (otherwise) - all ready to be included in the for /f command-options.
The syntax SET "var=value" (where value may be empty) is used to ensure that any stray spaces at the end of a line are NOT included in the value assigned. set /a can safely be used "quoteless".
If you know the number of lines to be, let's say 25.
You could have a vector holding each line, and then compute a random index and print out the corresponding line with the following example.
setlocal EnableDelayedExpansion
SET line[0]=Apples.
SET line[1]=This is a splash
SET line[2]=Test
SET line[3]=...
SET line[24]=many lines later...
SET /A index=%RANDOM% % 25
ECHO !line[index]!
A practical application of Jason Falkner's (and Magoo's) answer would be to their script and place it into a .cmd file that would reside in System32 (say randomline.cmd). This will integrate it into the command line utility, making it easily re-usable via a simple call line, avoiding you to have rewrite those lines over and over and over again in all your scripts. For example:
if you call the Magoo/Falkner script randomline.cmd and place it in System32
randomline.cmd
#ECHO OFF
SETLOCAL
SETLOCAL EnableDelayedExpansion EnableExtensions
REM Source file.
REM The first line on this file should be blank as it will never be selected.
REM Additionally, this file should have no empty lines on the end.
SET "TextFile=%1"
REM Determine the number of lines.
FOR /f %%a IN ('type "%textfile%"^|find /c /v ""') DO SET /a numlines=%%a
REM Pick a random line.
SET /A RandomLine=(%RANDOM% %% %NumLines%)
REM Prevent skipping all the lines.
IF "%RandomLine%"=="0" (SET "RandomLine=") ELSE (SET "RandomLine=skip=%randomline%")
REM Print the random line.
FOR /F "usebackq tokens=* %RandomLine% delims=" %%A IN (`TYPE %TextFile%`) DO (
ECHO %%A
REM We are done. Stop the script.
GOTO Finish
)
:Finish
ENDLOCAL
you'll be able to use it anypoint in the command prompt by entering
randomline any_old_text_file.txt
or if you want to use it in a cmd/bat script
call randomline any_old_text_file.txt
And if you want to set a value for a variable in your script from a random line in a file, you can do:
call radomline file_containing_values_on_each_line.txt>%temp%\randomlinevalue.txt
set /p any_old_variable=<%temp%\randomlinevalue.txt
del %temp%\randomlinevalue.txt

How to randomly rearrange lines in a text file using a batch file

I am creating a code that strips through different MAC addresses randomly, but cannot figure out how to do this. My thought on how to approach this is to randomize or rearrange the order of the MAC address in the text file with this script, but I cannot figure out how to do this with a batch file. How this will work is that it will read "maclist.txt", then create a new temp file with the random order "maclist_temp.txt", that will be the rearranged file. Then, it will pull this randomized file in order.
I have tried Google and searching the web, but I haven't found anything too useful. I'm still actively looking, but any advice would be extremely useful.
Something as simple as extracting and deleting a random line and then adding to the bottom might work. Randomization would be better though, but I want to keep the original list. Something like:
Make a temp copy of maclist.txt called maclist_temp.txt
Take one random MAC address, remove it from maclist_temp.txt
Readd it to the bottom
That is all I want, but any suggestions are welcome.
You may try this batch file to help you to shuffle your maclist.txt. The usage of the batch code is
C:\> type list.txt | shuffle.bat > maclist_temp.txt
Here are the contents of shuffle.bat:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET TmpFile=tmp%RANDOM%%RANDOM%.tmp
TYPE NUL >%Tmpfile%
FOR /F "tokens=*" %%i IN ('MORE') DO SET Key=!RANDOM!!RANDOM!!RANDOM!000000000000& ECHO !Key:~0,15!%%i>> %TmpFile%
FOR /F "tokens=*" %%i IN ('TYPE %TmpFile% ^| SORT') DO SET Line=%%i&ECHO.!Line:~15!
::DEL %TmpFile%
ENDLOCAL
After issuing the above command, maclist_temp.txt will contain a randomized list of MAC addresses.
Hope this helps.
Here is a simpler method to randomize/randomise a file, no temp files needed. You can even reuse the same input filename.
Limitations are: blank lines and line starting with ; will be skipped, and lines starting with = will have all leading = signs stripped and ^ characters are doubled.
#echo off
setlocal
for /f "delims=" %%a in (maclist.txt) do call set "$$%%random%%=%%a"
(for /f "tokens=1,* delims==" %%a in ('set $$') do echo(%%b)>newmaclist.txt
endlocal
I really like foxidrive's approach. Nevertheless I want to provide a solution with all the listed limitations eliminated (although cmd-related restrictions like file sizes < 2 GiB and line lengths < ~ 8 KiB remain).
The key is delayed expansion which needs to be toggled to not lose explamation marks. This solves all the potential problems with special characters like ^, &, %, !, (, ), <, >, | and ".
The counter index has been implemented in order not to lose a single line of the original text file, which could happen without, because random may return duplicate values; with index appended, the resulting variable names $$!random!.!index! are unique.
The findstr /N /R "^" command precedes every line of the original file with a line number followed by a colon. So no line appears empty to the for /F loop which would ignore such. The line number also implicitly solves the issue with leading semicolons, the default eol character of for /F.
Finally, everything up to and including the first colon (remember the said prefix added by findstr) is removed from every line before being output, hence no more leading equal-to signs are dismissed.
So here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set /A "index=0"
for /f "delims=" %%a in ('findstr /N /R "^" "%~dpn0.lst"') do (
setlocal EnableDelayedExpansion
for /F %%b in ("$$!random!.!index!") do (
endlocal
set "%%b=%%a"
)
set /A "index+=1"
)
> "%~dpn0.new" (
for /f "delims=" %%a in ('set $$') do (
set "item=%%a"
setlocal EnableDelayedExpansion
echo(!item:*:=!
endlocal
)
)
endlocal
exit /B
This seems to work. Feed it a command line parameter of the file to randomize.
#echo off
setlocal EnableDelayedExpansion
rem read the number of lines in the file
rem the find prepends the line number so we catch blank lines
for /f "delims=" %%n in ('find /c /v "" %1') do set "len=%%n"
set len=!len:*: =!
rem echo %1 has %len% lines
rem Relocate as many lines as there are lines in the file
for /l %%j in (1 1 !len!) do (
rem echo starting round %%j
rem geta random number between 1 and the number of lines in the file
set /a var=!random! %% !len! + 1
rem echo relocating line !var!
rem make sure there is no temp file
if exist %1.temp del %1.temp
rem read each line of the file, write any that don't match and then write the one that does
<%1 (
for /l %%i in (1 1 !len!) do (
rem if it is the target line then save it
if %%i == !var! (
set /p found=
rem echo saving !found!
)
rem if it is the target line then write it
if not %%i == !var! (
set /p other=
rem echo writing !other!
echo !other!>> %1.temp
)
)
rem now write the target line at the end
rem echo appending !found!
echo !found!>> %1.temp
)
rem replace the original with the temp version
move %1.temp %1>nul
)
rem print the result
type %1
Place in cmd file
for /f "tokens=2 delims=/" %%m in ('cmd /e:on /v:on /c "for /f %%f in (maclist.txt) do #echo !random!/%%f" ^| sort') do echo %%m
It spawns a cmd which reads the mac list in the inner for, prefixes a random value and a slash to the mac and sorts the list. Then this list is splitted in the outter for using the slash as delimiter and printing the mac address.

Resources