I have a shell script that is used both on Windows/Cygwin and Mac and Linux. It needs slightly different variables for each versions.
How can a shell/bash script detect whether it is running in Cygwin, on a Mac or in Linux?
I have a shell script that is used both on Windows/Cygwin and Mac and Linux. It needs slightly different variables for each versions.
How can a shell/bash script detect whether it is running in Cygwin, on a Mac or in Linux?
Usually, uname
with its various options will tell you what environment you're running in:
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
And, according to the very helpful schot
(in the comments), uname -s
gives Darwin
for OSX and Linux
for Linux, while my Cygwin gives CYGWIN_NT-5.1
. But you may have to experiment with all sorts of different versions.
So the bash
code to do such a check would be along the lines of:
unameOut="$(uname -s)"
case "${unameOut}" in
Linux*) machine=Linux;;
Darwin*) machine=Mac;;
CYGWIN*) machine=Cygwin;;
MINGW*) machine=MinGw;;
MSYS_NT*) machine=Git;;
*) machine="UNKNOWN:${unameOut}"
esac
echo ${machine}
Note that I'm assuming here that you're actually running within CygWin (the bash
shell of it) so paths should already be correctly set up. As one commenter notes, you can run the bash
program, passing the script, from cmd
itself and this may result in the paths not being set up as needed.
If you are doing that, it's your responsibility to ensure the correct executables (i.e., the CygWin ones) are being called, possibly by modifying the path beforehand or fully specifying the executable locations (e.g., /c/cygwin/bin/uname
).
MINGW32_NT-6.1
. Also, there is no /cygdrive
prefix, just /c
for C:
. –
Romaine How can a shell/bash script detect ...
and the other does. –
Glowing uname -s
ends up calling whatever uname
is first in your current path, which on my system turns out to be the version installed with geda
which returns the text WindowsNT
. It could equally be the MinGW version as described above, though. A reliable detection for cygwin must not rely on the path being set appropriately, IMO. Therefore, $(uname -s)
should be changed to $(/bin/uname -s)
to detect cygwin. –
Oteliaotero bash
shell of it (further supported by the tags). In that case, the paths should already be correctly set. While the path should be able to reach out for Windows executables, they should be after all the CygWin stuff. In any case, I'll clarify the answer. –
Pina #!/usr/bin/env bash
instead of #!/bin/sh
to prevent the problem caused by /bin/sh
linked to different default shell in different platforms, or there will be error like unexpected operator, that's what happened on my computer (Ubuntu 64 bits 12.04).expr
program unless you install it, so I just use uname
.uname
to get the system information (-s
parameter).expr
and substr
to deal with the string.if
elif
fi
to do the matching job.uname -s
specification.#!/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
[ "$(expr substr $(uname -s) 1 10)" == "MINGW32_NT" ]
. –
Bordelon "$(expr substr $(uname -s) 1 5)"
is weird a bit. There are more pretty ways to do that, for example: if [ `uname -s` == CYGWIN* ]; then
. Read it: if uname -s
starts with CYGWIN then... –
Dispend if [[ $(uname -s) == CYGWIN* ]]; then
–
Viole uname -s
will yield something other than "Linux"? –
Continue MINGW64_NT
instead of MINGW32_NT
. –
Georgenegeorges Use uname -s
(--kernel-name
) because uname -o
(--operating-system
) is not supported on some Operating Systems such as Mac OS and Solaris. You may also use just uname
without any argument since the default argument is -s
(--kernel-name
).
To distinguish WSL from Linux, einarmagnus recommends uname -sr
(--kernel-name --kernel-release
) as proposed in the following script. 😉
#!/bin/sh
case "$(uname -sr)" in
Darwin*)
echo 'Mac OS X'
;;
Linux*Microsoft*)
echo 'WSL' # Windows Subsystem for Linux
;;
Linux*)
echo 'Linux'
;;
CYGWIN*|MINGW*|MINGW32*|MSYS*)
echo 'MS Windows'
;;
# Add here more strings to compare
# See correspondence table at the bottom of this answer
*)
echo 'Other OS'
;;
esac
The following Makefile
is inspired from Git project (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
...
See also this complete answer about uname -s
and Makefile
.
The correspondence table in the bottom of this answer is from Wikipedia article about uname
. Please contribute to keep it up-to-date (edit the answer or post a comment). You may also update the Wikipedia article and post a comment to notify me about your contribution ;-)
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 |
OpenBSD | OpenBSD |
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 |
~/.profile
(to set environment variables like $PATH
-- commenting to provide search keywords for posterity). –
Interlock uname
sample. I suppose uname -s
displays Linux
on Windows Subsystem for Linux, isn't it? Can you confirm? –
Penalty DESKTOP-9ETGU1K 4.4.0-17134-Microsoft #285-Microsoft Thu Aug 30 17:31:00 PST 2018 x86_64 x86_64 x86_64 GNU/Linux
–
Aaronson uname -sr
and compare against Linux*Microsoft)
before Linux*)
. –
Hamblin Bash sets the shell variable OSTYPE. From man bash
:
Automatically set to a string that describes the operating system on which bash is executing.
This has a tiny advantage over uname
in that it doesn't require launching a new process, so will be quicker to execute.
However, I'm unable to find an authoritative list of expected values. For me on Ubuntu 14.04 it is set to 'linux-gnu'. I've scraped the web for some other values. Hence:
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
The asterisks are important in some instances - for example OSX appends an OS version number after the 'darwin'. The 'win' value is actually 'win32', I'm told - maybe there is a 'win64'?
Perhaps we could work together to populate a table of verified values here:
linux-gnu
cygwin
msys
darwin22.0
(Please append your value if it differs from existing entries)
env | grep OSTYPE
, but you will see it under set | grep OSTYPE
–
Bela OSTYPE
variable (conftypes.h) is configured at build time using the exact copy of automake's OS
variable (Makefile.in). One may consult automake's lib/config.sub file for all of the available types. –
Ot linux-gnu
. –
Claus # This script fragment emits Cygwin rulez under bash/cygwin
if [[ $(uname -s) == CYGWIN* ]];then
echo Cygwin rulez
else
echo Unix is king
fi
If the 6 first chars of uname -s command is "CYGWIN", a cygwin system is assumed
if [ `uname -s` == CYGWIN* ]; then
looks better and works the same. –
Dispend [[ $(uname -s) == CYGWIN* ]]
. Note also that extended regular expressions are more precise in our case: [[ $(uname -s) =~ ^CYGWIN* ]]
. –
Calendre expr substr $(uname -s) 1 6
gives an error (expr: syntax error
) on macOS. –
Kuykendall CYGWIN
requires .*
. Adding *
will only match extra N
s. So [[ $(uname -s) =~ ^CYGWIN.*$ ]]
is needed for precision, but for our case [[ $(uname -s) =~ ^CYGWIN ]]
would suffice –
Imprudent To build upon Albert's answer, I like to use $COMSPEC
for detecting 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
This avoids parsing variants of Windows names for $OS
, and parsing variants of uname
like MINGW, Cygwin, etc.
Background: %COMSPEC%
is a Windows environmental variable specifying the full path to the command processor (aka the Windows shell). The value of this variable is typically %SystemRoot%\system32\cmd.exe
, which typically evaluates to C:\Windows\system32\cmd.exe
.
https://en.wikipedia.org/wiki/Uname
All the info you'll ever need. Google is your friend.
Use uname -s
to query the system name.
Darwin
CYGWIN_...
LINUX
for mostWindows Subsystem for Linux did not exist when this question was asked. It gave these results in my test:
uname -s -> Linux
uname -o -> GNU/Linux
uname -r -> 4.4.0-17763-Microsoft
This means that you need uname -r to distinguish it from native Linux.
Ok, here is my way.
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 ^ $? ))
}
e.g.
osis Darwin &&
{
log_debug Detect mac osx
}
osis Linux &&
{
log_debug Detect linux
}
osis -n Cygwin &&
{
log_debug Not Cygwin
}
I use this in my dotfiles
I guess the uname answer is unbeatable, mainly in terms of cleanliness.
Although it takes a ridiculous time to execute, I found that testing for specific files presence also gives me good and quicker results, since I'm not invoking an executable:
So,
[ -f /usr/bin/cygwin1.dll ] && echo Yep, Cygwin running
just uses a quick Bash file presence check. As I'm on Windows right now, I can't tell you any specific files for Linuxes and Mac OS X from my head, but I'm pretty sure they do exist. :-)
© 2022 - 2024 — McMap. All rights reserved.