Como verificar se está sendo executado no Cygwin, Mac ou Linux?

334

Eu tenho um script de shell que é usado no Windows / Cygwin e Mac e Linux. Ele precisa de variáveis ​​ligeiramente diferentes para cada versão.

Como um script shell / bash detecta se está sendo executado no Cygwin, em um Mac ou no Linux?

bastibe
fonte

Respostas:

322

Geralmente, unamecom suas várias opções, você informa em que ambiente está executando:

pax> uname -a
CYGWIN_NT-5.1 IBM-L3F3936 1.5.25(0.156/4/2) 2008-06-12 19:34 i686 Cygwin

pax> uname -s
CYGWIN_NT-5.1

E, de acordo com o muito útil schot(nos comentários), uname -sDarwinpara OSX e LinuxLinux, enquanto meu Cygwin dá CYGWIN_NT-5.1. Mas você pode ter que experimentar todos os tipos de versões diferentes.

Portanto, o bashcódigo para fazer essa verificação seria ao longo das linhas de:

unameOut="$(uname -s)"
case "${unameOut}" in
    Linux*)     machine=Linux;;
    Darwin*)    machine=Mac;;
    CYGWIN*)    machine=Cygwin;;
    MINGW*)     machine=MinGw;;
    *)          machine="UNKNOWN:${unameOut}"
esac
echo ${machine}

Note que eu estou assumindo aqui que você está realmente correndo dentro CygWin (o bashshell dele) para caminhos já deve estar configurado corretamente. Como observa um comentarista, você pode executar o bashprograma, transmitindo o script por cmdsi mesmo e isso pode resultar na não configuração dos caminhos conforme necessário.

Se você estiver fazendo isso, é sua responsabilidade garantir que os executáveis ​​corretos (ou seja, os CygWin) sejam chamados, possivelmente modificando o caminho antecipadamente ou especificando completamente os locais dos executáveis ​​(por exemplo, /c/cygwin/bin/uname).

paxdiablo
fonte
11
Às vezes, menos é mais;) BTW, a Wikipedia tem uma tabela de exemplo de saída uname em en.wikipedia.org/wiki/Uname
schot
A saída do Git Bash uname -s no Windows 7 é MINGW32_NT-6.1. Além disso, não há /cygdriveprefixo, apenas /cpara C:.
ColinM
7
Git Bash não é Cygwin. É MinGW, GNU mínimo para MS Windows, e é por isso que o comportamento é diferente.
Boyd Stephen Smith Jr.
Promovi a outra resposta, porque essa resposta não lida com a parte do OP How can a shell/bash script detect ...e a outra.
Jesse Chisholm
Se eu executar um script no cygwin executando "\ cygwin64 \ bin \ bash -c scriptname", isso não funcionará necessariamente. Nessa situação, o caminho do cygwin não é configurado e uname -sacaba chamando o que unamefor o primeiro no seu caminho atual, que no meu sistema é a versão instalada com a gedaqual retorna o texto WindowsNT. Poderia igualmente ser a versão MinGW, conforme descrito acima. Uma detecção confiável para o cygwin não deve depender do caminho que está sendo definido adequadamente, IMO. Portanto, $(uname -s)deve ser alterado $(/bin/uname -s)para detectar cygwin.
Jules
329

Aqui está o script bash que usei para detectar três tipos diferentes de SO (GNU / Linux, Mac OS X, Windows NT)

Preste atenção

  • No seu script bash, use em #!/usr/bin/env bashvez de #!/bin/shevitar o problema causado por /bin/shvinculado a diferentes shell padrão em plataformas diferentes, ou haverá um erro como operador inesperado , foi o que aconteceu no meu computador (Ubuntu 64 bits 12.04).
  • O Mac OS X 10.6.8 (Snow Leopard) não tem exprprograma, a menos que você o instale, então eu apenas o uso uname.

Projeto

  1. Use unamepara obter as informações do sistema ( -sparâmetro).
  2. Use expre substrpara lidar com a string.
  3. Use if elif fipara fazer o trabalho correspondente.
  4. Você pode adicionar mais suporte ao sistema, se quiser, basta seguir as uname -sespecificações.

Implementação

