簡體   English   中英

如何從 Bash 腳本中檢測操作系統?

[英]How to detect the OS from a Bash script?

我想將我的.bashrc.bash_login文件保留在版本控制中,以便我可以在我使用的所有計算機之間使用它們。 問題是我有一些特定於操作系統的別名,所以我正在尋找一種方法來確定腳本是在 Mac OS X、Linux 還是Cygwin 上運行

Bash腳本中檢測操作系統的正確方法是什么?

我認為以下應該有效。 不過我不確定win32

if [[ "$OSTYPE" == "linux-gnu"* ]]; then
        # ...
elif [[ "$OSTYPE" == "darwin"* ]]; then
        # Mac OSX
elif [[ "$OSTYPE" == "cygwin" ]]; then
        # POSIX compatibility layer and Linux environment emulation for Windows
elif [[ "$OSTYPE" == "msys" ]]; then
        # Lightweight shell and GNU utilities compiled for Windows (part of MinGW)
elif [[ "$OSTYPE" == "win32" ]]; then
        # I'm not sure this can happen.
elif [[ "$OSTYPE" == "freebsd"* ]]; then
        # ...
else
        # Unknown.
fi

對於我的 .bashrc,我使用以下代碼:

platform='unknown'
unamestr=`uname`
if [[ "$unamestr" == 'Linux' ]]; then
   platform='linux'
elif [[ "$unamestr" == 'FreeBSD' ]]; then
   platform='freebsd'
fi

然后我做這樣的事情:

if [[ $platform == 'linux' ]]; then
   alias ls='ls --color=auto'
elif [[ $platform == 'freebsd' ]]; then
   alias ls='ls -G'
fi

這很丑陋,但它有效。 如果您願意,您可以使用case而不是if

bash 手冊頁說變量 OSTYPE 存儲操作系統的名稱:

OSTYPE自動設置為描述執行 bash 的操作系統的字符串。 默認值取決於系統。

此處設置為linux-gnu

$OSTYPE

您可以簡單地使用預定義的$OSTYPE變量,例如:

case "$OSTYPE" in
  solaris*) echo "SOLARIS" ;;
  darwin*)  echo "OSX" ;; 
  linux*)   echo "LINUX" ;;
  bsd*)     echo "BSD" ;;
  msys*)    echo "WINDOWS" ;;
  cygwin*)  echo "ALSO WINDOWS" ;;
  *)        echo "unknown: $OSTYPE" ;;
esac

但是,較舊的外殼(例如Bourne shell無法識別它。


uname

另一種方法是根據uname命令檢測平台。

請參閱以下腳本(已准備好包含在 .bashrc 中):

# Detect the platform (similar to $OSTYPE)
OS="`uname`"
case $OS in
  'Linux')
    OS='Linux'
    alias ls='ls --color=auto'
    ;;
  'FreeBSD')
    OS='FreeBSD'
    alias ls='ls -G'
    ;;
  'WindowsNT')
    OS='Windows'
    ;;
  'Darwin') 
    OS='Mac'
    ;;
  'SunOS')
    OS='Solaris'
    ;;
  'AIX') ;;
  *) ;;
esac

您可以在我的.bashrc找到一些實際示例


這是Travis CI上使用的類似版本:

case $(uname | tr '[:upper:]' '[:lower:]') in
  linux*)
    export TRAVIS_OS_NAME=linux
    ;;
  darwin*)
    export TRAVIS_OS_NAME=osx
    ;;
  msys*)
    export TRAVIS_OS_NAME=windows
    ;;
  *)
    export TRAVIS_OS_NAME=notset
    ;;
esac

檢測操作系統和 CPU 類型並不是那么容易便攜 我有一個大約 100 行的sh腳本,可以在非常廣泛的 Unix 平台上運行:我自 1988 年以來使用過的任何系統。

關鍵要素是

  • uname -p處理器類型,但在現代 Unix 平台上通常是unknown

  • uname -m將在某些 Unix 系統上給出“機器硬件名稱”。

  • /bin/arch ,如果存在,通常會給出處理器的類型。

  • 不帶參數的uname將命名操作系統。

最終,您將不得不考慮平台之間的區別以及您想讓它們做得多好。 例如,為了簡單i686 ,我將i386i686 、任何“ Pentium* ”和任何“ AMD*Athlon* ”都視為x86

