For statements from entry to mastery
1. Preface
In batch processing, for is the most powerful command statement. Its appearance makes it possible to parse text content, traverse file paths, increment/decrease value and other operations; combined with process control statements such as if, call, goto, etc., it can realize complex automated and intelligent operations of scripts; the rational use of for statements can also greatly simplify the code, eliminating the pain of writing a large number of repetitive statements. Whether you can use for statements proficiently has become the most important criterion for measuring a person's batch processing level.
In this series of tutorials, I will lead you into the magical door of for statements through examples that appear frequently in actual applications, and step by step into the magical palace of for statements, so that in actual applications, everyone can independently write concise and efficient code and gallop freely in the batch processing world.
Note: The following explanations are based on the operating system environment of Windows XP Pro SP3 in simplified Chinese
2. Basic usage of for statements
Just as the colorful colorful light is composed of three primary colors: red, green and blue, the most complex for statement also has its basic form, and its appearance is as follows:
In the cmd window:for %I in (command1) do command2
In batch file:for %%I in (command1) do command2
The reason why we need to distinguish between the two environments of cmd window and batch file is that in these two environments, although the behaviors shown by command statements are basically the same, they are still slightly different in details. The most obvious difference is: in the cmd window, the form variable I after for must be referenced using a single percent sign, that is, %I; while in the batch file, the reference form variable I must be referenced using a double percent sign, that is, %%I. For convenience, if not particularly emphasized, the following explanations take the batch file environment as an example.
Let's first look at what are the basic elements of the for statement:
1. For, in and do are keywords of the for statement, and they are indispensable;
2. %%I is a reference to a formal variable in a for statement. Even if it does not participate in the execution of the statement in the statement after do, it must appear;
3. After in, the brackets before do cannot be omitted;
4. Command1 represents a string or variable, and command2 represents a string, variable or command statement;
Now, you may already write a simple for statement, such as:
@echo off for %%I in () do echo %%I pause
Save as a batch file and execute it. You will see this information in the pop-up batch window:
[result1]
Please press any key to continue...
Soon, you will feel that this for statement is so simple that you can't feel its power at all: this for statement is no different from my direct use of echo statements!
Yes, demonstration code is always just a demonstration. Just like most high-level language textbooks, when guiding novices to learn, they basically tell everyone how to write a window that can display hello world!. From these demonstration codes, you can't see how practical they are. You just feel a little curious: Hey, a window actually pops up? After a moment, you will feel bored.
Well, in order to make everyone more interested in for, let’s first analyze some precautions for the for statement, and then let everyone take a look at more powerful for statement examples.
1. The form variable I of the for statement can be replaced with any one of 26 letters, and these letters will be case sensitive, that is, %%I and %%i will be considered not the same variable; the form variable I can also be replaced with other characters, but in order not to conflict with the 10 formal variables %0~%9 in batch processing, please do not replace %%I with any one of %%0~%9 at will;
2. The string or variable represented by command1 between in and do can be one or multiple. Each string or variable, we call it an element, and each element is separated by a space bar, a jump bar, a comma, a semicolon or an equal sign;
3. The for statement extracts each element in command1 in sequence, assigns its value to the formal variable I, and takes it to command2 after do to participate in the execution of the command; and only one element is extracted at a time, and then executes the command statement after do, regardless of whether this element is brought to command2 to participate in the execution of command2; after executing the statement after do, extract the next element in command1 and executes command2 again, and loops in this way, until all elements in command1 have been extracted, the for statement will not declare the execution end;
Among them, the third point is the most critical. It describes the execution process of the for statement and is the essence of the for statement. Everyone must remember this point in order to deeply understand the more complex for process.
With the above basis, let's take a look at another example. This example modifies some of the content of code1, and the result will be very different:
@echo off for %%I in (bbs,bathome,cn) do echo %%I pause
Compared with the execution result of code1, the execution result of code2 has changed as follows:
1. The display result is divided into 3 lines (not counting the last line of Chinese prompts);
2. Each line is divided from a comma;
If the dot in this string is replaced with a space, a jump or an equal sign, the execution result will be exactly the same as the execution result of code2.
Now, let’s analyze the execution process of the for statement in code2 code:
First, the for statement uses a comma as the separator to divide the string bbs, batheme, cn into three elements: bbs, batheme and cn, which determines that the statement after do will be executed 3 times;
Then, the first execution process is as follows: first take the bbs string as the value of the formal variable I and bring it into the statement after do for execution, that is, execute the echo %%I statement. The I value at this time is bbs. Therefore, the result of the first execution will display the bbs string on the screen; the second execution is the same as the first execution process, except that the value of I has been replaced by the second element in command1, that is, the bathe string; in this loop, after the third echo execution is completed, the entire for statement will be considered to be executed, and at this time, the next statement, that is, the pause command, will be executed.
In fact, this example is only a little more varied than the previous example, and it is a little more interesting: the execution result of a for statement is actually divided into 3 lines!
In order to let everyone see the real power of for, I racked my brains and read countless posts, but I couldn't figure it out. In desperation, I had to reveal a piece of code that had been blocked at the bottom of the box for many years: detecting which partitions are currently in the hard disk^_^
@echo off set str=c d e f g h i j k l m n o p q r s t u v w x y z echo The current hard disk partition has: for %%i in (%str%) do if exist %%i: echo %%i: pause
This code can detect which partitions are present in the hard disk, including the partitions of the USB drive and the mobile hard disk. However, when there is a disk in the optical drive, it will also be listed. This is a shortcoming of this code. In the future explanation, I will tell you how to eliminate this flaw. Please pay attention to the subsequent chapters of this series.
Advanced applications:
Want to know what files are in the current directory? Please use the following code:
@echo off for %%i in (*.*) do echo "%%i" pause
Want to list all text files in the current directory? Please use the following code:
@echo off for %%i in (*.txt) do echo "%%i" pause
Want to list text files that only use two characters as file names? Please use the following code:
@echo off for %%i in (??.txt) do echo "%%i" pause
Off topic:
1. The simplest way to list various files in the current directory is to use the dir command. However, from the above code, you can deepen your understanding of the execution process of the for statement (using wildcard characters * and ?);
2. Note: The above code cannot list files containing hidden or system attributes;
Exercise: Create and use the for statement.
For more comprehensive exercises, please see this post: For sentences from introductory to mastering supporting exercises
In order to cooperate with the explanation of for statements from introductory to proficient, this topic was specially opened, and a batch of targeted exercises are given here for each part of the for tutorial, so that novices can quickly master the usage of for statements. This exercise does not pursue quantity, but provides targeted questions for various technical details of the for statement, striving to cover various technical points of the for statement.
This exercise only mentions the function of summarizing the main points. To write more efficient and refined code, you also need to carefully ponder it in practical applications and try to solve multiple solutions for one problem.
For the convenience of others' reference, please specify the question number before each code in the reply.
A: The basic usage part of the for statement (please use a simple for statement to answer the questions in this section):
A1: Assume that set str=I love,bbs;bathome=net, please display the five strings: I, love, bbs, bathe and net respectively. Purpose: Understand which symbols are used as element separators for the for statement.
A2: The conditions are the same as A1, and only the string net is displayed at the end. Purpose: Understand the execution process of the for statement.
3. Text analysis shows its power: for /f usage details
Preface
for /f is a very powerful guy.
If the for statement is the most powerful statement in batch processing, then for /f is the essence of the essence.
The power of for /f is closely related to its numerous switches. Because there are many switches, the usage is complicated. This chapter will be divided into several subsections to introduce the powerful for /f statements one by one.
(I) Born to parse text: the basic usage of for /f
All objects, whether files, forms, or controls, are nothing more than text information such as "c:\" and "CWnd" in the eyes of all non-machine languages; while all objects, such as a configuration information in the ini file, a key value in the registry, a record in the database... can only be recognized and manipulated by the code. It can be said that a large part of programming is racking your brains to find ways to extract this text information.
Extracting text information is the best thing for /f: reading the file content; extracting a few lines of characters; intercepting a certain character fragment; dividing, disrupting, and mixing the extracted content... As long as you can think of, for /f will try every means to help you, because for /f is designed specifically for parsing text.
Let’s take a look at an example first.
If there is a text file, the content is as follows:
[txt1]
The goal of the forum is: not seeking the largest, but seeking the best, and being the most practical batch forum.
Forum address:.
Here is: a blessed land for novices to advance, and a paradise for masters to discuss swordsmanship.
Then, save the following code as and run it in the same directory, and the content will be displayed as it is on the screen:
@echo off for /f %%i in () do echo %%i pause
This code mainly allows you to establish the concept: if you read the content of a text file, please use the for /f statement!
Advanced topic: Does the for /f statement display the entire one at a time?
In this code, although the execution result is to display everything in it, it seems that the for /f statement displays the entire screen at one time, but this is not the case.
Regardless of the changes made by the for statement, its execution still follows the basic for process: each element is processed in sequence until all elements are processed. However, in the for /f statement, the element here refers to each line in the file, that is, the for /f statement processes text files in behavior units. This is an extremely important rule, and it was emphasized in the previous chapter. I hope that in the next learning process, you can always keep this principle in mind, so many problems will be solved. The following is a demonstration code to verify this statement (adding the &pause statement on the basis of [code4]):
@echo off for /f %%i in () do echo %%i&pause pause
(II) A powerful tool for dividing strings: delims=
Maybe you are disdainful of the code [code4]: Isn’t it just displaying the content? It doesn't seem to be of much use.
OK, let's play magic.
Or the text [txt1] is changed to [code4]:
@echo off for /f "delims=," %%i in () do echo %%i pause
Run again, have you seen any changes? !
[result2]
The goal of the forum is: not to seek maximum
Forum address:.
Here is: a blessed place for beginners to advance
Please press any key to continue...
As a result, you are surprised to find that everything after the first comma of each line is gone (if there is a line that does not exist, it will be left as it is), that is, you successfully extracted everything before the first comma of each line!
Just imagine, what's the use of this code?
If someone gives you a list of software and each line is in the format of "English software name (comma) Chinese software name", but you only want to keep the English name, how useful this code will be! Suppose there is such an IP file. The first column is the IP address in the number format, and the second column is the specific spatial address. The columns are separated by commas. You want to extract the IP in the number format. Haha, if I don’t say it, you know what to do, right?
If the text content is not separated by commas, but by other symbols, then replace the comma of "delims=," with the corresponding symbol.
Here we introduce a new switch: "delims=,", which means: use a comma as the delimiter of the string being processed.
In batch processing, the method of specifying delimiting symbols is to add a switch shaped like "delims=symbol list", so that each line of string being processed will be separated by the symbols listed in the symbol list.
It should be noted that if the "delims=symbol list" switch is not specified, then the for /f statement uses the space bar or the jump bar as the delimiting symbol by default. Please change the punctuation marks at different positions in [txt1] to spaces or jumps, and then run [code4] to try.
Advanced topic: What should I do if I want to specify more than one symbol?
In the above explanation, I mentioned the method of specifying delimiting symbols: add a switch with the shape of "delims=symbol list". I don't know if you noticed it, my statement is "symbol list" rather than "symbol", which is very particular, because you can specify multiple delimiting symbols at once!
Let’s take [txt1] as an example and remodel [code6]:
@echo off for /f "delims=.," %%i in () do echo %%i pause
The results show:
[result3]
The goal of the forum is: not to seek maximum
Forum address: bbs
Here is: a blessed place for beginners to advance
Please press any key to continue...
In this way, the content before the first dot or the first comma is extracted.
The execution process of [code7] is: read the content line by line, and divide the content of each line with dots and commas (there is no dots and commas, and it will no longer be divided. For the convenience of description, we use string fragments divided by dots or commas, called sections), and then, for /f will extract the content of the first section as the final result and display it on the screen. It should be noted that here, the strings of all lines are divided into more than two sections, but the code of [code7] will only extract the contents of the first section string, because the for /f statement only extracts the strings of the first section by default.
(III) Fixed-point extraction: tokens=
When explaining delims= in the previous section, I repeatedly emphasized that for /f can only extract the content of the first section by default. Now let’s think about a question: What if the content I want to extract is not in the first section?
This time, it's time to make a move.
tokens= is usually followed by numbers, such as tokens=2, and can also be followed by multiple numbers, but each number is separated by commas, such as tokens=3,5,8. Their meanings are: extracting the 2nd string, extracting the 3rd, 5th and 8th strings respectively. Note that the "section" mentioned here is divided by the switch delims=, and its content is not static.
Let’s take a look at an example:
[txt2]
There is a shortcoming and a strength in a short time. Learning batch processing well will not discuss it. Considering problems will be complicated and solving problems will be simplified.
For the text [txt2], assuming they are saved in a file, if I want to extract the sentence "Learn batch processing well and have no negotiation", how should I write the code?
We will find that if we use commas as the singling symbol, we can just turn "learning batch processing without negotiation" into a separate "section". Combined with the explanation of the previous section, we know that the "delims=," switch is indispensable, and the content to be extracted is in the 3rd section of the comma segmentation, then the number after tokens= should be 3. The final code is as follows:
@echo off for /f "delims=, tokens=3" %%i in () do echo %%i pause
What if we want to extract more than one "section" now, but multiple ones, then what should we do? For example, if you want to extract the 2nd and 5th section strings divided by commas, is it written like this?
@echo off for /f "delims=, tokens=2,5" %%i in () do echo %%i pause
After running the batch, I found that the execution results only show the contents of Section 2.
It turns out that %%i after echo only receives the string represented by the first value 2 in tokens=2,5, while the string represented by the second value 5 cannot be displayed in the execution result because there is no variable to receive it.
So, how to receive content referred to by tokens= multiple values below?
The for /f statement makes the following provisions for this situation:
If tokens= is specified afterwards, if the formal variable is %%i, then the content referred to by the first number is received by the first formal variable %%i, the content referred to by the second digit is received by the second formal variable %%j, and the content referred to by the third digit is received by the third formal variable %%k... The content referred to by the Nth digit is received by the Nth formal variable, where the formal variable follows the order of letters, and what symbol the Nth formal variable is specifically determined by the first formal variable: if the first formal variable is %%i, then the second formal variable is %%j; if the first formal variable uses %%x, then the second formal variable is %%y.
Now looking back at [code9], you should know how to modify it to meet the requirements of the question, right? The modification results are as follows:
@echo off for /f "delims=, tokens=2,5" %%i in () do echo %%i %%j pause
If there is a requirement: display the content in [txt2], but the comma should be replaced with spaces, how to write the code?
Based on the above-mentioned content and a little thought, you may soon come up with the answer:
@echo off for /f "delims=, tokens=1,2,3,4,5" %%i in () do echo %%i %%j %%k %%l %%m pause
After writing, you may realize the question: If the number of "sections" to be extracted is not 5, but 10, or 20, or more, do I have to write from 1 to 10, 20 or more? Is there a simpler way to write it?
The answer is: if the content to be extracted is a continuous "section", then the continuous numbers can only write the minimum and maximum values, and connect them with a short horizontal line. For example, tokens=1,2,3,4,5 can be abbreviated as tokens=1-5.
You can also write this expression more complicatedly: tokens=1,2-5, tokens=1-3,4,5, tokens=1-4,5... Just write as you are convenient.
You may also see a weird way of writing:
@echo off for /f "delims=, tokens=1,*" %%i in () do echo %%i %%j pause
As a result, the first comma is gone, and it is replaced by a space symbol, and the rest remains unchanged.
The mystery lies in this asterisk.
tokens=The asterisk followed has the function of: the string is divided from left to right into the number of sections immediately after the value before *, and the rest of the string remains unchanged, and the whole is received by a variable represented by *.
Theoretical explanation is relatively boring, especially for the sake of rigor, many limited modifiers are used, which leads to long sentences and increases the difficulty of understanding. Let's explain it in combination with [code12].
The content of [txt2] is divided, and the slicing symbol is a comma. After the first section is divided, the slicing action will no longer continue, because in tokens=1,*, the asterisk is followed by the number 1; after the first section string is divided, the rest of the strings are not divided, and the whole is used as the second section string. In this way, [txt2] is divided into two sections, received by the variable %%i and the variable %%j respectively.
The above slicing methods can be used together. I don’t know if you can understand the meaning of the following code. If you can’t understand it, then run the code and then think about it repeatedly. You will definitely understand the content explained in this section more deeply:
@echo off for /f "delims=, tokens=1,3-4,*" %%i in () do echo %%i %%j %%k %%l pause
(IV) Skip irrelevant content and go straight to the topic: skip=n
Many times, useful information does not run through the text content, but is located in the line after the Nth line. In order to improve the efficiency of text processing, or not be disturbed by unnecessary information, for /f allows you to skip these useless lines and start processing directly from the N+1th line. At this time, you need to use the parameter skip=n, where n is a positive integer, indicating the number of lines to be skipped. For example:
@echo off for /f "skip=2" %%i in () do echo %%i pause
This code will skip the first two lines of content and display the information from line 3.
(V) Ignore lines starting with specified characters: eol=
In the cmd window, the relevant explanation is:
eol=c - Refers to the end of a line comment character(Just one) [/quote] [quote] FOR /F "eol=; tokens=2,3* delims=, " %i in () do @echo %i %j %k
Will analyze each line in , ignoring those lines starting with a semicolon...
The first explanation is nonsense and quite puzzling: What does the end of the line comment character mean? What's going on with "(just one)"? Based on the second explanation, we know that eol has the function of ignoring the specified row. However, are these two explanations contradictory: are they ignored lines that start with specified characters or lines that end with specified characters?
Practice is the only criterion for testing truth. It is better to use code to test the function of eol:
@echo off for /f "eol=;" %%i in () do echo %%i pause
As a result, those lines starting with semicolons are not displayed.
From this we can see that the second explanation is correct. The accurate meaning of eol= is: ignore lines that start with specified characters. The "end" of the first post is purely Microsoft's talk.
So, what is the explanation of "(just one)"?
Try this code:
@echo off for /f "eol=,;" %%i in () do echo %%i pause
At this time, there should be no error message on the screen. It can be seen that when specifying characters, you can only specify one - in many cases, I have a lot of complaints about such a design and can't do anything about it: Why can you only specify one instead of multiple? To ignore multiple, it has to be if and findstr and add a pipeline to filter multiple times, the efficiency is too low - basically all the functions that can be used are provided, but they cannot be better. Batch processing, why are your functions so weak?
I don't know if you have noticed that if there are lines starting with a semicolon in it, then these lines will disappear out of thin air in the execution result of code [code14].
It turns out that the for /f statement ignores the content of the line starting with a semicolon by default, just as it defaults to use the space bar or jump key as the slit character of the string.
Many times, we can make full use of this feature. For example, when designing a configuration file that is about to be read in for, you can add a semicolon at the beginning of the comment text. For example, when writing a virus file search code, you can read the list of virus files through the for statement. Then, the configuration file of virus file list.ini can be written like this:
;The following are common virus files, please see Kill the other^_^
;copyleft: No
If you want to cancel this default setting, the option is:
1. Specify another character for eol=;
2. Use the for /f "eol=" statement, that is, force the character to be empty, just like dealing with delims=.
(VI) How to decide which sentence pattern to use for /f? (Also talk about the use of usebackq)
for /f %%i in (…) do (…) statements have several deformation statements. The difference is that the content in the first brackets: some are enclosed in single quotes, some are enclosed in double quotes, and some are enclosed in no symbols. The specific format is:
1. for /f %%i in (file name) do (…)
2. for /f %%i in ('Command Statement') do (...)
3. for /f %%i in ("String") do (...)
Seeing this, I think many people may have begun to be confused: if you want to solve a specific problem, facing so many choices, how do you decide which one to use?
In fact, when I listed these statements above, there were already some prompts, and I don’t know if you have noticed it.
If you cannot understand the mystery for a while, that's fine, please listen to me one by one.
1. When you want to read the content in the text file, the first bracket is not wrapped with any symbols, and the first statement should be used; for example: if you want to display the content, then use for /f %%i in () do echo %%i;
2. When you read the contents of the execution result of the command statement, the command statement in the first bracket must be wrapped in single quotes, and the second statement should be used; for example: When you want to display a text file with the test string in the file name in the current directory, you should use a statement such as for /f %%i in ('dir /a-d /b *test*.txt') do echo %%i;
3. When you are dealing with a string, the content in the first bracket must be enclosed in double quotes, and the third statement should be used; for example: when you want to replace the dot in this string with a short horizontal line and display it, you can use a statement such as for /f "delims=. tokens=1-3" %%i in ("") do echo %%i-%%j-%%k.
Obviously, whether the first bracket needs to be wrapped in symbols and what kind of symbols is used depends on what type of object to be processed: if it is a file, there is no need to be wrapped; if it is a command statement, it is wrapped in single quotes; if it is a string, it is wrapped in double quotes.
Of course, this is not absolutely the case. If you think of the difficult special characters in batch processing, you will definitely be as big as a fight.
Perhaps a flash of inspiration in your mind has already come up with a very headache-inducing question: In the first statement, what should I do if the file name contains spaces or &?
As usual?
Try it with a file called test.
You quickly wrote the code, create a new file --> codeword --> save it as batch, which takes less than 1 minute:
@echo off for /f %%i in (test ) do echo %%i pause
You double-click the batch process excitedly, and after running, a shameful error message appears on the screen: the system cannot find the file test.
When you switch test to test& , something even weirder happened: the CMD window flashed before your eyes, and then disappeared gracefully.
You may feel that you have written some symbols in your code, so you carefully checked it again to confirm that there were no typos. Then, you double-clicked the batch again, and the problem was the same; you began to suspect that other programs might have an impact on it, so you closed the other windows and ran it again, and the problem was still the same; you ran it several times in a row without consent, but the same result was still.
How strange!
You slapped your thigh and suddenly remembered something: when the path contains special characters, you should use quotes to enclose the path. Yes, that's it!
But when you write the code out, you quickly get lost: for /f %%i in ("test ") do echo %%i, isn't this the format of the for /f command mentioned above? Batch processing will recognize the file name test as a string!
You boredly typed for /? in the CMD window, and hit Enter heavily, searching aimlessly in the help information, hoping to find something.
It really brought you something.
You saw a description like this:
usebackq -Specify that the new syntax has been used in the following classes:
Execute a quoted string as a command and a single
Quotation characters are literal string commands and are allowed in filenameset
Use double quotes to extend the file name.
However, after reading it through, you feel like you are falling into a fog in five miles, not knowing what you say.
Fortunately, there is an example below, with a simple explanation:
FOR /F "usebackq delims==" %i IN (`set`) DO @echo %i
The environment variable names in the current environment will be enumerated.
You carefully compared the difference in writing when using usebackq and not using usebackq, and you quickly found the answer: After usingbackq, if there is a command statement in the first bracket, then you should change the single quote ' to the back quotation mark` (the key below the esc key in the upper left corner of the keyboard is on the same key position as ~).
Looking back at the description of usebackq, carefully thinking about it and thinking it over and over again, you finally deciphered the secret of Tianji: usebackq is an enhanced parameter. After using this parameter, the writing method in the first bracket in the original for statement should be changed as follows: If the object in the first bracket is a command statement, the original single quote should be changed to the last quote. If the object in the first bracket is a string, the original double quote "to be changed to the single quote"; if the object in the first bracket is a file name, the original double quote should be included in the double quote".
Verify it and rewrite [code17] into the following code:
@echo off for /f "usebackq" %%i in ("test ") do echo %%i pause
Test passed!
At this moment, you may sigh to the sky: Shit, Microsoft's damn machine translation!
As for changing the space in the [code17] code to &, the CMD window will exit directly, because & is the connector of the compound statement. When CMD preprocesses, it will give priority to parsing the two parts of & before and after as two statements, rather than a complete for statement as everyone imagines, which causes serious syntax errors. Because it involves preprocessing mechanism issues, it does not belong to the content to be discussed in this section, so I will not explain it in detail here.
At this time, we will be surprised to find that there are as many as 6 sentence patterns in just one for statement:
1. for /f %%i in (file name) do (…)
2. for /f %%i in ('Command Statement') do (...)
3. for /f %%i in ("String") do (...)
4. for /f "usebackq" %%i in ("File name") do (...)
5. for /f "usebackq" %%i in (`Command Statement`) do (…)
6. for /f "usebackq" %%i in ('String') do (...)
Among them, 4, 5, 6 develop from 1, 2, and 3, and they have such correspondence: 1-->4, 2-->5, 3-->6.
Fortunately, the last three situations are not commonly used, so it is enough to firmly grasp the application of the first three sentence patterns. Otherwise, it is really a bit confusing to determine which sentence to choose among so many sentence patterns.
As for why for /f adds the usebacq parameter, I only found a reasonable explanation for the 4th statement: in order to be compatible with the spaces or & in the file name. I don’t understand why it still exists in the 5th and 6th sentences, and it’s up to you to slowly discover it.
(VII) Detailed explanation of variable delay
Variable delay plays a crucial role in the for statement. It is not only in the for statement, but also in other compound statements, it also works silently behind the scenes. In order to highlight its importance, the content of this section is published on a separate floor, hoping to attract everyone's attention.
For batch processing novices, the concept of "variable delay" may be unheard of, but it is like an invisible high wall standing on your way forward. You cannot feel its existence, but when you try to rush forward, it will bounce you back hard, making you unable to overcome and return without success. Once you find a way to cross it, you will find that in the for world, there is already a smooth road ahead, and your understanding of batch processing has risen to a new level.
For example, you wrote a code like this:
@echo off set num=0&&echo %num% pause
Your original intention is to assign a value to the variable num and then display this value. As a result, the display is not 0, but display: ECHO is in the off state.
The reason why it makes mistakes is because the guy "variable delay" is causing trouble.
Before explaining variable latency, we need to understand the execution process of batch processing, which will help us understand variable latency in depth.
What is the execution process of batch processing?
"From top down, execute one by one", I think everyone is familiar with this classic saying. When you are free, you can read it backwards, and it has a unique ancient charm^_^, but what I want to ask you is, have you really understood the meaning of this sentence deeply?
"From the top down" article has little to do with our explanation in this section. I will not talk about it for the time being. In the latter article, "execution by item" has a great relationship with variable delay, and it is the focus of our attention in this section.
Many people often think that a line of code is a statement, which equates "execution item by item" with "execution item by item", which is a big mistake.
Could it be that there is a mystery hidden in "executing one by one"?
That's exactly that.
"One by one" is not the same as "line by line". This "bar" means "a complete statement" and does not refer to "one line of code". In batch processing, whether it is a complete statement is not discussed in terms of the line, but depends on its scope of action.
What kind of statement is considered a "complete statement"?
1. In a compound statement, the entire compound statement is a complete statement, regardless of how many rows the compound statement occupies. Common compound statements include: for statements, if...else statements, statements connected with connectors &, || and &&, statements connected with pipeline symbols|, and statement blocks enclosed in brackets and composed of multiple statements;
2. In a non-compound statement, if the statement occupies a line of position, the line of code is a complete statement.
For example:
@echo off set num=0 for /f %%i in ('dir /a-d /b *.exe') do ( set /a num+=1 echo num The current value is %num% ) echo There are a total of the current directory %num% indivualexedocument dir /a-d /b *.txt|findstr "test">nul&&( echo Existing contains test Textbook of strings )||echo 不Existing contains test 字符串的文本document if exist ( echo exist document ) else echo 不exist document pause
There are 14 lines in the above code, but there are only 7 complete statements, and they are:
Article 1: The echo statement in line 1;
Article 2: Set statement on line 2;
Article 3: For compound statement on lines 3, 4, 5, and 6;
Article 4: The echo statement in line 7;
Article 5: Compound statements connected with && and || on lines 8, 9, and 10;
Article 6: If…else compound statement on lines 11, 12, 13;
Article 7: Pause statement on line 14.
Here, the reason why I spent so long to explain that a line of code is not necessarily a statement is because the execution characteristic of batch processing is "item-by-line" rather than "line-by-line". If this misunderstanding is clarified, we will better understand the preprocessing mechanism of batch processing.
During the process of "item-by-item execution", this batch interpreter will do some preprocessing work for each statement, which is the famous "preprocessing mechanism" in batch processing. The general situation of preprocessing is as follows: First, read a complete statement into memory (regardless of how many lines there are in this statement, they will be read together), and then identify which parts are command keywords, which are switches, parameters, and variable references... If the code syntax is incorrect, an error message is given or exit the batch environment; if it passes smoothly, then, all the referenced variables in the statement and the percentage sign pairs on both sides of the variable are replaced by the specific value of the variable when this statement is read into memory... After all the preprocessing work is completed, the batch will execute the original function of each command within each complete statement. That is to say, if the command statement contains variable references (variables and percent sign pairs immediately adjacent to it), and the value of a variable is changed during the execution of the command, even if this variable is used elsewhere inside the statement, they will not be replaced with the latest value, because when a statement is preprocessed, all variable references have been replaced with string constants, and the variable value is changed inside the compound statement and will not affect anywhere else inside the statement.
By the way, after running the code [code20], the screen will display on how many exe files are in the current directory, whether there is a text file containing the test string, and whether this file exists. What many people can't figure out is: if there is an exe file in the current directory, then how many exe files there are will be prompted on the screen "num is currently 0", instead of displaying 1 to N (N is the number of exe files).
Based on the above two examples, let’s analyze why the execution results of these two pieces of code are somewhat different from our expectations.
In [code19], set num=0&&echo %num% is a compound statement, which means: assign 0 to the variable num, and after success, the value of the variable num is displayed.
Although the value of the variable num is displayed only after the variable num is assigned successfully, because this is a compound statement, during preprocessing, the %num% after && can only be replaced by the specific value of the variable num assigned to the variable num by the statement before the set statement, and cannot be replaced by the value assigned to num by the set statement inside the compound statement and before &&. It can be seen that this num is not that num. However, before this compound statement, we did not assign a value to the variable num, so %num% after && is a null value, which is equivalent to only executing the echo command after &&. Therefore, the current status of the echo command will be displayed instead of the value of the variable num (although the value of the variable has been changed by the set statement).
In [code20], the meaning of the for statement is: list the exe files in the current directory. Every time an exe file is found, the value of the variable num is accumulated by 1, and the value of the variable num is displayed.
After reading the analysis of [code19], it is no longer so difficult to analyze [code20]: the code on lines 3, 4, and 5 together form a complete for statement, and the current value of the statement "echo num" is %num% and "set /a num+=1" are in the same place as the compound statement for for. Then, after set on line 4 changes the value of num, it cannot have any effect on the variable num on line 5, because in the preprocessing stage, the variable reference %num% on line 5 has been replaced by the specific value of the variable num assigned before for, and it is replaced by 0 (assigned by the set statement on line 2).
What should I do if I want the value assigned to num by & before in the execution result of the code [code19], and when the code [code20] lists the exe file, the number of exe files will be displayed from 1 to N, then what should I do?
For code [code19], you can split the && link compound statement into two separate statements and write it as:
@echo off set num=0 echo %num% pause
However, this is not the result we want this time.
The method that applies to both code is: use variable delays the extension statement, delay the extension behavior of the variable, thereby obtaining the value we want.
Here, let’s first charge and see what “variable expansion” is going on.
The original words of the batch processing expert willsort in CN-DOS are: "In many visible official documents, a pair of percent signs are used to close environment variables to complete the replacement of their values, which is called "expansion". This is actually a first-party concept, which is called from the perspective of the command interpreter, and from the perspective of our users, we can regard it as a reference, call, or get." (See: Under what circumstances should we use variable delays?/forum/?tid=20733) To put it bluntly, the so-called "variable expansion" is actually a very simple thing: use a specific value to replace the referenced variable and the pair of percent signs close to it.
Since we can get the results we want by delaying the expansion behavior of variables, what are the specific methods?
Generally speaking, the extension behavior of delayed variables can be selected as follows:
1. Use the setlocal enabledelayedexpansion statement in the appropriate position;
2. Use call statements in the appropriate position.
Using the setlocal enabledelayedexpansion statement, then [code19] and [code20] can be modified respectively to:
@echo off setlocal enabledelayedexpansion set num=0&&echo !num! pause
@echo off set num=0 setlocal enabledelayedexpansion for /f %%i in ('dir /a-d /b *.exe') do ( set /a num+=1 echo num The current value is !num! ) echo There are a total of the current directory %num% indivualexedocument dir /a-d /b *.txt|findstr "test">nul&&( echo Existing contains test Textbook of strings )||echo 不Existing contains test 字符串的文本document if exist ( echo exist document ) else 不exist document pause
Using the call statement, then [code19] and [code20] can be modified respectively to:
@echo off set num=0&&call echo %%num%% pause
@echo off set num=0 for /f %%i in ('dir /a-d /b *.exe') do ( set /a num+=1 call echo num The current value is %%num%% ) echo There are a total of the current directory %num% indivualexedocument dir /a-d /b *.txt|findstr "test">nul&&( echo Existing contains test Textbook of strings )||echo 不Existing contains test 字符串的文本document if exist ( echo exist document ) else 不exist document pause
It can be seen that if the variable is delayed by using the setlocal enabledelayedexpansion statement, the variable reference originally used to close with a percent sign pair is changed to an exclamation point pair is used to close; if the call statement is used, the call command is added to the front of the original command, and the single-layer percent sign pair referenced by the variable is changed to a double-layer. Among them, because the call statement uses a double-layer percent sign pair, it is easy to make people confused, so it is used less. The commonly used is to use the setlocal enabledelayedexpansion statement (set means setting, local means local means, enable means able, delayed means delayed, and expansion means expansion. Together, it means: make a variable become a local variable and delay its expansion behavior).
Through the above analysis, we can know:
1. Why use variable delay? Because the variables inside the compound statement need to be able to perceive the change in the variable value in real time.
2. In which occasions do variable delay statements need to be used? In a compound statement, if the value of a variable has changed and the changed value needs to be used elsewhere within the compound statement, then the variable delay statement needs to be used. The compound statements include: for statements, if...else statements, statements connected with connectors &, || and &&, statements connected with pipe symbols|, and statement blocks enclosed in brackets and composed of multiple statements. The most common occasions are the for statement and if...else statement.
3. How to use variable delay?
There are two methods:
① Use the setlocal enabledelayedexpansion statement: Use the setlocal enabledelayedexpansion before obtaining the changed variable value statement, and change the variable reference originally used by the percentage sign pair to close with exclamation mark pairs;
② Use the call statement: add the call command to the front of the original command, and change the single-layer percent sign pair referenced by the variable to a double-layer.
"Variable Delay" is a very important mechanism in batch processing. It is born from the preprocessing mechanism and is used in composite statements, especially in large quantities in powerful for statements. Only by skillfully using this mechanism can you be as seldom in the for-world and take your batch processing level to the next level. Many times, we have always been watching the for processing mechanism. Even if we occasionally gain something, it is just a matter of being difficult to convey. I hope everyone will think about it repeatedly and practice more. Many experiences in details can only be obtained through a lot of exploration. Good Luck!
This section refers to this article in principle: Under what circumstances should variable delay be used?/forum/?tid=20733, the address in this forum is:/?tid=2899
Special thanks: willsort.
4. Go through the boxes and cabinets and traverse the folder: for /r
(I) The function and usage of for /r
According to the help information, the function of for /r is "recursive". Let's change to a more popular one, called "traversing folders".
A more detailed explanation is: in the following statement, if the "element set" is just a dot, then the function of this statement is to list the "directory" and all subdirectories below, and execute the command statements in the "command statement collection" for these folders. Its function is similar to the "dir /ad /b /s path" function nested into the for /f compound statement. If "Directory" is omitted, the previously described operation will be performed in the current directory.
[quote]
for /r directory %%i in (element collection) do command statement collection
[/quote]
Let's first add a code to enhance your impression:
@echo off for /r d:\test %%i in (.) do echo %%i pause
The execution results are as follows:
[quote]
d:\test\.
d:\test\1\.
d:\test\2\.
d:\test\3\.
[/quote]
The effect is to show that the d:\test directory and all subdirectories are paths below, and its effect is similar to dir /ad /b /s d:\test. If you want to talk about the difference between the two, you can summarize three points:
1. The paths listed for /r are all slashed and dotted at the end, while the dir statement does not, which will affect the further processing of the obtained path;
2. For /r cannot list directories with hidden attributes, while the dir statement can obtain directories with specified attributes by specifying parameters immediately followed by /a, which is more flexible;
3. If you want to further process the obtained path, you need to put the dir statement into the for /f statement for analysis and write it in the form of for /f %%i in ('dir /ad /b /s') do …; since the for /r statement is to list the paths while processing, you will not feel any pause in the early stage when processing a large number of paths, and the for /f statement needs to wait until the dir /ad /b /s statement lists all paths before reading it into memory for processing. Therefore, when processing a large number of paths, you will feel a pause in the early stage.
The second difference is easily overlooked by everyone, resulting in omissions when listing paths with for /r; while the third point will give everyone a more intuitive feeling and it is easy to feel the difference between the two.
What if the "element collection" is not a dot number? So what?
Let's take a look at this code:
@echo off for /r d:\test %%i in (a b c) do echo %%i pause
The result of the operation is:
[quote]
D:\test\1\a
D:\test\1\b
D:\test\1\c
D:\test\2\a
D:\test\2\b
D:\test\2\c
D:\test\3\a
D:\test\3\b
D:\test\3\c
[/quote]
It turns out that its meaning is: list d:\test and all subdirectories, add a, b, and c to all directory paths and then display it.
Let's look at another code:
@echo off for /r d:\test %%i in (*.txt) do echo %%i pause
The result of the operation is:
[quote]
D:\test\
D:\test\1\
D:\test\1\
D:\test\2\
D:\test\2\
D:\test\3\
[/quote]
The meaning of this code is: list the txt text files in d:\test and all subdirectories (folders ending in .txt will not be listed).
Let's look back and summarize the function of this statement:
[quote]
for /r directory %%i in (element collection) do command statement collection
[/quote]
The function of the above statement is:
1. List the "directory" and all subdirectories under the directory path, and splice the listed directory path and each element in the element set into a new string in the format of "directory path\element", and then execute each command in the "command statement collection" for each such new string;
In particular, when "element set" is accompanied by a wildcard character separated by a dot number? or *, "element set" is regarded as a file (not regarded as a folder), and the function of the entire statement is to match the files matching under the folder referred to by the "directory" and all subfolders; if it is not separated by a dot number, "element set" is regarded as a folder (not regarded as a file);
2. When the "directory" is omitted, it is targeted to the current directory;
3. When there is only a dot in the element set, only the directory path will be listed;
(II) for /r or dir /ad /b /s? How to choose when listing directories
As mentioned earlier, when listing directories, the effects of for /r and dir /ad /b /s are very similar, which creates a question: When I want to get the directory path and further process it, how should I choose between the two?
This issue has actually been discussed before, so let’s make a detailed analysis.
Let's take a look at the advantages and disadvantages of both:
1、for /r:
1) Advantages:
① The operation of obtaining the directory path and processing the directory path can be achieved at the same time through only 1 statement;
② When traversing folders, they are listed and processed. When they get a path, they process one path. The memory usage is small and there will be no sense of pause when processing a large number of paths;
2) Disadvantages:
① If a directory with hidden attributes cannot be obtained, omissions will occur;
② Cannot get directory with specified attributes
2、dir /ad /s:
1) Advantages:
① Can obtain directories with arbitrary attributes at one time, and there will be no omissions;
② It is possible to obtain directories with arbitrary attributes by specifying different parameters, which is more flexible.
2) Disadvantages:
① The dir /ad /s statement can only obtain the directory path. If you want to implement further processing, you need to embed it in the for /f statement to achieve it, and the writing method is not concise enough;
② After embedding the for /f statement, it needs to be written as for /f "delims=" %%i in ('dir /ad /b /s') do .... It is restricted by the operation mechanism of the for /f statement. You need to list all paths first and put them in memory before each path can be further processed. When processing a large number of paths, the memory usage is too large, and there will be a significant sense of pause in the early stage, and the user experience is not good enough;
Based on the above analysis, the following choices can be made:
1. If it is just to get the paths to a certain folder and all its subfolders, please select the dir /ad /b /s statement;
2. If you need to filter folders with hidden attributes, both for /r and dir statements can be implemented, but for /r has a small memory footprint and fast processing speed, which is the best choice;
3. If you need to get all folders, there is no choice except dir /ad /b /s, because the for /r statement will miss folders with hidden attributes;
In actual use, I prefer to use a combination of for /f and dir because it will not cause omissions and can give me a more flexible way of handling. The only thing I need to endure is the early pause when dealing with a large number of paths, and the slightly higher memory footprint behind it; when I pursue speed and can ignore directories with hidden attributes, I will switch to the for /r solution, but there are not many such situations - who would be willing to tolerate omissions in order to pursue speed?
5. Exist only to match the first layer directory: for /d
For /d, the complete meaning is /directory, which is originally intended to handle folders. Its complete statement should be like this:
for /d %%i in (element set) do command statement set
When the "element collection" contains wildcards? or *, it matches the folder, but, compared to for /r, for /d at this time, its effect is pitifully small: it can only match the first-level folder in the current directory, or the folder in the specified location, and cannot match the deeper subfolders.
For example: for /d %%i in (d:\test*) do echo %%i will match folders such as: d:\test, d:\test1, d:\test2. If such a path does not exist, there will be no echo.
When "element set" does not contain any wildcards, its function is no different from statements like "for %%i in (element set) do command statement collection".
Therefore, the role of for /d becomes very subtle: when the "element collection" contains wildcards? or *, its function is to match folders. At this time, it can only match the first-level folder in the current directory, or folders at the specified location, which is not as deep as for /r in hierarchy, but it has the same bad temper as for /r: it cannot match folders with hidden attributes; it is not as flexible as the combination of for /f and dir; when the "element collection" does not contain any card characters, it is a completely replica of the "for %%i in (element collection) do..." statement, but it is slightly complicated.
The effect of for /d is so limited that I used it so few times that I couldn't find its place to use it for a time. I thought it tasteless and it was a pity to throw it away. It was a whole piece of nothing.
In a certain year and month, I wrote this code in the cmd window:
for /d %i in (test*) do @echo %i
My original intention was to see how many test folders have been created in my temporary directory for years of testing, so that I can then replace echo with rd and delete it. At this time, I found that this code is so concise, which is irreplaceable by the combination of for /r or for and dir /ad /b (echo can be replaced by rd and you can delete these test directories directly).
The joy brought to me by the concise code lasted only for a few more seconds, and I began to be confused - similar situations where for /d seemed to be very few and lacking.
Six. Counting loop: for /l
/l is the abbreviation of /loop. It is translated from the bird's song, loop means loop. In fact, all for statements can be regarded as a kind of "loop", but in /l, it specifically refers to looping according to the specified number of times.
The complete format of the for /l statement is as follows: for /l %%i in (x,y,z) do (…). In this statement, x, y and z can only take integers, both positive and negative, x refers to the starting value, y refers to the step size, and z is the ending value. The specific meaning is: counting from x, y is the step size, until the integer value closest to z, the statement after do will be executed as many times.
Let's give a specific example:
for /l %%i in (1,2,10) do echo bathome
In the above code, the initial value is 1, the step size is 2, and the termination value is 10, indicating that the count starts from 1, and is calculated every 2 numbers until the integer closest to 10, listed, which is 1, 3, 5, 7, 9, and the next one is 11, if it exceeds 10, it will no longer be calculated. Therefore, the statement after do is only executed 5 times, and 5 bathomes will be displayed in succession.
In fact, the values of x, y and z can be positive or negative, or even 0, and the limit is very loose:
1. The value of step size y cannot be 0;
2. When the value of step size y is a positive integer, the terminating value z cannot be less than the initial value x;
3. When the value of step size y is a negative integer, the terminating value z cannot be greater than the initial value x.
In other words, it is necessary to ensure that a valid array sequence can be obtained between in and do.
For example:
for /l %%i in (-1,2,5) do echo bathome
for /l %%i in (5,-2,-1) do echo bathome
The functions of the above two codes are exactly the same, and they will display the bathe 4 times. The difference is that [code26] is a positive order calculation, while [code27] is just a reverse order count.
The following codes are all problematic:
for /l %%i in (1,0,1) do echo bathome
for /l %%i in (2,1,1) do echo bathome
for /l %%i in (1,-1,2) do echo bathome
Among them, [code28] violates the limit that the step size cannot be 0 and will fall into an infinite loop; [code29] and [code30] both make the same mistake: the valid sequence elements cannot be obtained, resulting in the value obtained between in and do being an empty element, making the entire for statement unable to be executed.
When everyone understands the specific function of for /l, do they think of goto loop statements that are similar to it? It seems that for /l and goto loop statements can be replaced with each other?
Generally speaking, the for /l statement can be replaced with a goto loop, but the goto loop may not be replaced by the for /l statement. For the specific reasons, please think about it carefully. I will not explain in detail here. I just provide a concise answer to a question that everyone is very concerned about, that is: when should we use for /l counting loop, and when should we use goto condition loop?
The answer is very simple: when the number of loops is determined, the for /l statement is preferred, and the goto statement can also be used, but it is not recommended; when the number of loops is uncertain, the goto statement will be the only choice, because at this time, conditional statements such as if need to be used to determine when the goto jump will end.
postscript:
When Windows opens colorful graphic windows for us
DOS is destined to die
CMD will shrink without any suspense
Batch processing is gradually becoming silent
The arrival of powershell will undoubtedly make more people forget about batch processing
This is a technique that is about to be lost
This is a treacherous area
However, command line tools still have the great power to batch everything up
Character interfaces are still synonymous with efficient operation
I have praised the convenience and flexibility of batch processing
Once deeply impressed by the simplicity and speed of batch processing
I have always wanted to do something for batch promotion
So, from answering questions to everyone in CN-DOS, to setting up your own forum for answering questions and then writing tutorials without regrets, step by step, joy, anger, sorrow, and mixed feelings
Until now, he resigned from all management positions such as webmaster and gradually faded out of the batch processing circle
The dream is still there, but the desire is still there, but the strength is gradually insufficient
This teaching post, which has been published in October 2008 and has been delayed for more than two years without realizing it.
Every time the member who reads the comments asks when there will be updates
There is always a trace of guilt in my heart
Today, I finally took the time to break it
But the flowers are similar every year, but the people are different every year
The complicated affairs have made me no longer have the same state of mind as before
For /l always feels like a tiger's head and tail
I can only say sorry to you
Before I completely fade out of the batch circle
I can only do my best to give you what I have learned
Finally, I hope that the forum managers can often pump water for this post according to the management tips on the top floor.
Or permanently lock this post for the convenience of reading
If reading text is troublesome, I suggest you download and read the pdf version///books/