#!/usr/bin/env bash

if [ "$(uname)" == "Darwin" ]; then
    # Do something under Mac OS X platform        
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
    # Do something under GNU/Linux platform
elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW32_NT" ]; then
    # Do something under 32 bits Windows NT platform
elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then
    # Do something under 64 bits Windows NT platform
fi

Teste

  • O Linux (Ubuntu 12.04 LTS, Kernel 3.2.0) testou OK.
  • OS X (10.6.8 Snow Leopard) testou OK.
  • O Windows (Windows 7 de 64 bits) testou OK.

O que eu aprendi

  1. Verifique as cotações de abertura e fechamento.
  2. Verifique se não há parênteses e chaves {}

Referências

Albert
fonte
Para MinGW, pode fazer mais sentido para verificar: [ "$(expr substr $(uname -s) 1 10)" == "MINGW32_NT" ].
Achal Dave
2
@ Albert: a expressão "$(expr substr $(uname -s) 1 5)"é um pouco estranha. Há mais bonitas maneiras de fazer isso, por exemplo: if [ `uname -s` == CYGWIN* ]; then. Leia: Se uname -scomeça com CYGWIN então ...
David Ferenczy Rogožan
10
@DawidFerenczy Eu acredito que exigiria colchetes duplos comoif [[ $(uname -s) == CYGWIN* ]]; then
rogerdpack #
1
Isso não detecta o Cygwin, como foi solicitado na pergunta.
rmcclellan
2
Por que isso verifica apenas os 5 primeiros caracteres do Linux? Existem exemplos de distribuições modernas do Linux em que uname -strará algo diferente de "Linux"?
Ktbiz
127

Use uname -s( --kernel-name) porque uname -o( --operating-system) não é suportado em alguns sistemas operacionais, como Mac OS e Solaris . Você também pode usar apenas unamesem nenhum argumento, já que o argumento padrão é -s( --kernel-name).

O snippet abaixo não requer (ou seja, não requer #!/bin/bash)

#!/bin/sh

case "$(uname -s)" in

   Darwin)
     echo 'Mac OS X'
     ;;

   Linux)
     echo 'Linux'
     ;;

   CYGWIN*|MINGW32*|MSYS*|MINGW*)
     echo 'MS Windows'
     ;;

   # Add here more strings to compare
   # See correspondence table at the bottom of this answer

   *)
     echo 'Other OS' 
     ;;
esac

O abaixo Makefileé inspirado no projeto Git ( config.mak.uname) .

ifdef MSVC     # Avoid the MingW/Cygwin sections
    uname_S := Windows
else                          # If uname not available => 'not' 
    uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
endif

# Avoid nesting "if .. else if .. else .. endif endif"
# because maintenance of matching if/else/endif is a pain

ifeq ($(uname_S),Windows)
    CC := cl 
endif
ifeq ($(uname_S),OSF1)
    CFLAGS += -D_OSF_SOURCE
endif
ifeq ($(uname_S),Linux)
    CFLAGS += -DNDEBUG
endif
ifeq ($(uname_S),GNU/kFreeBSD)
    CFLAGS += -D_BSD_ALLOC
endif
ifeq ($(uname_S),UnixWare)
    CFLAGS += -Wextra
endif
...

Veja também esta resposta completa sobre uname -seMakefile .

A tabela de correspondência na parte inferior desta resposta é do artigouname da Wikipedia sobre . Contribua para mantê-lo atualizado (edite a resposta ou publique um comentário). Você também pode atualizar o artigo da Wikipedia e postar um comentário para me notificar sobre sua contribuição ;-)