我的~/.profile在啟動時運行一個腳本,它將一個變量設置為一個字符串,指示 CPU 和操作系統的組合。 我有特定於平台的binmanlib ,並include基於此設置的目錄。 然后我設置了一大堆環境變量。 例如,重新格式化郵件的shell 腳本可以調用例如$LIB/mailfmt ,它是特定於平台的可執行二進制文件。

如果你想偷工減料uname -m和普通的uname會告訴你你想在許多平台上知道什么。 需要時添加其他內容。 (和用caseif不是嵌套的!)

我建議使用這個完整的 bash 代碼

lowercase(){
    echo "$1" | sed "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/"
}

OS=`lowercase \`uname\``
KERNEL=`uname -r`
MACH=`uname -m`

if [ "{$OS}" == "windowsnt" ]; then
    OS=windows
elif [ "{$OS}" == "darwin" ]; then
    OS=mac
else
    OS=`uname`
    if [ "${OS}" = "SunOS" ] ; then
        OS=Solaris
        ARCH=`uname -p`
        OSSTR="${OS} ${REV}(${ARCH} `uname -v`)"
    elif [ "${OS}" = "AIX" ] ; then
        OSSTR="${OS} `oslevel` (`oslevel -r`)"
    elif [ "${OS}" = "Linux" ] ; then
        if [ -f /etc/redhat-release ] ; then
            DistroBasedOn='RedHat'
            DIST=`cat /etc/redhat-release |sed s/\ release.*//`
            PSUEDONAME=`cat /etc/redhat-release | sed s/.*\(// | sed s/\)//`
            REV=`cat /etc/redhat-release | sed s/.*release\ // | sed s/\ .*//`
        elif [ -f /etc/SuSE-release ] ; then
            DistroBasedOn='SuSe'
            PSUEDONAME=`cat /etc/SuSE-release | tr "\n" ' '| sed s/VERSION.*//`
            REV=`cat /etc/SuSE-release | tr "\n" ' ' | sed s/.*=\ //`
        elif [ -f /etc/mandrake-release ] ; then
            DistroBasedOn='Mandrake'
            PSUEDONAME=`cat /etc/mandrake-release | sed s/.*\(// | sed s/\)//`
            REV=`cat /etc/mandrake-release | sed s/.*release\ // | sed s/\ .*//`
        elif [ -f /etc/debian_version ] ; then
            DistroBasedOn='Debian'
            DIST=`cat /etc/lsb-release | grep '^DISTRIB_ID' | awk -F=  '{ print $2 }'`
            PSUEDONAME=`cat /etc/lsb-release | grep '^DISTRIB_CODENAME' | awk -F=  '{ print $2 }'`
            REV=`cat /etc/lsb-release | grep '^DISTRIB_RELEASE' | awk -F=  '{ print $2 }'`
        fi
        if [ -f /etc/UnitedLinux-release ] ; then
            DIST="${DIST}[`cat /etc/UnitedLinux-release | tr "\n" ' ' | sed s/VERSION.*//`]"
        fi
        OS=`lowercase $OS`
        DistroBasedOn=`lowercase $DistroBasedOn`
        readonly OS
        readonly DIST
        readonly DistroBasedOn
        readonly PSUEDONAME
        readonly REV
        readonly KERNEL
        readonly MACH
    fi

fi
echo $OS
echo $KERNEL
echo $MACH

更多示例示例: https : //github.com/coto/server-easy-install/blob/master/lib/core.sh

我建議避免其中一些答案。 不要忘記,您可以選擇其他形式的字符串比較,這將清除大多數變體或提供的丑陋代碼。

一個這樣的解決方案是一個簡單的檢查,例如:

if [[ "$OSTYPE" =~ ^darwin ]]; then

盡管它是版本后綴,但它具有匹配任何版本的 Darwin 的額外好處。 這也適用於人們可能期望的任何Linux變體。

您可以在此處查看我的 dotfiles 中的一些其他示例

嘗試使用“uname”。 例如,在 Linux 中:“uname -a”。

根據手冊頁,uname 符合 SVr4 和 POSIX,因此它應該也適用於 Mac OS X 和Cygwin ,但我無法確認。

順便說一句:$OSTYPE 也在這里設置為linux-gnu :)

在 bash 中,使用$OSTYPE$HOSTTYPE ,如文檔所述; 這就是我所做的。 如果這還不夠,而且即使是unameuname -a (或其他適當的選項)也沒有提供足夠的信息,那么總會有 GNU 項目中的config.guess腳本,正是為此目的而制作的。

