程序通过命令行获取操作系统名称+版本+CPU名称等各种信息

34 篇文章 2 订阅

用不同的开发语言/工具,获取操作系统和CPU等信息方法不一样,真的记不住。
返回的文字方式也很多样,有时仅仅显示个Linux了事。
但不管什么语言写的程序,都通过调用命令行来获得这些信息,则结果比较统一好看。

(一)命令行取得信息

(1.1)Windows

(1.1.1)操作系统名称版本

通过ver命令查看版本号,并且只看包含Windows字样的一行。
不过由于Windows神奇的版本号设置,就算是Win11看到的也是10.0呢(好像22000以上就是Win11)。

C:\>ver | find /I "Windows"
Microsoft Windows [版本 10.0.22621.900]

(1.1.2)CPU名称

通过wmic指令查看CPU的名称,因为结果第一行是标题,所以查找不看包含标题Name的一行。
PS:假设CPU的名字里面有Name那就看不到了……有这种名字么……?
同理可以查询其它CPU参数信息(比如多少核),或其它硬件信息,这里不再赘述。

C:\>wmic cpu get name | find /V /I "Name"
12th Gen Intel(R) Core(TM) i9-12900F

(1.2)Linux

(1.2.1)操作系统名称版本

通过查看/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

(1.2.2)CPU名称

通过查看/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

(1.3)实测

  • Microsoft Windows [版本 10.0.22621.900]
  • Ubuntu 22.04.1 LTS 5.15.74.2-microsoft-standard-WSL2
  • Anolis OS 7.9 3.10.0-1160.80.1.0.1.an7.x86_64
  • openEuler 22.03 LTS 5.10.0-60.68.0.93.oe2203.x86_64
  • CentOS Linux7 (Core) 3.10.0-1160.71.1.el7.x86_64
  • ……

(二)程序调用命令行

  • ⚠️ 在Windows下如果要执行ver指令,需要在cmd.exe当中执行(因为ver不是一个单独的程序)。
    所以上面例子指令改为:cmd.exe /C ver
  • ⚠️ 在Windows下执行执行如果加上管道,似乎没法返回结果。Windows真麻烦……

(2.1)Golang

💡 其实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
}

(2.2)Java

💡 同样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();
    }

(2.3)C++

💡 也可以参考《关于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;
}

(2.4)Pascal

(2.4.1)Lazarus - Free Pascal

💡 如果不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;  

(2.4.2)Delphi

💡 没想到最快速的开发工具确是最麻烦的。
💡 当然是可以用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人呀……

(2.5)Python

💡 其实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()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值