3

I have a batch file which invokes a tool. Now i face an issue in the batch file at a for loop. But with all research about the Batch script, I am still not able to make out what the expression is trying to say. I have turned to this forum for the help after some days of digging for this line. The for loop goes as below:

for /f "tokens=1-3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do (    @echo Debug Output: %%g    set JAVAVER=%%g  )

I understand its a for loop trying to set JAVA version and running through different files, but the issue is its not doing the intended task and hence i need to further understand what the below line is evaluating to:

('java -version 2^>^&1 ^| findstr /i "version"')

Also to add to my confusion, if i run the below command separately, it gives output as below which does not have any "version" text in it. But the same command, if i run in conjunction as above, does give an output of current java version.

Code:

for /f "tokens=3" %%g in ('java -version 2^>^&1') do (    @echo Debug Output: %%g    set JAVAVER=%%g    )

Output:

Debug Output: "1.8.0_231"
Debug Output: Runtime
Debug Output: 64-Bit

Example for reply to Doug Deden:

When i do below, it does display the echo command output.

for /f "tokens=3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do (
@echo Debug Output: %%g
set JAVAVER=%%g
@echo %JAVAVER%
)

But when i do the above by removing the 2^>^&1, it does not consider echo the command.

My confusion is while i put the 2^>^&1 part in code, the standard error is redirected to standard output, but what happens if i remove it completely? Does it redirect to some other output and also why does it stop echoing the command?

MitSM
  • 33
  • 4

1 Answers1

3

That one line has a lot of functionality in it. An plain-English summary would go something like this:

  • Ask Java what version it is. This gives multiple pieces of information.
  • Find the line in Java's output that contains "version".
  • Find the third component on that line.
  • Echo that as output, and also set the JAVAVER environment variable to that value.

Let's break it apart.

for /f "tokens=1-3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do ( @echo Debug Output: %%g set JAVAVER=%%g )

I'll start with the innermost piece: 'java -version 2^>^&1 ^| findstr /i "version"' The batch file tells Windows to run this, and then the batch file will do something with the result.

The carets (^) serve to tell the batch file to treat the following characters literally. Otherwise, the batch file would attempt to interpret them and treat them as instructions or modifiers to the batch file itself. Instead, we want the batch file to just pass them along "as-is". So, the batch file tells Windows to run this:

'java -version 2>&1 | findstr /i "version"'

  • java - run java.exe, wherever it is found in the Path
  • -version - tell java.exe to print version information and exit
  • 2>&1 - redirect any errors to the same place as standard output (Rob van der Woude has a great page about redirection) -- you need this because Java sends its version information to the standard error stream instead of to the standard output stream -- see this Q&A on StackOverflow -- this makes it send the version information to the standard output stream instead, so it can eventually end up being seen by findstr
  • | - send the results so far to the next program -- findstr
  • findstr - search for a string in the input, which is the output of the java -version 2>&1 command
  • /i - tell findstr to do a case-insensitive search
  • "version" - tell findstr what text to search for

Typical output of java -version will look like this:

java version "1.8.0_92"
Java(TM) SE Runtime Environment (build 1.8.0_92-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.92-b14, mixed mode)

And then after passing it through findstr, you be left with only the line that contains "version":

java version "1.8.0_92"

Next, let's look at the for /f piece. for /f … in … will look at the result of the command that follows "in" and process it. Your first and third examples have different values for the tokens=… part, so something may have been lost in the copying and pasting. In any case, the tokens=… part tells the for /f command which parts (aka tokens) of the line being processed to care about. By default, a space or a tab indicates a new token. So a parameter of tokens=3 would tell for /f to look at the third piece of the line, which is "1.8.0_92". The %%g option tells for /f to store what it finds in variable %g. (The extra % is needed because you are running this from a batch file. If you ran it directly at a command prompt, you'd only need a single %.)

So far, if you use tokens=3, you'll have "1.8.0_92" stored in the %g variable. (If you used tokens=1-3 instead, you'll have "java" in %g, version in %h, and "1.8.0_92" in %i. The for /f command starts shoving tokens into variables starting with the variable you specify, and incrementing through the alphabet.)

Then, the do portion of the line tells what command you want executed for each thing the for /fcommand comes up with. In this case, it does two things:

  • @echo Debug Output: %%g - print "Debug Output:" to the standard out, followed by the value of the %g variable, which we saw earlier is "1.8.0_92".
  • set JAVAVER=%%g - set the value of the JAVAVER environment variable to whatever is in the %g variable.

(Rob van der Woude also has a great page explaining the for/f command.)

When you took out the findstr part in one of your experiments, and used tokens=3, you saw output indicating that the for /f command grabbed the third token on each line of Java's version information, highlighted here in bold:


java version "1.8.0_92"
Java(TM) SE Runtime Environment (build 1.8.0_92-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.92-b14, mixed mode)

And that experiment would also have set the JAVAVER environment variable to each one of those strings, so it would end up with the last one -- 64-bit.

Doug Deden
  • 1,844
  • 7
  • 10
  • Thanks for the excellent piece of detailed explanation. I went through Rob's links too and those were also very helpful. I tried the code with some modifications and again found some confusing pieces. If you could take a look at the recent edit it can explain more. Again, thank you very much for your efforts in explaining the whole part! – MitSM Mar 03 '20 at 08:13
  • @MitSM Nice observation! I hadn't dug into why the `2>&1` part was needed until you pointed it out. It's because of the way Java outputs its version information -- to the standard error stream, instead of the standard output stream. I've edited my answer to incorporate this additional information. – Doug Deden Mar 03 '20 at 23:35
  • Excellent..Fully understood the whole thing. Thank you very much for the inputs! – MitSM Mar 05 '20 at 08:38