用不同的开发语言/工具,获取操作系统和CPU等信息方法不一样,真的记不住。
返回的文字方式也很多样,有时仅仅显示个Linux了事。
但不管什么语言写的程序,都通过调用命令行来获得这些信息,则结果比较统一好看。
通过ver
命令查看版本号,并且只看包含Windows
字样的一行。
不过由于Windows神奇的版本号设置,就算是Win11看到的也是10.0呢(好像22000以上就是Win11)。
C:\>ver | find /I "Windows"
Microsoft Windows [版本 10.0.22621.900]
通过wmic
指令查看CPU
的名称,因为结果第一行是标题,所以查找不看包含标题Name
的一行。
PS:假设CPU的名字里面有Name
那就看不到了……有这种名字么……?
同理可以查询其它CPU参数信息(比如多少核),或其它硬件信息,这里不再赘述。
C:\>wmic cpu get name | find /V /I "Name"
12th Gen Intel(R) Core(TM) i9-12900F
通过查看/etc/os-release
里的好听的名字,可以看到发行版名称和版本。
然后uname -r
查看Linux的内核版本。
[shion@homewsl ~]$ cat /etc/os-release | grep PRETTY | cut -d '"' -f 2
Ubuntu 22.04.1 LTS
[shion@homewsl /]$ uname -r
5.15.74.2-microsoft-standard-WSL2
通过查看/proc/cpuinfo
里的model name
,得到CPU名称。
然后去掉重复(每个核心都有一份重复的CPU名称)。
然后取这行冒号:
后面的名称,
并去掉名称前面的空格。
[shion@homewsl ~]$ cat /proc/cpuinfo |grep 'model name'|uniq | cut -d ':' -f 2 | sed 's/^[ ]*//g'
12th Gen Intel(R) Core(TM) i9-12900F
ver
指令,需要在cmd.exe
当中执行(因为ver
不是一个单独的程序)。cmd.exe /C ver
。💡 其实go可以用runtime.GOOS
来取得操作系统名称,但是……太简化了。
💡 注意执行指令的时候,程序和参数别用单个字符串传进去,程序名称和每个参数分别传递。
用法为:out, err := getCmdResult("cmd.exe", "/C", "ver")
:
func getCmdResult(aCMD string, arg ...string) (string, error) {
var cmdText string
out, err := exec.Command(aCMD, arg...).Output()
if err != nil {
return "", err
}
if OSisWin() {
// the Windows is default GBK in Chinese, need to be decoded to UTF8.
b, err := simplifiedchinese.GBK.NewDecoder().Bytes(out)
if err != nil && err != io.EOF {
return "", err
}
buffer2 := bytes.NewBuffer(b)
cmdText = buffer2.String()
} else {
cmdText = string(out)
}
return strings.TrimSpace(cmdText), nil
}
💡 同样Java也可以用System.getProperty("os.name")
来取得操作系统名称……
💡 执行指令和go相反,把指令全部传进去就可以了。
用法为:out = getCmdResult("cmd.exe /C ver",Charset.forName("GBK"))
:
public static String getCmdResult(String aCMD,Charset cs) throws Exception {
Runtime r = Runtime.getRuntime();
Process proc = r.exec(aCMD);
BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream(), cs));
String line;
StringBuilder sb = new StringBuilder();
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
proc.waitFor();
return sb.toString();
}
💡 也可以参考《关于Windows环境下处理.tar.gz文件的问题》里那样CreateProcess
的方法。
下面明显简单点(是否能在Windows服务程序中使用就不知道了)。
呃,如果纯C请别用string。
std::string getCmdResult(const string& strCmd)
{
char buf[65535] = { 0 };
#if defined(_MYLINUX)
FILE* pf = popen(strCmd.c_str(), "r");
if (pf == NULL) return "";
#elif defined(_MYWINDOWS)
FILE* pf = _popen(strCmd.c_str(), "r");
if (pf == NULL) return "";
#endif
string strResult = "";
while (fgets(buf, sizeof buf, pf))
{
strResult += buf;
}
#if defined(_MYLINUX)
pclose(pf);
#elif defined(_MYWINDOWS)
_pclose(pf);
#endif
return strResult;
}
💡 如果不wait,有时还没读到调用的命令行返回就过去了。
💡 参数需要单独添加到参数列表,这里为了方便传进去时用空格分割。
💡 不像go或java,现在需要自己区分Windows和Linux了。
function getCmdResult(const ACmd: string; const Params:string ='';const aWait:Integer=0): string;
var
{$IFDEF MSWINDOWS}
Process: TProcess;
AStringList: TStringList;
AParamList: TStringList;
i:Integer;
{$ENDIF}
{$IFDEF LINUX}
t: Text;
s: string;
{$ENDIF}
begin
Result := '';
{$IFDEF MSWINDOWS}
Process := TProcess.Create(nil);
try
Process.Options := Process.Options + [poNoConsole, poUsePipes];
Process.Executable := ACmd;
AParamList := TStringList.Create;
StrToStrings(Params, ' ', AParamList, False);
Process.Parameters.Clear;
for i := 0 to AParamList.Count -1 do
begin
Process.Parameters.Add(AParamList[i]);
end;
Process.Execute;
if aWait>0 then
Process.WaitOnExit(aWait);
AStringList := TStringList.Create;
AStringList.LoadFromStream(Process.Output);
Result := CP936ToUTF8(AStringList.Text.Trim);
AStringList.Free;
AParamList.Free;
finally
Process.Free;
end;
{$ENDIF }
{$IFDEF LINUX}
popen(t, ACmd, 'R');
if not Eof(t) then
begin
Readln(t, s);
result:=s.Trim;
end;
pclose(t);
{$ENDIF }
end;
💡 没想到最快速的开发工具确是最麻烦的。
💡 当然是可以用TOSVersion.ToString
来取得操作系统名称的……
因为方式差太多,所以Windows/Linux分开写了:
{$IFDEF MSWINDOWS}
function getCmdResult(Command: string): string;
var
SA: TSecurityAttributes;
SI: StartupInfo;
PI: PROCESS_INFORMATION;
MainRead, ChildWrite: THandle;
WasOK: Boolean;
Buffer: array[0..255] of AnsiChar;
BytesRead: Cardinal;
begin
Result := '';
SA.nLength := sizeof( SECURITY_ATTRIBUTES );
SA.lpSecurityDescriptor := nil;
SA.bInheritHandle := TRUE;
if ( not CreatePipe( MainRead, ChildWrite, @SA, 0 ) ) then
begin
raise( Exception.Create('CreatePipe MRCW!'));
end;
ZeroMemory( @SI, sizeof( STARTUPINFO ) );
SI.cb := sizeof( STARTUPINFO );
SI.wShowWindow := SW_HIDE;
SI.hStdError := ChildWrite;
SI.hStdOutput := ChildWrite;
SI.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
ZeroMemory( @PI, sizeof( PROCESS_INFORMATION ) );
if not CreateProcess(nil, PChar(@Command[1]), nil, nil, True, NORMAL_PRIORITY_CLASS , nil, nil, SI, PI) then
begin
CloseHandle( PI.hThread );
CloseHandle( PI.hProcess );
CloseHandle( MainRead );
CloseHandle( ChildWrite);
raise( Exception.Create('Error create process!'));
end
else
begin
CloseHandle( PI.hThread );
CloseHandle( PI.hProcess );
CloseHandle( ChildWrite);
end;
repeat
WasOK := ReadFile(MainRead, Buffer, 255, BytesRead, nil);
if BytesRead > 0 then
begin
Buffer[BytesRead] := #0;
Result := Result + String(AnsiString(Buffer));
end;
until not WasOK or (BytesRead = 0);
WaitForSingleObject(PI.hProcess, 1000);
CloseHandle(MainRead);
end;
{$ENDIF}
然后是Linux:
{$IFDEF LINUX}
uses
Posix.Base,
Posix.Unistd,
Posix.Fcntl,
......
......
type
TStreamHandle = pointer;
function popen(const command: PAnsiChar; const _type: PAnsiChar): TStreamHandle; cdecl; external libc name _PU + 'popen';
function pclose(filehandle: TStreamHandle): int32; cdecl; external libc name _PU + 'pclose';
function fgets(buffer: pointer; size: int32; Stream: TStreamHAndle): pointer; cdecl; external libc name _PU + 'fgets';
function getCmdResult(Command: string): string;
var
Handle: TStreamHandle;
Data: array[0..511] of uint8;
acommand : PAnsiChar;
sReturn : string;
begin
Result := '';
try
acommand := PAnsiChar(AnsiString(command));
Handle := popen(acommand, 'r');
sReturn := '';
try
while fgets(@Data[0], Sizeof(Data), Handle) <> nil do
sReturn := sReturn + Utf8ToString(@Data[0]);
finally
pclose(Handle);
end;
Result := sReturn;
except
end;
end;
{$ENDIF}
简直累sker人呀……
💡 其实Python可以用platform.platform()
来取得操作系统名称……
取得的名称还算正常,只是仍然摆脱不了“Win11版本号是10”这种bug。
使用上Python可能是最简单的,而且Windows下可以用管道符号。
简化到都不用自己再封装函数。
import subprocess
...
# Windows
out = subprocess.check_output('wmic os get Caption | find /V /I "Caption"', shell=True).decode().strip()
# or Linux
out = subprocess.check_output('cat /proc/cpuinfo | grep \'model name\' | uniq | cut -d\':\' -f 2', shell=True).decode().strip()