CMD.EXE – ‘LOOPS’ – PART III
ABSTRACT: This article delves into FOR loops in all their weirdness.
The loop playground
CMD.exe provides FOR loops that work in a rather awkward manner but they are quite powerful indeed. In this section, let us see how can we understand the for loop switches and their purposes, using a problem description and solution approach.
FOR loop remind us of the pleasures of iterations and what can be done by repeating an operation a certain number of times. Adding a condition to the scenario means we can make better use of logical operations.
FOR loops in Windows cmd.exe help us to do line-based parsing quite effectively. Further, the feature that enables us to parse the output in memory of executed commands, including wmic and piping of additional cmd commands, makes the process more facilitated. The use of the IF command adds a flavour of programming-like decision making that gives a limited albeit flexible enough range of options.
As you will see it in the examples below, there are many ways to do the same and similar things, while the same features can be used to do quite different things. It’s kind of a dualism, I guess, but I don’t really know.
IF ?? (BE) ELSE (!BE)
Let’s have quick look of the IF command.
Type IF /? in the cmd prompt.
IF [NOT] ERRORLEVEL number command
IF [NOT] string1==string2 command
IF [NOT] EXIST filename command
The three modes are ERRORLEVEL number, string comparison, and file existence Boolean check.
The NOT is used to invert the logical condition. It is not enclosed in square brackets if it is to be used.
The command at the end of the statement is executed if the IF condition is satisfied. However, if the ELSE condition has to be satisfied, then the preceding command has to be enclosed in ().
Example from the man page:
IF EXIST filename. (del filename.) ELSE echo filename. Missing
This is quite self-explanatory.
A set of conditional processing definitions are provided as compare-operators.
IF [/I] string1 compare-op string2 command
These compare-operators are:
NEQ—not equal to
LEQ—less than or equal to
GEQ—greater than or equal to
The comparisons have a dual nature. The string is the default comparison type. However, if both strings are numbers, they are subjected to a numeric comparison.
Open cmd.exe by pressing WINDOWS_KEY+R to get the run dialog, type cmd and press ENTER.
Type for /? in the prompt to get the page paused list of the FOR help description (without the use of the more command piping).
The general layout of this command is as follows:
FOR<switches D /R /F /L> <context-based switch options> %variable
in (set/file/commands) do <@commands>
The simple version of the above would be:
FOR <look IN-to a dataset > DO <something>.
The dataset is switch-sensitive.
/D enumerate the directories (not files) and feeds that as a data set.
/R recursively traverses the directory tree and feeds that tree (folder and files) as a data set.
/F parses the memory / file and extracts each line (CR+LF/No blank lines) and takes further parameters to work with each line (tokens/delimiters/line skips). Each parsed line is fed as a dataset. Memory is used as the buffer for the command run and its output within the brackets. Regular commands from cmd and piped commands can be used to generate the dataset which will be in turn parsed line by line from memory and processed as programmed.
/L generates numbers as in a traditional numeric loop and feeds that number list as a dataset.
The main keywords in this line are “FOR,” “IN,” and “DO.”
All the commands are case-insensitive in cmd and thus FOR /F is equivalent to for /f and fOr /F.
We will discuss the context-based switch options that are especially required FOR /F option while reading command output from memory or a text file.
A set of attributes can be extracted from folders or files when used in combination with the FOR /F mode. The kinds of properties that can be queried off are:
%~I expands %I removing any surrounding quotes (“)
%~fI expands %I to a fully qualified path name
%~dI expands %I to a drive letter only
%~pI expands %I to a path only
%~nI expands %I to a file name only
%~xI expands %I to a file extension only
%~sI expanded path contains short names only
%~aI expands %I to file attributes of file
%~tI expands %I to date/time of file
%~zI expands %I to size of file
%~$PATH:I searches the directories listed in the PATH
And they can also be compounded to give:
%~dpI expands %I to a drive letter and path only
%~nxI expands %I to a file name and extension only
%~fsI expands %I to a full path name with short names only
%~dp$PATH:I searches the directories listed in the PATH environment variable for %I and expands to the drive letter and path of the first one found.
%~ftzaI expands %I to a DIR-like output line
TIP: As you will have to construct statements, it’s good to think of their execution path as linear, regardless of the kernel mode context switching and scheduling mechanism that give us the illusion of simultaneous execution. Innermost commands are run first and the execution path starts from the left side of the statement and travels to the right side.
Learn loop in 24 statements
PROBLEM 1: List the sizes and names of all subfolder in the target directory (one for C:TEST and the current directory).
ANS:: CASE 1- for /D %i in (C:TEST*.*) do @echo %~zi %i
CASE 2-for /D %i in (*.*) do @echo %~zi %i
PROBLEM 2: Iterate from 2 to 20 using a FOR loop and display it in the console.
ANS:: for /L %i in (2,1,20) do @echo %i
PROBLEM 3: You need to copy all .m4v extension video files from a repository folder TEST (including subfolders) to a dummy folder BIN in the root directory. Use a for loop to do it.
ANS:: for /R “C:TEST” %i in (*.m4v) do @xcopy “%i” C:BIN
FOR /R sets the operating mode to directory recursive mode, which is what we need.
The format for this command would require the source directory to be specified immediately after the switch within double quotes.
All FOR loop statements require the use of a variable that is alphabetical in sequence. For most examples, as in loop counter variables during programming, the convention is to use the letter “i.”
If more variables are needed, then we continue with the next letters in sequence. If we had started from “a,” the next variable would have to be “b,” except in special cases that are implicitly divided into several variables, such as the use of tokens later on.
All variables in every FOR statement have to be prefixed with a modulus sign ‘%’.
The next compulsory key word is “IN” in every variation of the FOR command.
The parentheses are mandatory and hold the “thing” to iterate/parse into. Any sort of output to be parsed, no matter how complex or long, has to be contained in these two brackets for a particular FOR statement.
In this case, we pass the wildcard string containing the familiar * as the wildcard and the string to search in the filenames. What happens here is that the directory tree containing the folder names and the full filenames (path+filename+extension) is traversed and every entry attribute is compared to the extension “m4v” and all the valid entries are collated into the final list.
All of this is accumulated in the memory. Only after the completion of the bracketed commands does the “do” part of the for loop start.
The @ sign prefixing negates the separate output display of the command run itself in stdout and snubs that into the background, giving only the desired command(s) final output.
Finally, the action command itself is xcopy %i C:BIN. The %i acts as a placeholder for the full path of the files containing the extension m4v and thus facilitating xcopy to do its job (copy from source to destination).
ANS:: for /R “C:TEST” %i in (*.m4v) do @echo %i; >> SAVE.txt
This statement saves the list of m4v files in recursive mode to a text file in appending mode while adding a semicolon on every line. The semicolon acts as a delimiter when reading back this text file list from the filesystem, using the FOR /f switch. This is one way to do it. The other will be discussed in the length of the article.
PROBLEM 4: Read a text file and delete all the files contained in that text file. One file per line, the file is delimited using ; to denote the end of each line.
ANS:: for /F “delims=;” %i in (SAVE.txt) do @del “%i”
A thing to remember about FOR /F mode is that it does not
read blank lines as lines. This is unlike the behaviour of find /c /v “”, where even blank lines and carriage returns are counted as lines. This makes counting very accurate using FOR loops.
The statement below uses the FOR /F switch to read from file/memory from the file above. In this case the current directory is C:TEST and hence just passing the filename works. In all FOR statements shown here, if the full path is not given, assume that it is in the current directory. If not present you would get an error.
Notice that the “delims” parameter is passed in double quotes, while assigning the value of the delimiter “;“. This is in accordance with the list and syntax of the FOR /F mode. Once learned, it’s really quite simple.
Finally, the activity to be done is to delete all such files (m4v ext) by reading the ready made list.
The reasons for using delimiters is that the FOR /F command on its own does not parse spaces in filesystem paths; thus “C:TEST” would be parsed just fine; however ‘C:DIN OF BIN’ would be parsed as C:DIN essentially and would thus feed the name of this non-existent folder to the command, producing an error.
PROBLEM 5: Count the number of files in a target directory. Use a repository file that saves each video path and update the number of files/lines in that repository file. Display the number count only in a static position in the leftmost corner of the console.
ANS:: for /R “C:TEST” %i in (*.m4v) do @echo %i >> TMP.txt | cls | type TMP.txt | find /c /v “”
The above statement parses the folder tree and saves the video files to TMP.txt in the current directory, clears the screen intermittently, and displays the count of lines in the resulting file in real time. It is displayed in the top left and does not keep appending. It’s a little blurry but still gives a position static display of real time data. Better options to do this are quite limited, though there might be a more effective work-around.
PROBLEM 6: Continuously delete all exe files in a malware dump directory. Use a timer after each deletion.
ANS:: for /R “C:MALWAREDUMPSITE” %i in (*.exe) do @del “%i” & ping -n 1 localhost > nul
This statement keeps deleting executable files in a target folder. Here notice that del is passed to the file path strings within double quotes, which effectively processes the blank spaces in file paths in the /R mode. This is different from the use of delimiters in for /F mode. The other method is using tokens, as we will see soon.
The ping trick is quite well-known now using the ping count command. The time used to do each ping is not quite precise (exact) though accurate (average is stable) enough for our purposes. Here we are pinging localhost and feeding the output to nul (sort of a console output recycle bin). Thus we don’t see the pinging text cluttering the actual desired output.
A few other commands like netstat also allow the use of timers; however, once started these other commands actually keep running and thus don’t allow the continuation of the command daisy chain. Ping is the only inbuilt command that actually can be used as a timer workaround.
PROBLEM 7: Find all exe and dll files in a target directory from a target subdirectory tree using FINDSTR regex strings.
ANS:: for /R “C:TEST” %i in (*.*) do @echo %i | findstr [.][ed][xl][el]
This time we are searching for all/any files recursively in the target folder and parsing the whole output in memory, using findstr regex to filter exe and dll files only. This method is good for mass extraction in minimal lines of code.
PROBLEM 8: Continuously list the services running.
ANS:: for /L %i in (1,0,10) do @tasklist /svc
for /L is used where the 1,0,10 denotes the starting counter value, the step and the terminating value. The step being 0, this is an immediate infinite loop. Basically tasklist /svc will be run infinitely. This would be useful for giving a real time monitoring view; in this case, the output can be further gleaned to make it like an alarm when a new service (known name strings) is started, without listing the usual benign ones, by piping FIND or using FC to diff with a text list of known strings and setting a conditional display using the IF command.
You could try this yourself.
PROBLEM 8: Using FOR loop and taskkill filters, continuously kill notepad.exe and use a timer within each successive operation.
ANS:: for /L %i in (1,0,1) do @taskkill /fi “imagename eq notepad.exe” & ping -n 3 localhost > null
Here notepad.exe is continuously killed while keeping the display refreshed to visible speeds in an infinite loop. Press CTRL+C to break any such loop.
PROBLEM 9: Read a file from the disk and delimit the contents of each line with a “;”. Thereafter display the SIZE and NAME of each file from the playlist. You could use a buffer to accumulate the delimited list of file paths and later delete that after the operation is done.
ANS:: (for /f “tokens=1*” %i in (‘type “C:OWNZMDEIAMUZfull.m3u”‘) do @echo %i %j; >> C:ownzt.txt) & (for /f “delims=;” %i in (C:ownzt.txt) do @echo %~zi %~ni) & (del C:ownzt.txt)
Here an m3u file is taken (music playlist-Winamp), wherein the full file paths are listed to a temp file t.txt after delimiting with ‘;” (read the @echo statement properly) and on successful completion of the above a second FOR loop parses the newly created and delimited t.txt file is parsed and the file attributes are processed from just the file paths and displayed on the console stdout. After the successful completion of the second FOR loop, the file t.txt is deleted
This is a feature provided in for /f loops. The use of %~zi will
enumerate the file sizes in bytes for each file read in that variable. %~ni will enumerate the names of the files only sans the path or extension.
Notice the use of a new parameter “tokens=1*”. The use of the tokens parameter keyword denotes the indexing of spaced text within each line. It simulates the use of columns, if that can be taken as an analogy. Think of a line containing spaces or tabs as a set of fields. Now each field can be tokenized starting from the number 1 (not 0).
C:DIN TO BIN
can be tokenized as
The tokens parameter takes this index list as a wildcard as well, using the asterisk character.
“tokens=1,2*” for the above instance would mean:
“tokens=1*” would mean
Thus, the index count is always incremental and ends with a star if all the rest of the line regardless of the space or tabbed contents. This brings us to our third method of reading a line, using the tokens parameter keyword. Remember the “remaining part” of the “*” index includes anything after the second index is filled. But this part has to be accessed using a separate variable and, in keeping with the use of alphabetical series, the next logical letter has to be used in the same fashion as the earlier variables.
PROBLEM 10: Display the SIZE, NAME, and EXTENSION of the subdirectory tree of C:TEST (folders and files).
ANS:: for /R C:TEST %i in (*.*) do @echo %~zi %~ni %~xi
This statement displays the size, name, and extension of each file in the target directory.
PROBLEM 11: Read entire lines (even containing spaces) to successfully enable the display of each line read from a file c.txt in the current directory.
ANS:: for /F “tokens=1*” %i in (c.txt) do @echo %i %j
Here we learn about the tokens keyword in its simplest variation to read a complete line from a text file and display it to the stdout.
PROBLEM 12 : Copy all files having extension .rns from C:OWNZ subdirectory tree to C:OWNZ.
ANS:: for /f “tokens=1,2*” %i in (‘dir /b /s C:ownz*.rns’) do @xcopy “%i %j %k” C:ownz
Here, we feed the variables %i, %j, and logically %k within double quotes to xcopy. The tokens keyword indicates that the first two spaces will be tokenized and thereafter the rest of the lines will be appended in the last variable holding the asterisk value.
Notice how cmd.exe commands are passed within single quotes.
VARIATION: Use a single token instead of 2.
for /f “tokens=1*” %i in (‘dir /b /s C:ownz*.rns’) do @echo %i %j
There are quite a few purposes for this tokenizing.
- One that comes to mind is rearranging the output columns to an order other than the default sequence.
- Another benefit would be breaking down the text output per line for more effective parsing.
- Still another benefit is extracting the exact text location.
Undoubtedly syntax like “wmic process get processid” is a lot simpler to use, though it would be indeed cumbersome to work on text files and other formats that might require custom parsing logic. Here FOR loops definitely keep the leverage. In fact, though data retrieval in wmic is quick and intuitive, the data formatting and parsing/reordering options are rather limited, unless standard issue XML and HTML is exactly what you need and thereafter only third-party tools would enable salvaging the data from it.
TIP: When using tokens to parse a full line regardless of the number of spaces and returns, just use “token=1*”. The first space will be the tokenizing one and the rest of the line is taken up by the second variable.
PROBLEM 13: Search for all text files in C:ownzsoft and display the file paths. Further, show the count of lines per file in the display.
ANS:: for /f “tokens=1*” %i in (‘dir /b /s C:ownzsoft*.txt’) do @echo %i %j & (@echo %i %j >> C:ownztmp.txt | @type C:ownztmp.txt | find /c /v “”)
VARIATION: Delete the tmp file from the operation and, when the entire process is complete, display the string, “TMP Deleted”.
ANS:: (for /f “tokens=1*” %i in (‘dir /b /s C:ownzsoft*.txt’) do @echo %i %j & (@echo %i %j >> C:ownztmp.txt | @type C:ownztmp.txt | find /c /v “”)) & (@del C:ownztmp.txt & @echo File TMP Deleted)
The above statement parses the lines from all text files in the directory specified, counts the number of lines in each file, appends the count to the end of each file, and deletes the temporary file after the operation. You could open any file and see the count recorded at the bottom of the file.
PROBLEM 14: Search for all exe and dll files in C:ownzsoft and save them to a file exelist.txt in the current directory for later retrieval. Count the number of lines in the resulting file, display the number, and at the same time append that number to the end of the exelist.txt file. After that, start notepad with exelist.txt loaded in it. Use a single line statement to do all the above.
ANS:: (for /f “tokens=1*” %i in (‘”dir /b /s C:ownzsoft*.exe & dir /b /s C:ownz
soft*.dll”‘) do @echo %i %j >> C:ownzexelist.txt) & (@type C:ownzexelist.txt | find /c /v “”) & (@type C:ownzexelist.txt | find /c /v “” >> C:ownzexelist.txt) & (notepad C:ownzexelist.txt)
Here, all exe and dll extension executable files are accumulated in memory using a linear approach of command execution. First, dir /b /s C:ownzsoft*.exe runs. Now the memory contains a per line list of the exe files.
Next, dir /b /s C:ownzsoft*.dll runs. Now, the memory contains the original list of exe files with the dll file list appended directly after that. This memory list is dumped to the file system for later recovery using echo with two variables to a file exelist.txt. Note the use of parentheses. Each FOR loop will complete linearly from left to right. Further this sequence will only continue if the last one was successful. “&” provides the fail-safe conditioning logic so that errors are not propagated to the next one in the chain.
Thereafter display the count to the screen (stdout) and append the total count of executable files to the end of the exelist.txt file.
After all the preceding operations are done, open notepad with exelist.txt opened in it, saving the manual labor of starting notepad to check for the operation.
PROBLEM 15: Search for all exe files recursively from a C:ownzsoft and display the file size and the full path of each file.
ANS:: for /f “tokens=1*” %i in (‘”dir /b /s C:ownzsoft*.exe”‘) do @echo %~zi %i %j
Here we get the file size and the file path in a simple two-column display from the dir /b command output. First dir runs its course and then we extract the path names and the sizes of each file and change the output format to our requirement.
PROBLEM 16: Search through a directory for all text files and display the full path only if the filename equals to exelist (don’t check for extension).
ANS:: for /f “tokens=1*” %i in (‘dir /b /s C:ownz*.txt’) do @if %~ni==exelist (echo %i)
This displays the full path of exelist from the memory list of text files searched recursively. The use of the “if” command in cmd.exe is demonstrated. If the name equals exelist the file path is sent to the echo command. It sure looks like a thorough way to search for a file location.
PROBLEM 17: Search for all EXE files recursively in a target directory and display the file path only if the size of the file is greater than or equal to 600,000 bytes.
ANS:: for /f “tokens=1*” %i in (‘dir /b /s C:ownz*.exe’) do @if %~zi GEQ 600000 (echo %i)
Here we check for file size greater than or equal to ~600KB and only then display the full file paths to the console. Notice how the IF statement takes the IF conditional action in parentheses. The else keyword would follow likewise.
PROBLEM 18: Parse two separate text files and display their contents. Use the tokens parameter to include full lines containing spaces with minimal tokenizing and not delimiters.
ANS:: for /f “tokens=1*” %i in (C:ownzn.txt,C:ownzn2.txt) do @echo %i
PROBLEM 19: Parse a list of mp3 files in an m3u playlist file and find mp3’s whose sizes are >= 10000 bytes. Display them in a two-column format – SIZE NAME. Name would be without file path and extension.
ANS:: (for /f “tokens=1*” %i in (‘type “C:OWNZMDEIAMUZ full.m3u”‘) do @echo %i %j; >> C:ownzt.txt) & (for /f “delims=;” %i in (C:ownzt.txt) do @if %~zi GEQ 10000 (echo %~zi %~ni)) & (del C:ownzt.txt)
In this statement, we are searching for all mp3 files in the m3u playlist that have sizes greater than equal to 10KB and we display the size column first and the names of each file only (without path and extension.) We use delimiters and a temporary buffer file, which is deleted afterward.
PROBLEM 20: Reorder the output of the tasklist command and skip the first four lines (the divider line, the headers line, system idle process and system). Reverse order the column display.
ANS:: for /f “tokens=1,2,3* skip=5” %i in (‘tasklist’) do @echo %l %j %k %i
Here we learn about the skip parameter keyword for the FOR /F loop mode. Skip essentially skips the first n number of lines from the data set (file/memory) to be processed. The index starts from 1. Essentially the display runs from the next line after the skipped lines. Therefore you just feed the line number you want to start from starting from index 1. This could well have used “show from” or “display offset.” Thus we feed the number 5, as the 4 lines have to be skipped. If we give 4, the system process is still displayed, in case you were wondering, giving a bit of a confused logic because the fourth line is not skipped but rather excluded from the skipping. It’s better to think it in terms of the display offset.
Also the column is reordered. In this case the display is reversed as the last column is the first and vice versa.
The key to successful tokenizing is to get an idea of what field you are looking for in that line and how many spaces have elapsed from the start of the line. Once you know the position of its occurrence, tokenizing becomes easy.
PROBLEM 21: Filter out chrome.exe from the tasklist default output and skip the first four lines. Use the IF command in the do segment, instead of FIND in the set segment.
ANS:: for /f “tokens=1,2,3* skip=5” %i in (‘tasklist’) do @if %i==chrome.exe (echo %l %j %k %i)
Here the output of tasklist is processed. The headers are removed and the idle and dummy process names like system are removed. Further, the string of chrome.exe will occur if the %i variable is read as the process name is the first column, and if chrome.exe is found then reorder the final column display of tasklist.
PROBLEM 22: Tokenize the wmic process output for chrome.exe, get the name, processid, and threadcount. Further reorder the output of wmic to THREADCOUNT, PROCESSID, and NAME (reverse the order).
Thereafter change the current directory to “C:OWNZ”. Use a nested FOR loop in the next loop sequence. Read the directory of the current directory recursively for all exe and dll files. Pass the output of this FOR /R loop within a FOR /F and search for the resulting list in memory for file sizes greater than 10000 bytes and display the final result as SIZE FULLPATH format.
Change back the current directory to C:TEST.
ANS:: for /f “tokens=1,2,3* skip=1″ %i in (‘”wmic process where name=”chrome.exe” get threadcount,name, processid”‘) do @echo %k %j %i (cd C:ownz) & (for /f “tokens=1*” %a in (‘”for /r %i in (*.exe *.dll) do @echo “%i””‘) do @if %~za GEQ 10000 (echo %~za %a %b)) & (cd C:TEST)
Here wmic is used to get the details of chrome.exe and the threadcount, name, and processid are extracted. Notice that changing the order of the fields will not change the order of the final output with just wmic. Thus writing get processid, name, threadcount will get the same order as the default. So we parse the final output and tokenize it, and then reorder the output to our preference by simply exchanging the sequence of the token variable placeholders.
Notice how the entire wmic command is kept in single quotes (for any command) and then enclosed within double quotes for containing the complete command.
PROBLEM 23: Enumerate the owners who have started a process, use tasklist –v. Extract only the owner column and the process name. Use two tokens and one wildcard for this. Check if owner is VSRM and display only the ones who aren’t. Display should be in PROCESS_NAME OWNER column format. The output should be sorted according to the process name.
ANS:: (for /f “tokens=1,8* skip=1” %i in (‘tasklist -v’) do @if NOT %j == VSRMVSRM (@echo %i %j)) | sort
Here, we try to sort a list of processes and their extracted owners that are not the current user account. This helps flag unwanted accounts or hidden accounts that have started a process. Notice how you would use the first and the eight columns directly to pinpoint the location in the text output in memory.
VARIATION 1: Sort and decorate the output format. Use a few more tokens and list a few more fields.
ANS:: (for /f “tokens=1,2,8* skip=5” %i in (‘tasklist -v’) do @if NOT %k == VSRMVSRM (@echo %j — %i %k)) | sort
Here we redecorate the output by using OwnerName – ProcessName two-column format.
VARIATION 2: List processes containing ntdll.dll and display just the PID and the PROCESSNAME from the default output (which is quite large and displays the full list of loaded dlls in each process; further the columns are formatted in separate segments).
ANS:: (for /f “tokens=1,2,3*” %i in (‘”tasklist /m /fi “modules eq ntdll.dll” | find /i “exe””‘) do @echo %j %i)
In this case we want the output of tasklist to give us the list of processes containing the ntdll.dll but not the whole collated list. Rather we simplify the list by using find /I “exe” (I switch for case insensitive) to filter out the process names containing that dll and echo the line containing the PID and the NAME.
PROBLEM 24: A few commands are typed and saved in a file c.txt in the current directory. Execute the commands contained in the text file. This is not a batch file.
ANS:: for /F “tokens=1*” %i in (c.txt) do %i %j
For simple non-piped cmd.exe commands, simply removing the @ sign and writing the command after the do statement will execute that command from a file. Each individual command has to be on its own line for this command to work. This is not to be a counterpart to .bat files, which are far more efficient, but to display the options you have if required. Basically, while parsing a list, you could make a condition to simply display all the lines on the console save one line if it contains the text “notepad” then start notepad.exe. Stuff like that, wherein the conditional processing will make it more useful. This certainly has the potential to be misused in a set of benign text files.
More complex commands such as nested and piped FOR loops can be also executed, but they will run in cmd /c or cmd /k modes and cancelling the operation becomes a pain at that point. Further, the use of CTRL +C will not work as expected and thus makes the effort not really conducive to regular console based batch operations from a text file. Just for kicks, try it yourself.
NOTE: For batch files, all the variables prefixed with % have to be changed to %%. Thereafter, simply double-clicking the .bat file should open a cmd window and carry out the instructions.
You have seen 24 instances of fooling around with FOR loops and have gained a better understanding of this little-used command in cmd.exe. It is much quicker for a variety of tasks instead of heading for the C/C++ compiler to make your own version of similar algorithms, or using a third-party package of sorts. Much of this embedded functionality is at your fingertips if you are inclined to explore more about it. We have also taken a good look at combining various other commands and features like IF/FINDSTR/WMIC/FOR LOOP attribute extensions/NESTED LOOPS/DAISY CHAINING using PARENTHESES and the AMPERSAND operator etc. We have also gained an understanding of how to extract and format data from textual data sources to our requirements–from memory and the file system which gives us a particular flexibility of options to work with. More of this is coming up in the next part.