我在我的.bashrc寫了這些糖:

if_os () { [[ $OSTYPE == *$1* ]]; }
if_nix () { 
    case "$OSTYPE" in
        *linux*|*hurd*|*msys*|*cygwin*|*sua*|*interix*) sys="gnu";;
        *bsd*|*darwin*) sys="bsd";;
        *sunos*|*solaris*|*indiana*|*illumos*|*smartos*) sys="sun";;
    esac
    [[ "${sys}" == "$1" ]];
}

所以我可以做這樣的事情:

if_nix gnu && alias ls='ls --color=auto' && export LS_COLORS="..."
if_nix bsd && export CLICOLORS=on && export LSCOLORS="..."
if_os linux && alias psg="ps -FA | grep" #alternative to pgrep
if_nix bsd && alias psg="ps -alwx | grep -i" #alternative to pgrep
if_os darwin && alias finder="open -R"
uname

或者

uname -a

如果你想了解更多信息

您可以使用以下內容:

OS=$(uname -s)

然后您可以在腳本中使用 OS 變量。

我編寫了一個個人 Bash 庫和腳本框架,它使用GNU shtool來進行相當准確的平台檢測。

GNU shtool 是一組非常可移植的腳本,其中包含“shtool 平台”命令等有用的東西。 這是輸出:

shtool platform -v -F "%sc (%ac) %st (%at) %sp (%ap)"

在幾台不同的機器上:

Mac OS X Leopard: 
    4.4BSD/Mach3.0 (iX86) Apple Darwin 9.6.0 (i386) Apple Mac OS X 10.5.6 (iX86)

Ubuntu Jaunty server:
    LSB (iX86) GNU/Linux 2.9/2.6 (i686) Ubuntu 9.04 (iX86)

Debian Lenny:
    LSB (iX86) GNU/Linux 2.7/2.6 (i686) Debian GNU/Linux 5.0 (iX86)

如您所見,這會產生非常令人滿意的結果。 GNU shtool 有點慢,所以我實際上在我的腳本調用的系統上的一個文件中存儲和更新平台標識。 這是我的框架,所以對我有用,但你的里程可能會有所不同。

現在,您必須找到一種方法將 shtool 與您的腳本打包在一起,但這並不難。 您也可以隨時使用 uname 輸出。

編輯:

我錯過了 Teddy 關於config.guess的帖子(不知何故)。 這些是非常相似的腳本,但並不相同。 我個人也將 shtool 用於其他用途,它對我來說效果很好。

這應該可以安全地用於所有發行版。

$ cat /etc/*release

這會產生類似的東西。

     DISTRIB_ID=LinuxMint
     DISTRIB_RELEASE=17
     DISTRIB_CODENAME=qiana
     DISTRIB_DESCRIPTION="Linux Mint 17 Qiana"
     NAME="Ubuntu"
     VERSION="14.04.1 LTS, Trusty Tahr"
     ID=ubuntu
     ID_LIKE=debian
     PRETTY_NAME="Ubuntu 14.04.1 LTS"
     VERSION_ID="14.04"
     HOME_URL="http://www.ubuntu.com/"
     SUPPORT_URL="http://help.ubuntu.com/"
     BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"

根據需要提取/分配給變量

注意:在某些設置上。 這也可能會給您帶來一些您可以忽略的錯誤。

     cat: /etc/upstream-release: Is a directory

下面是一種檢測基於 Debian 和 RedHat 的Linux 操作系統的方法,該方法使用/etc/lsb-release/etc/os-release (取決於您使用的 Linux 版本)並基於它采取簡單的操作。

#!/bin/bash
set -e

YUM_PACKAGE_NAME="python python-devl python-pip openssl-devel"
DEB_PACKAGE_NAME="python2.7 python-dev python-pip libssl-dev"

 if cat /etc/*release | grep ^NAME | grep CentOS; then
    echo "==============================================="
    echo "Installing packages $YUM_PACKAGE_NAME on CentOS"
    echo "==============================================="
    yum install -y $YUM_PACKAGE_NAME
 elif cat /etc/*release | grep ^NAME | grep Red; then
    echo "==============================================="
    echo "Installing packages $YUM_PACKAGE_NAME on RedHat"
    echo "==============================================="
    yum install -y $YUM_PACKAGE_NAME
 elif cat /etc/*release | grep ^NAME | grep Fedora; then
    echo "================================================"
    echo "Installing packages $YUM_PACKAGE_NAME on Fedorea"
    echo "================================================"
    yum install -y $YUM_PACKAGE_NAME
 elif cat /etc/*release | grep ^NAME | grep Ubuntu; then
    echo "==============================================="
    echo "Installing packages $DEB_PACKAGE_NAME on Ubuntu"
    echo "==============================================="
    apt-get update
    apt-get install -y $DEB_PACKAGE_NAME
 elif cat /etc/*release | grep ^NAME | grep Debian ; then
    echo "==============================================="
    echo "Installing packages $DEB_PACKAGE_NAME on Debian"
    echo "==============================================="
    apt-get update
    apt-get install -y $DEB_PACKAGE_NAME
 elif cat /etc/*release | grep ^NAME | grep Mint ; then
    echo "============================================="
    echo "Installing packages $DEB_PACKAGE_NAME on Mint"
    echo "============================================="
    apt-get update
    apt-get install -y $DEB_PACKAGE_NAME
 elif cat /etc/*release | grep ^NAME | grep Knoppix ; then
    echo "================================================="
    echo "Installing packages $DEB_PACKAGE_NAME on Kanoppix"
    echo "================================================="
    apt-get update
    apt-get install -y $DEB_PACKAGE_NAME
 else
    echo "OS NOT DETECTED, couldn't install package $PACKAGE"
    exit 1;
 fi

exit 0

Ubuntu Linux 的輸出示例

delivery@delivery-E5450$ sudo sh detect_os.sh
[sudo] password for delivery: 
NAME="Ubuntu"
===============================================
Installing packages python2.7 python-dev python-pip libssl-dev on Ubuntu
===============================================
Ign http://dl.google.com stable InRelease
Get:1 http://dl.google.com stable Release.gpg [916 B]                          
Get:2 http://dl.google.com stable Release [1.189 B] 
...            

您可以使用以下 if 子句並根據需要擴展它:

if [ "${OSTYPE//[0-9.]/}" == "darwin" ]
then
    aminute_ago="-v-1M"
elif  [ "${OSTYPE//[0-9.]/}" == "linux-gnu" ]
then
    aminute_ago="-d \"1 minute ago\""
fi

嘗試這個:

DISTRO=$(cat /etc/*-release | grep -w NAME | cut -d= -f2 | tr -d '"')
echo "Determined platform: $DISTRO"

我傾向於將我的 .bashrc 和 .bash_alias 保存在所有平台都可以訪問的文件共享上。 這就是我在 .bash_alias 中解決問題的方法:

if [[ -f (name of share)/.bash_alias_$(uname) ]]; then
    . (name of share)/.bash_alias_$(uname)
fi

例如,我有一個 .bash_alias_Linux :

alias ls='ls --color=auto'

這樣我將特定於平台的代碼和可移植的代碼分開,你可以對 .bashrc 做同樣的事情

我在幾個 Linux 發行版中嘗試了上述消息,發現以下最適合我。 這是一個簡短、准確的單詞答案,也適用於 Windows 上的 Bash。

OS=$(cat /etc/*release | grep ^NAME | tr -d 'NAME="') #$ echo $OS # Ubuntu

這會檢查一堆known文件以識別 linux 發行版是 Debian 還是 Ubunu,然后它默認為$OSTYPE變量。

os='Unknown'
unamestr="${OSTYPE//[0-9.]/}"
os=$( compgen -G "/etc/*release" > /dev/null  && cat /etc/*release | grep ^NAME | tr -d 'NAME="'  ||  echo "$unamestr")

echo "$os"

如果有人對檢測 WSL 與 WSL 版本 2 感興趣,這就是我使用的方法。

#!/usr/bin/env bash

unameOut=$(uname -a)
case "${unameOut}" in
    *Microsoft*)     OS="WSL";; #must be first since Windows subsystem for linux will have Linux in the name too
    *microsoft*)     OS="WSL2";; #WARNING: My v2 uses ubuntu 20.4 at the moment slightly different name may not always work
    Linux*)     OS="Linux";;
    Darwin*)    OS="Mac";;
    CYGWIN*)    OS="Cygwin";;
    MINGW*)     OS="Windows";;
    *Msys)     OS="Windows";;
    *)          OS="UNKNOWN:${unameOut}"
esac

echo ${OS};

執行以下操作有助於正確執行 ubuntu 的檢查:

if [[ "$OSTYPE" =~ ^linux ]]; then
    sudo apt-get install <some-package>
fi

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM