1

I'm running this in Java:

Runtime.getRuntime().exec("wmic bios get SerialNumber");

and when I read the output of that command, the line separator is not the expected \r\n, but instead, \r\r\n. The output of that command on cmd looks like this:

C:\Users\pupeno>wmic bios get SerialNumber
SerialNumber
System Serial Number


C:\Users\pupeno>

If I read the output with a BufferedReader:

System.out.println("====================");
BufferedReader output = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = output.readLine()) != null) {
     System.out.println(line);
}
System.out.println("====================");

it looks like this:

====================
SerialNumber          

System Serial Number  



====================

As you can see, I'm getting an extra blank line per line. Investigating it by reading it straight from the InputStreamReader like this:

try (InputStreamReader inputStreamReader = new InputStreamReader(p.getInputStream())) {
    int c;
    while ((c = inputStreamReader.read()) != -1) {
        String ch = String.valueOf((char) c);
        switch(c) {
            case 10:
                ch = "LF";
                break;
            case 13:
                ch = "CR";
                break;
            case 32:
                ch = "Space";
                break;

        }
        System.out.printf("%3d - %s%n", c, ch);
    }
}

I get this output:

 83 - S
101 - e
114 - r
105 - i
 97 - a
108 - l
 78 - N
117 - u
109 - m
 98 - b
101 - e
114 - r
 32 - Space
 32 - Space
 32 - Space
 32 - Space
 32 - Space
 32 - Space
 32 - Space
 32 - Space
 32 - Space
 32 - Space
 13 - CR
 13 - CR
 10 - LF
 83 - S
121 - y
115 - s
116 - t
101 - e
109 - m
 32 - Space
 83 - S
101 - e
114 - r
105 - i
 97 - a
108 - l
 32 - Space
 78 - N
117 - u
109 - m
 98 - b
101 - e
114 - r
 32 - Space
 32 - Space
 13 - CR
 13 - CR
 10 - LF
 13 - CR
 13 - CR
 10 - LF

Any ideas why this is happening? Will all Windows computers behave like this?

Pablo Fernandez
  • 279,434
  • 135
  • 377
  • 622
  • wmic is Unicode. As a programmer you can call it direct and don't need to shell. See here for a vbscript example (you use the same objects and the same methods). https://stackoverflow.com/questions/48969278/is-there-a-way-to-run-a-cmd-file-when-a-specific-program-is-closed/48969980#48969980 – ACatInLove Feb 26 '18 at 07:58
  • 1
    @ACatInLove ok, if it's Unicode, that looks like UTF-8. Why would UTF-8 have \r\r\n as new line? Also, that answer is .Net and this is Java. I'm not aware of the same API existing in Java. – Pablo Fernandez Feb 26 '18 at 08:22
  • The answer is COM which most languages can do as its the second way of interacting with windows. (API calls, COM calls, and .NET framework calls. And a small section of the graphics are C++ (with an unusable COM interface as well). Also it's not UTF8 but it is UTF 16. – ACatInLove Feb 26 '18 at 08:31
  • 1
    Accessing COM from Java is incredibly convoluted. Why does UTF-16 uses \r\r\n? According to what I'm reading on https://en.wikipedia.org/wiki/UTF-16, each character in UTF-16 it's at least two bytes, but what I'm seeing with program is that each character is one byte. I know the API might return UTF-16, but what I'm getting from the command line doesn't look like UTF-16. It looks like ASCII (or UTF-8 if we are generous). – Pablo Fernandez Feb 26 '18 at 08:42
  • I sent it to a text file. It's a UTF16 file with a UTF16 header. Also 100,000 answers here say it's Unicode. Command is OEM 8 bit. Windows does a lot of conversions between Unicode/ansi/and OEM. Remember that in windows plain text controls breaks on LF only (Notepad) and ignores CR and richtext controls break on CR and ignore LF, so do browsers (for source code). – ACatInLove Feb 26 '18 at 08:54
  • @ACatInLove, wmic.exe only writes Unicode (UTF-16LE) to a disk file. When writing to a pipe or console it hard codes using a best-fit OEM encoding (not the current console codepage, so we can't get around this via chcp.com). The OP's displayed `char` (byte) output is obviously not UTF-16. That would have interleaved NUL bytes. OEM is fine for a serial number, but if you need non-mojibake Unicode text (user names, file names, etc), then wmic's output must be redirected to a disk file; a pipe will not work. – Eryk Sun Feb 26 '18 at 08:55
  • As to getting CRCRLF, that's a bug in wmic.exe. It uses a file opened in the C runtime's text mode to print strings that already end in CRLF. Then the C runtime converts the LF to CRLF and you end up with CRCRLF. This bug should have been fixed ages ago. – Eryk Sun Feb 26 '18 at 08:58
  • 2
    @ACatInLove `wmic` being Unicode has exactly nothing to do with it being accessible, or not, from Java, nor with the stated issue of receiving `\r\r\n`. – user207421 Feb 26 '18 at 09:27
  • COM gives you Unicode which programs can understand. COM calls are a thousand times faster than shelling. – ACatInLove Feb 26 '18 at 09:33
  • @ACatInLove Unicode has nothing to do with `wmic` printing `\r\r\n`. These characters are the same in all encodings. You are not making any sense. – user207421 Feb 26 '18 at 23:42

2 Answers2

1

This is very curious.

If you run your wmic command from a terminal and redirect to a file, you get \r\n. If you run it via a Java process you get \r\r\n.

It also happens if you just use the InputStream directly, without the InputStreamReader, so it therefore has nothing to do with Unicode, contrary to what you've been told in comments.

I wasn't able to find another Windows command that behaves like this on a very quick test. So it seems we just have to put it down to a peculiarity of wmic when outputting to a pipe.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • 1
    wmic explicitly adds CRLF to the strings it writes. Typically in C/C++ only LF is used, and it gets translated to CRLF by the standard I/O library if the file is opened in text mode (not a Windows OS behavior per se; it's a C runtime feature). wmic's implementation forks on whether stdout is a disk file or a pipe/console. When it's a disk file, it ends up using a C++ stream class method `CFileOutputStream::Write` that basically writes the string in binary mode. When it's a pipe or console, it uses `fprintf`, which for the text-mode file descriptor ends up translating CRLF to CRCRLF. – Eryk Sun Feb 27 '18 at 07:00
  • 1
    Also, when writing to a console or pipe it uses a best-fit OEM encoding, which will mojibake mangle strings that can't be represented in the OEM codepage (a set of about 250 characters). This can include filenames, registry keys and values, user names, etc. So the best way to use wmic in general is to write the output to a temporary file, which will be UTF-16 and end lines properly with only CRLF instead of CRCRLF. – Eryk Sun Feb 27 '18 at 07:03
0

Perhaps using PowerShell will make this a bit easier.

Runtime.getRuntime().exec("powershell.exe -NoLogo -NoProfile -Command (Get-CimInstance -ClassName CIM_BIOSElement).SerialNumber")

A full-up program.

import java.io.*;

public class t2 {
    public static void main( String[] args ) {
        try {
            // Runtime.getRuntime().exec("wmic bios get SerialNumber");
            Process p = Runtime.getRuntime().exec("powershell.exe -NoLogo -NoProfile -Command (Get-CimInstance -ClassName CIM_BIOSElement).SerialNumber");

            BufferedReader output = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String line;
            while ((line = output.readLine()) != null) {
                 System.out.println(line);
            }
        }
        catch (Throwable t) {
        }
    }
}
lit
  • 14,456
  • 10
  • 65
  • 119