Operating System uname -s
Mac OS X Darwin
Cygwin 32-bit (Win-XP) CYGWIN_NT-5.1
Cygwin 32-bit (Win-7 32-bit)CYGWIN_NT-6.1
Cygwin 32-bit (Win-7 64-bit)CYGWIN_NT-6.1-WOW64
Cygwin 64-bit (Win-7 64-bit)CYGWIN_NT-6.1
MinGW (Windows 7 32-bit) MINGW32_NT-6.1
MinGW (Windows 10 64-bit) MINGW64_NT-10.0
Interix (Services for UNIX) Interix
MSYS MSYS_NT-6.1
MSYS2 MSYS_NT-10.0-17763
Windows Subsystem for Linux Linux
Android Linux
coreutils Linux
CentOS Linux
Fedora Linux
Gentoo Linux
Red Hat Linux Linux
Linux Mint Linux
openSUSE Linux
Ubuntu Linux
Unity Linux Linux
Manjaro Linux Linux
OpenWRT r40420 Linux
Debian (Linux) Linux
Debian (GNU Hurd) GNU
Debian (kFreeBSD) GNU/kFreeBSD
FreeBSD FreeBSD
NetBSD NetBSD
DragonFlyBSD DragonFly
Haiku Haiku
NonStop NONSTOP_KERNEL
QNX QNX
ReliantUNIX ReliantUNIX-Y
SINIX SINIX-Y
Tru64 OSF1
Ultrix ULTRIX
IRIX 32 bits IRIX
IRIX 64 bits IRIX64
MINIX Minix
Solaris SunOS
UWIN (64-bit Windows 7) UWIN-W7
SYS$UNIX:SH on OpenVMS IS/WB
z/OS USS OS/390
Cray sn5176
(SCO) OpenServer SCO_SV
(SCO) System V SCO_SV
(SCO) UnixWare UnixWare
IBM AIX AIX
IBM i with QSH OS400
HP-UX HP-UX

olibre
fonte
3
Polegares para cima para Solaris e pesquisa acima.
Okutane
Oi @okutane. Eu não entendo o que você quer dizer. Forneça mais detalhes. Você sugere alguma coisa? Cheers
olibre
3
Estou votando, é isso.
okutane
1
Era exatamente isso que eu procurava para escrever uma plataforma portátil / multiplataforma ~/.profile(para definir variáveis ​​de ambiente como $PATH- comentar para fornecer palavras-chave de pesquisa para posteridade).
Braham Snyder
1
Eu vim aqui porque queria detectar o WSL especificamente e diferenciá-lo de outros Linux. O que parece funcionar para mim, então, é verificar uname -sre comparar Linux*Microsoft)antes Linux*).
einarmagnus
65

Bash define a variável do shell OSTYPE. De man bash:

Configure automaticamente como uma sequência que descreva o sistema operacional no qual o bash está sendo executado.

Isso tem uma pequena vantagem uname, pois não requer o lançamento de um novo processo, portanto, será mais rápido para executar.

No entanto, não consigo encontrar uma lista autorizada de valores esperados. Para mim, no Ubuntu 14.04, está definido como 'linux-gnu'. Raspei a web em busca de outros valores. Conseqüentemente:

case "$OSTYPE" in
  linux*)   echo "Linux / WSL" ;;
  darwin*)  echo "Mac OS" ;; 
  win*)     echo "Windows" ;;
  msys*)    echo "MSYS / MinGW / Git Bash" ;;
  cygwin*)  echo "Cygwin" ;;
  bsd*)     echo "BSD" ;;
  solaris*) echo "Solaris" ;;
  *)        echo "unknown: $OSTYPE" ;;
esac

Os asteriscos são importantes em alguns casos - por exemplo, o OSX anexa um número de versão do SO após o 'darwin'. O valor 'win' é realmente 'win32', disseram-me - talvez exista um 'win64'?

Talvez possamos trabalhar juntos para preencher uma tabela de valores verificados aqui:

  • Linux Ubuntu (incl. WSL ):linux-gnu
  • Cygwin de 64 bits: cygwin
  • Msys / MINGW (Git Bash para Windows): msys

(Anexe seu valor se ele diferir das entradas existentes)

Jonathan Hartley
fonte
1
Já como sua resposta mais do que a minha, se encaixa perfeitamente nas raras vezes que eu precisava disso
Charles Roberto Canato
6
Tecnicamente, não é uma variável de ambiente, é uma variável de shell. É por isso que você não vai vê-lo em env | grep OSTYPE, mas você vai vê-lo sobset | grep OSTYPE
wisbucky
4
Para os interessados, a OSTYPEvariável do Bash (conftypes.h) é configurada no tempo de construção usando a cópia exata da OSvariável do automake (Makefile.in) . Pode-se consultar o arquivo lib / config.sub da automake para todos os tipos disponíveis.
jdknight
9

