【发布时间】:2011-03-28 19:50:54
【问题描述】:
我有一个在 Windows/Cygwin 和 Mac 和 Linux 上都使用的 shell 脚本。每个版本需要稍微不同的变量。
shell/bash 脚本如何检测它是在 Cygwin、Mac 还是 Linux 中运行?
【问题讨论】:
标签: bash shell cross-platform cygwin
我有一个在 Windows/Cygwin 和 Mac 和 Linux 上都使用的 shell 脚本。每个版本需要稍微不同的变量。
shell/bash 脚本如何检测它是在 Cygwin、Mac 还是 Linux 中运行?
【问题讨论】:
标签: bash shell cross-platform cygwin
通常,uname 及其各种选项会告诉您正在运行的环境:
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
并且,根据非常有用的schot(在 cmets 中),uname -s 为 OSX 提供 Darwin,为 Linux 提供 Linux,而我的 Cygwin 提供 CYGWIN_NT-5.1。但是您可能必须尝试各种不同的版本。
因此,执行此类检查的 bash 代码如下:
unameOut="$(uname -s)"
case "${unameOut}" in
Linux*) machine=Linux;;
Darwin*) machine=Mac;;
CYGWIN*) machine=Cygwin;;
MINGW*) machine=MinGw;;
*) machine="UNKNOWN:${unameOut}"
esac
echo ${machine}
请注意,我在这里假设您实际上是在在 CygWin(它的bash shell)中运行,因此应该已经正确设置了路径。正如一位评论者所说,您可以运行 bash 程序,并从 cmd 本身传递脚本,这可能会导致未按需要设置路径。
如果您正在这样做,则您有责任确保调用正确的可执行文件(即 CygWin 的),可能通过事先修改路径或完全指定可执行文件位置(例如, /c/cygwin/bin/uname)。
【讨论】:
MINGW32_NT-6.1。此外,没有/cygdrive 前缀,只有/c 代表C:。
How can a shell/bash script detect ... 而另一个则可以。
#!/usr/bin/env bash而不是#!/bin/sh来防止/bin/sh链接到不同平台的不同默认shell引起的问题,否则会出现unexpected operator之类的错误,这就是我电脑上发生的事情(Ubuntu 64 bits 12.04)。expr 程序,除非你安装它,所以我只使用uname。uname获取系统信息(-s参数)。expr和substr处理字符串。ifeliffi进行匹配工作。uname -s 规范即可。#!/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" ] 可能更有意义。
"$(expr substr $(uname -s) 1 5)" 有点奇怪。有更多漂亮的方法可以做到这一点,例如:if [ `uname -s` == CYGWIN* ]; then。阅读:如果uname -s 以 CYGWIN 开头,那么...
if [[ $(uname -s) == CYGWIN* ]]; then 这样的双括号
uname -s 会产生“Linux”以外的其他内容?
使用 uname -s (--kernel-name),因为 Mac OS 和 Solaris。您也可以只使用 uname 而不使用任何参数,因为默认参数是 -s (--kernel-name)。
下面的 sn-p 不需要bash(即不需要#!/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
以下Makefile 的灵感来自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
...
另见this complete answer about uname -s and Makefile。
这个答案底部的对应表来自Wikipedia article about uname。请做出贡献以使其保持最新(编辑答案或发表评论)。您也可以更新 Wikipedia 文章并发表评论以通知我您的贡献;-)
Operating System uname -s Mac OS X Darwin Cygwin 32-bit (Win-XP) CYGWIN_NT-5.1 Cygwin 32-bit (Win-7 32-bit)@ 987654348@Cygwin 32-bit (Win-7 64-bit)CYGWIN_NT-6.1-WOW64 Cygwin 64-bit (Win-7 64-bit)CYGWIN_NT-6.1 kbd>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 @987654325@ Linux Android Linux coreutils Linux CentOS Linux Fedora Linux Gentoo @987654374 @Red Hat Linux Linux Linux Mint Linux openSUSE Linux Ubuntu Linux Unity Linux Linux
@987654385 @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 @ 987654420@MINIX Minix Solaris SunOS UWIN (64-bit Windows 7) UWIN-W7 SYS$UNIX:SH on OpenVMS IS/WB z/OS USS OS/390
@ 987654431@sn5176 (SCO) OpenServer SCO_SV (SCO) System V kbd>SCO_SV (SCO) UnixWare UnixWare IBM AIX AIX IBM i with QSH OS400 HP-UX HP-UX
【讨论】:
~/.profile(设置环境变量,如$PATH——评论为后代提供搜索关键字)。
uname -sr并在Linux*)之前与Linux*Microsoft)进行比较。
Bash 设置 shell 变量 OSTYPE。来自man bash:
自动设置为描述操作系统的字符串 哪个 bash 正在执行。
这与uname 相比有一个微小的优势,因为它不需要启动新进程,因此执行起来会更快。
但是,我找不到权威的期望值列表。对我来说,在 Ubuntu 14.04 上它设置为“linux-gnu”。我已经在网上搜索了一些其他值。因此:
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
星号在某些情况下很重要 - 例如 OSX 在“darwin”之后附加一个操作系统版本号。有人告诉我,'win' 值实际上是 'win32' - 也许有一个 'win64'?
也许我们可以一起工作,在这里填写一个验证值表:
linux-gnu
cygwin
msys
(如果与现有条目不同,请附加您的值)
【讨论】:
env | grep OSTYPE 下看到它,但你会在set | grep OSTYPE 下看到它
OSTYPE 变量 (conftypes.h) 在构建时使用 automake 的 OS 变量 (Makefile.in) 的精确副本进行配置。可以查阅 automake 的 lib/config.sub 文件以了解所有可用类型。
# This script fragment emits Cygwin rulez under bash/cygwin
if [[ $(uname -s) == CYGWIN* ]];then
echo Cygwin rulez
else
echo Unix is king
fi
如果uname -s命令的前6个字符为“CYGWIN”,则假定为cygwin系统
【讨论】:
if [ `uname -s` == CYGWIN* ]; then 看起来更好,效果也一样。
[[ $(uname -s) == CYGWIN* ]]。另请注意,在我们的例子中,扩展正则表达式更精确:[[ $(uname -s) =~ ^CYGWIN* ]]。
expr substr $(uname -s) 1 6 在 macOS 上会出现错误 (expr: syntax error)。
CYGWIN 之后的“任何字符”需要 .*。添加* 只会匹配额外的Ns。因此,[[ $(uname -s) =~ ^CYGWIN.*$ ]] 需要精确度,但对于我们的情况,[[ $(uname -s) =~ ^CYGWIN ]] 就足够了
基于 Albert 的回答,我喜欢使用 $COMSPEC 来检测 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
这避免了为$OS 解析Windows 名称的变体,以及解析uname 的变体,如MINGW、Cygwin 等。
背景:%COMSPEC% 是一个 Windows 环境变量,用于指定命令处理器(也称为 Windows shell)的完整路径。该变量的值通常为%SystemRoot%\system32\cmd.exe,其计算结果通常为C:\Windows\system32\cmd.exe。
【讨论】:
http://en.wikipedia.org/wiki/Uname
您将需要的所有信息。 Google 是您的朋友。
使用uname -s查询系统名称。
Darwin
CYGWIN_...
LINUX 最多【讨论】:
当被问到这个问题时,Linux 的 Windows 子系统并不存在。它在我的测试中给出了这些结果:
uname -s -> Linux
uname -o -> GNU/Linux
uname -r -> 4.4.0-17763-Microsoft
这意味着你需要 uname -r 来区分它和原生 Linux。
【讨论】:
好的,这是我的方式。
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 ^ $? ))
}
例如
osis Darwin &&
{
log_debug Detect mac osx
}
osis Linux &&
{
log_debug Detect linux
}
osis -n Cygwin &&
{
log_debug Not Cygwin
}
我在我的dotfiles 中使用它
【讨论】:
我猜 uname 的答案是无与伦比的,主要是在清洁度方面。
虽然执行起来需要很长的时间,但我发现测试特定文件的存在也能给我带来更好更快的结果,因为我没有调用可执行文件:
所以,
[ -f /usr/bin/cygwin1.dll ] && echo Yep, Cygwin running
只使用快速的 Bash 文件存在检查。由于我现在在 Windows 上,我无法告诉你任何适用于 Linux 和 Mac OS X 的特定文件,但我很确定它们确实存在。 :-)
【讨论】: