如何从Bash脚本检测操作系统?


524

我想将我的文件.bashrc.bash_login文件保留在版本控制中,以便可以在所有使用的计算机之间使用它们。问题是我有一些特定于OS的别名,因此我正在寻找一种确定脚本是否在Mac OS X,Linux或Cygwin上运行的方法

Bash脚本中检测操作系统的正确方法是什么?


1
您是否曾经考虑过共享配置?我一直在寻找相同的设置方法:)
sorin 2012年

@sorin我知道这是一条旧评论,但是如果您仍然好奇,我一直在构建ProfileGem,它使您可以为所有计算机配置可插拔的bash环境。
dimo414 2014年

1
@ dimo414 profilegem似乎已移至此处bitbucket.org/dimo414/profilegem。至于一般的分享庆典CONFIGS,尝试一些这些点文件的项目:github.com/...
mahemoff

1
@mahemoff感谢您提供的更新链接,对不起,我无法编辑我的评论。如果您正在尝试ProfileGem,欢迎您提出反馈或发现问题!
dimo414

唉到位桶被拒绝了水银的支持,所以我要再次更新我的链接:( github.com/dimo414/ProfileGem是新的家庭。
dimo414

Answers:


521

我认为以下应该起作用。我不确定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

7
在Windows上,您将获得msysGit Bash / msysGit和cygwinCygwin
Friederbluemle 2014年

1
包括如何检测Windows在内的答案很好。我也在使用Mingw32,它显示为msys
d48 2014年

2
有趣的是。Mac OSX仍会给出“ darwin”。
jonathanbell,2015年

6
当您用命令替换注释时,让我感到震惊的是-确保以结尾;
Vadim Peretokin

1
当我添加类似的命令时source somefile ; ,我得到了syntax error near unexpected token elif
oarfish

291

对于我的.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


6
为什么要从unamestr设置平台,而不仅仅是使用unamestr?
csexton

80
不要使用反引号,而是使用更新的更清晰的语法:“ unamestr = $(uname)”。
放松

14
乍一看,反引号可能类似于弦。
Harrison Powers

29
@ david-winiecki因为如果您需要嵌套命令,例如$(command-1 $(command-2))
Michael Barton

3
这样做会更好platform="$(uname | tr '[:upper:]' '[:lower:]')"吗?然后一定要处理一个案子吗?硬编码对非常特定的输出的依赖项似乎很奇怪。
布兰登·巴克2015年

274

bash联机帮助页上说,变量OSTYPE存储了操作系统的名称:

OSTYPE自动设置为描述执行bash的操作系统的字符串。默认值取决于系统。

设置到linux-gnu这里。


5
另外,$ OSTYPE在我的Mac(Leopard)上为'darwin9.0',在Cygwin下为'cygwin'。
dF。

5
$ OSTYPE在SnowLeopard上为darwin10.0。附带版本的WTF?意味着一个简单的case语句将不起作用。
mxcl

16
删除它os=${OSTYPE//[0-9.]/}
没什么

86
case $OSTYPE in darwin*) echo I am a Mac ;; esac
2012年

36
@tripleee或适合使用“ if”语法的人:if [[ $OSTYPE == darwin* ]]; then echo I am a Mac;fi
Rican7

157

$OSTYPE

您可以简单地使用预定义$OSTYPE变量,例如:

case "$OSTYPE" in
  solaris*) echo "SOLARIS" ;;
  darwin*)  echo "OSX" ;; 
  linux*)   echo "LINUX" ;;
  bsd*)     echo "BSD" ;;
  msys*)    echo "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

1
$ OSTYPE检查缺少cygwin的分支,该分支表示Windows机器。在cygwin uname返回“ CYGWIN_NT-10.0”和在MinGW uname返回“ MINGW64_NT-10.0”时,uname检查也将无法正常工作。
Matt Pennington

1
别名ls ='/ bin / ls -G'实际上是安全的选择,在基于OS的操作系统中,请参阅superuser.com/a/1299969/84662
Andrei Neculau,

我建议使用uname,Oracle掩盖了约定,并在它们的后面放了一个免费版本字符串OSTYPE ==> solaris2.11uname清洁一点,减少工作量。

