Как определить ОС из скрипта Bash?

Я хотел бы сохранить мой .bashrc а также .bash_login файлы в системе контроля версий, чтобы я мог использовать их между всеми компьютерами, которые я использую. Проблема в том, что у меня есть некоторые псевдонимы для конкретных ОС, поэтому я искал способ определить, работает ли скрипт в Mac OS X, Linux или Cygwin.

Как правильно определить операционную систему в скрипте Bash?

22 ответа

Решение

Для моего.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 Если вы предпочитаете.

Я думаю, что следующее должно работать. Я не уверен насчет 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

Страница руководства bash говорит, что переменная OSTYPE хранит имя операционной системы:

OSTYPE Автоматически устанавливается в строку, описывающую операционную систему, в которой выполняется bash. Значение по умолчанию зависит от системы.

Это установлено в linux-gnu Вот.

Вы можете просто использовать предопределенную переменную $OSTYPE т.е.

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

Другой метод заключается в обнаружении платформы на основе 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,

Определить операционную систему и тип процессора не так просто, как в переносном режиме. у меня есть sh скрипт длиной около 100 строк, работающий на самых разных платформах Unix: любая система, которую я использовал с 1988 года.

Ключевые элементы

  • uname -p тип процессора, но обычно unknown на современных платформах Unix.

  • uname -m в некоторых системах Unix выдаст "имя аппаратного обеспечения".

  • /bin/arch, если он существует, обычно дает тип процессора.

  • uname без аргументов назовем операционную систему.

В конце концов вам придется подумать о различиях между платформами и о том, как хорошо вы хотите их делать. Например, чтобы все было просто, я лечу i386 через i686, любой "Pentium*"и любой"AMD*Athlon*"все как x86,

мой ~/.profile запускает скрипт при запуске, который устанавливает одну переменную в строку, указывающую комбинацию процессора и операционной системы. У меня есть платформа для конкретной bin, man, lib, а также include каталоги, которые создаются на основе этого. Затем я установил множество переменных среды. Так, например, скрипт оболочки для переформатирования почты может вызывать, например, $LIB/mailfmt который является платформно-исполняемым двоичным файлом.

Если вы хотите срезать углы, uname -m и просто uname скажу вам, что вы хотите знать на многих платформах. Добавляйте другие вещи, когда вам это нужно. (И использовать caseне вложенный if!)

Я рекомендую использовать этот полный код 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

Я бы предложил избежать некоторых из этих ответов. Не забывайте, что вы можете выбрать другие формы сравнения строк, которые бы прояснили большинство вариантов или ужасный код.

Одним из таких решений будет простая проверка, такая как:

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

Который имеет дополнительное преимущество соответствия любой версии Darwin, несмотря на то, что это суффикс версии. Это также работает для любых вариантов Linux можно ожидать.

Вы можете увидеть некоторые дополнительные примеры в моих точечных файлах здесь

Это то, что я использую, если кто-то также заинтересован в обнаружении 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};
uname

или же

uname -a

если вы хотите больше информации

Попробуйте использовать "uname". Например, в Linux: "uname -a".

Согласно странице руководства, uname соответствует SVr4 и POSIX, поэтому он должен быть доступен и в Mac OS X и Cygwin, но я не могу это подтвердить.

Кстати: $OSTYPE также установлен в linux-gnu Вот:)

Ниже приведен подход к обнаружению ОС Linux на основе Debian и RedHat с использованием /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] 
...            

В bash используйте $OSTYPE а также $HOSTTYPE, как задокументировано; вот что я делаю. Если этого недостаточно, и если даже uname или же uname -a (или другие соответствующие параметры) не дают достаточно информации, всегда есть скрипт config.guess из проекта GNU, созданный именно для этой цели.

Я написал эти сахара в моем .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"

Вы можете использовать следующее:

OS=$(uname -s)

тогда вы можете использовать переменную ОС в вашем скрипте.

Попробуй это:

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

Я написал личную библиотеку 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 и для других целей, и он работал довольно хорошо для меня.

Это должно быть безопасно для использования на всех дистрибутивах.

$ 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

Вы можете использовать следующее условие 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

Это проверяет кучу 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"

Я стараюсь хранить мои.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 и обнаружил, что следующее работает лучше всего для меня. Это короткий, краткий и точный ответ, который также подходит для Bash в Windows.

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

Выполнение следующих действий помогло правильно выполнить проверку для Ubuntu:

if [[ "$OSTYPE" =~ ^linux ]]; then
    sudo apt-get install <some-package>
fi
Другие вопросы по тегам