Para aproveitar a resposta de Albert, eu gosto de usar $COMSPECpara detectar o Windows:

#!/bin/bash

if [ "$(uname)" == "Darwin" ]
then
 echo Do something under Mac OS X platform
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]
then
  echo Do something under Linux platform
elif [ -n "$COMSPEC" -a -x "$COMSPEC" ]
then 
  echo $0: this script does not support Windows \:\(
fi

Isso evita analisar variantes de nomes do Windows $OSe analisar variantes de unamecomo MINGW, Cygwin etc.

Background: %COMSPEC%é uma variável de ambiente do Windows que especifica o caminho completo para o processador de comandos (também conhecido como shell do Windows). O valor dessa variável é tipicamente %SystemRoot%\system32\cmd.exe, que normalmente é avaliado como C:\Windows\system32\cmd.exe.

Steve Jansen
fonte
Não corrija mais, pois $ COMSPEC não está definido ao executar no ambiente do Windows UWS Bash.
Julian Knight
9
# This script fragment emits Cygwin rulez under bash/cygwin
if [[ $(uname -s) == CYGWIN* ]];then
    echo Cygwin rulez
else 
    echo Unix is king
fi

Se os 6 primeiros caracteres do comando uname -s forem "CYGWIN", será assumido um sistema cygwin

Jan Helge
fonte
if [ `uname -s` == CYGWIN* ]; thenparece melhor e funciona da mesma maneira.
David Ferenczy Rogožan
6
Sim, mas usar colchetes duplos: [[ $(uname -s) == CYGWIN* ]]. Note-se também que as expressões regulares estendidas são mais precisos no nosso caso: [[ $(uname -s) =~ ^CYGWIN* ]].
Andreas Spindler
Acima funciona melhor, porque expr substr $(uname -s) 1 6gera um erro ( expr: syntax error) no macOS.
Doekman #
2

Ok, aqui está o meu caminho.

osis()
{
    local n=0
    if [[ "$1" = "-n" ]]; then n=1;shift; fi

    # echo $OS|grep $1 -i >/dev/null
    uname -s |grep -i "$1" >/dev/null

    return $(( $n ^ $? ))
}

por exemplo

osis Darwin &&
{
    log_debug Detect mac osx
}
osis Linux &&
{
    log_debug Detect linux
}
osis -n Cygwin &&
{
    log_debug Not Cygwin
}

Eu uso isso nos meus dotfiles

Wener
fonte
2

http://en.wikipedia.org/wiki/Uname

Toda a informação que você precisa. Google é seu amigo.

Use uname -spara consultar o nome do sistema.

  • Mac: Darwin
  • Cygwin: CYGWIN_...
  • Linux: vários, LINUXpara a maioria
rubenvb
fonte
2

O Subsistema Windows para Linux não existia quando essa pergunta foi feita. Deu estes resultados no meu teste:

uname -s -> Linux
uname -o -> GNU/Linux
uname -r -> 4.4.0-17763-Microsoft

Isso significa que você precisa de uname -r para diferenciá-lo do Linux nativo.

Um nevoeiro
fonte
1
Infelizmente, o Mingw-w64 oferece exatamente o mesmo.
A Fog
1

Eu acho que a resposta uname é imbatível, principalmente em termos de limpeza.

Embora demore um tempo ridículo para executar, descobri que o teste para a presença de arquivos específicos também me fornece resultados bons e mais rápidos, pois não estou invocando um executável:

Assim,

[ -f /usr/bin/cygwin1.dll ] && echo Yep, Cygwin running

apenas usa uma verificação rápida de presença de arquivo Bash. Como estou no Windows no momento, não posso informar nenhum arquivo específico para Linux e Mac OS X, mas tenho certeza de que eles existem. :-)

Charles Roberto Canato
fonte
-3

Use apenas isso na linha de comando, funciona muito bem, graças a Justin:

#!/bin/bash

################################################## #########
# Bash script to find which OS
################################################## #########

OS=`uname`
echo "$OS"

fonte

lizardhr
fonte
O que há de novo no seu script?
mallaudin 21/02/19