37

检测操作系统和CPU类型也不是那么容易做到的可移植性。我有一个sh大约100行的脚本,该脚本可在非常广泛的Unix平台上运行:自1988年以来我使用的任何系统。

关键要素是

  • uname -p处理器类型,但通常unknown在现代Unix平台上。

  • uname -m 在某些Unix系统上会给出“机器硬件名称”。

  • /bin/arch,如果存在,通常会给出处理器的类型。

  • uname 不带参数的名称将命名为操作系统。

最终,您将不得不考虑平台之间的区别以及您希望如何区分它们。 例如,只是为了让事情变得简单,我把i386通过i686,任何“ Pentium*”任何“ AMD*Athlon*”所有的x86

My ~/.profile在启动时运行一个脚本,该脚本将一个变量设置为一个字符串,该字符串指示CPU和操作系统的组合。我有特定于平台的binmanlib,和include基于该是那些获得建立目录。然后设置一系列环境变量。因此,例如,可以重新格式化邮件的外壳脚本可以调用,例如,$LIB/mailfmt它是特定于平台的可执行二进制文件。

如果您想偷工减料uname -mPlain uname会告诉您在许多平台上要了解的内容。在需要时添加其他内容。(并且使用case,而不是嵌套if!)


2
根据uname命令,不带参数的uname等同于使用“ -s”参数:“ -s显示系统名称。此标志默认情况下处于启用状态。”。明确地说,可以使用“ uname -s”代替“ uname”。(在对“ Shell输出帮助”的回答中进行了详细说明)
Peter Mortensen 2012年

您是否具有带有此100脚本的github git或pastebin的链接,以可靠地检测操作系统和处理器体系结构?
Andrew De Andrade 2013年

4
@AndrewDeAndrade我很to愧将此代码公开。pastebin.com/J66Lj6wf
Norman Ramsey

那又如何uname -s呢?
亚历山大·米尔斯

35

我建议使用此完整的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

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


1
您省略了小写功能,我为您更新了答案。
罗宾·克洛尔斯

1
一旦知道它是Linux,如果只是闲逛(而不是尝试自动查找),似乎最简单的方法就是公正地ls /etc寻找“ blah-release”(不管是什么),这就是Linux OS-CentOS,Ubuntu等等
克里斯·莫斯基尼

我进行编辑是因为对OS变量的某些引用被写为“ {$ OS}”,我将它们更改为“ $ {OS}”,这是正确的方式,我对其进行了测试
bazz 2014年

您是否考虑在脚本的最后添加“ echo $ {OS}”?
奥列格·科科林

10

在bash中,使用$OSTYPE$HOSTTYPE,如记录所示;这就是我要做的。如果这还不够,并且甚至unameuname -a(或其他适当的选项)没有提供足够的信息,则总是有GNU项目中的config.guess脚本,正是为此目的而编写的。


10

我建议避免使用某些答案。不要忘记,您可以选择其他形式的字符串比较,这将清除大多数变体或提供的丑陋代码。

这样的解决方案之一就是简单的检查,例如:

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

尽管具有版本后缀,但具有匹配任何版本的Darwin的额外好处。这也适用于任何Linux可能的变化。

您可以在此处在我的点文件中看到一些其他示例


9

尝试使用“ uname”。例如,在Linux中:“ uname -a”。

根据手册页,uname符合SVr4和POSIX,因此它也应在Mac OS X和Cygwin上可用,但我无法确认。

顺便说一句:$ OSTYPE也设置为linux-gnu这里:)


没有参数,uname在Mac OS X(豹)上打印'Darwin'。使用-a可以打印很多其他信息(内核版本,体系结构以及我无法解读的其他信息)。我不能在Cygwin上测试
dribeas大卫-罗德里格斯

8

我把这些糖写在我的.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"

顺便说一句,这9条线适用于在大多数设备上运行的任何Bash。没有外壳程序,几乎可以使用任何样式的个人.bashrc(大嵌套if / case树或大量随机别名)。IMO,把上面的每一个答案都打败了。
kfix 2015年

到目前为止,包括Cygwin和其他BSD(基本上是流行的* NIX的编译)最接近完成
HidekiAI 2016年

完成与今天相关的用户群并运行Bash。AIX / HP-UX / IRIX / OpenVMS用户可以发出声音。Linux的新Windows子系统可运行amd64 Ubuntu安装库存,因此对于if_nix gnu&都应返回true if_os linux
kfix

1
@kfix是的,对,win 10 bash子系统输出“ linux-gnu” echo $OSTYPE。为了检测赢10 bash的子系统而言,唯一的解决办法我已经找到了检查“微软”在/proc/sys/kernel/osrelease
Jpnh


7

我写了一个个人的Bash库和脚本框架,它使用GNU shtool进行了相当准确的平台检测。

GNU shtool是一组非常可移植的脚本,除其他有用的内容外,还包含“ shtool platform”命令。这是输出:

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输出。

编辑:

我错过了泰迪config.guess(以某种方式)的帖子。这些是非常相似的脚本,但不相同。我个人也将shtool用作其他用途,并且对我来说效果很好。



6

下面是一种使用/ etc / lsb-release/ etc / os-release(取决于您使用的Linux风格)来检测基于Debian和RedHat的Linux OS并基于此采取简单操作的方法。

#!/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] 
...            

5

在所有发行版上都应该可以安全使用。

$ 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

注意:在CentOS上,这会给[user@host ~]$ cat /etc/*release CentOS release 6.4 (Final) CentOS release 6.4 (Final) CentOS release 6.4 (Final)
cfstras

是的,我发布的输出只是一个示例,来自我的一台笔记本电脑,而不是随处可见的静态内容。
RJ

刚刚补充说,所以没有人会认为结果总是以相同的形式出现:)
cfstras 2014年

1
+1我在脚本的开头使用了set -e(因此该解决方案无法正常运行doxer.org/linux-shell-centos-debian-ostype)。其他解决方案并不专门针对CentOS。但是我发现这个参考证明了这个答案unix.stackexchange.com/a/92212
gihanchanuka 2014年

4
不适用于Mac:cat: /etc/*release: No such file or directory
eidal

3

尝试这个:

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

在macOS上,我得到cat: /etc/*-release: No such file or directory
csexton

@csexton真的,我的意思是一样的:zsh: no matches found: /etc/*release-我认为这仅适用于linux系统...
Alexander Nekrasov

当然,此答案仅适用于Linux。但是其他答案只能检测与其他系统相反的Linux。这个基本发现根本无济于事,因为发现特定的Linux发行版与区分FreeBSD和Darwin一样重要。
Thomas Urban

3

您可以使用以下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

2

我在几个Linux发行版中尝试了上述消息,发现以下内容最适合我。这是一个简短,简明的确切单词答案,也适用于Windows上的Bash。

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

2
尽管此代码段是受欢迎的,并且可能会提供一些帮助,但是如果它包含有关如何以及为什么可以解决此问题的说明,则可以大大改善。请记住,您将来会为读者回答问题,而不仅仅是现在问的人!请编辑您的答案以添加解释,并指出适用的限制和假设。
Toby Speight

1
对于大多数linux系统而言,它看起来不错而又简洁,但是不幸的是,这将导致macOS上的错误。也许这更像是“从Bash检测linux发行版的味道
csexton

1
@csexton,OS=$( ([[ -f /etc/*release ]] && cat /etc/*release | grep ^NAME | tr -d 'NAME="') || echo "Unknown" )它将正常工作而不会引发错误。
库泽科

实际上,这是一种正确的方法OS=$( $(compgen -G "/etc/*release" > /dev/null) && cat /etc/*release | grep ^NAME | tr -d 'NAME="') || $( echo "${OSTYPE//[0-9.]/}")
Kuzeko,

@Kuzeko不幸的是,我仍然在macOS上运行时遇到错误。
csexton '18 -10-1

1

我倾向于将.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执行相同操作


1

这会检查一堆known文件以识别linux发行版是Debian还是Ubunu,然后默认使用该$OSTYPE变量。

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

echo "$os"

-1

进行以下操作有助于正确检查ubuntu:

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

4
其他不使用apt-get的Linux系统呢?
Wilf 2015年

ubuntu是发行版,而不是操作系统类。
抖动
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.