45

Pretty straightforward, the usual places to figure out the OS you're on seem to be identical to plain Ubuntu on Ubuntu for Windows. For example uname -a is identical to a native GNU/Linux install and /etc/os-version is identical to a Ubuntu Trusty Tahr install.

The only thing I can think of is to check if /mnt/c/Windows exists, but I'm not sure if that's a foolproof idea.

Tim
  • 4,354
  • 3
  • 30
  • 41
DrKabob
  • 451
  • 1
  • 5
  • 3

14 Answers14

46

The following works in bash on Windows 10, macOS, and Linux:

#!/bin/bash
set -e
if grep -qEi "(Microsoft|WSL)" /proc/version &> /dev/null ; then
    echo "Windows 10 Bash"
else
    echo "Anything else"
fi

You need to check for both "Microsoft" and "WSL" per this comment by Ben Hillis, WSL Developer:

For the time being this is probably the best way to do it. I can't promise that we'll never change the content of these ProcFs files, but I think it's unlikely we'll change it to something that doesn't contain "Microsoft" or "WSL".

/proc/sys/kernel/osrelease
/proc/version

And case shall be ignored for grep. In WSL2, /proc/version gives lowercased microsoft.

Atif
  • 315
  • 1
  • 4
  • 16
Gary S. Weaver
  • 7,806
  • 4
  • 36
  • 60
  • 3
    Don't need grep, `if [[ "$(< /proc/version)" == *@(Microsoft|WSL)* ]]; then ...` is faster. – Niklas Holm Jan 11 '19 at 07:58
  • 8
    Commenting to add that in WSL 2 it says "microsoft", all lowercase. – Marc Cornellà Sep 19 '19 at 18:16
  • 1
    Beware! parsing /proc/version, it could contain misleading info (imagine a *gcc compiled by microsoft*). It is better to parse only the kernel release: */proc/sys/kernel/osrelease* – Massimo Aug 26 '21 at 12:11
  • Also beware that it is possible to compile your own kernel for WSL2 (which wasn't the case when the question was originally asked). Assuming that *you* control the environment and can be sure this isn't the case, this answer is fine. If you need to be able to handle the rare corner-case where you don't *know* that WSL is running stock-Microsoft kernel, then additional methods might be warranted like @Massimo proposes. – NotTheDr01ds Aug 26 '21 at 19:40
22

Updating answer by @per-lundberg:

if [[ -n "$IS_WSL" || -n "$WSL_DISTRO_NAME" ]]; then
    echo "This is WSL"
else
    echo "This is not WSL"
fi

Note: IS_WSL existed in older versions (using lxrun) while WSL_DISTRO_NAME exists in current versions (from Microsoft Store).

iBug
  • 32,728
  • 7
  • 79
  • 117
Shital Shah
  • 55,892
  • 12
  • 218
  • 175
  • logic is reversed, first case is wsl: if [[ ! -z "$IS_WSL" && ! -z "$WSL_DISTRO_NAME" ]]; then echo "You are in wsl!" else echo "You are not in wsl!" fi – Amir T Nov 08 '20 at 19:09
  • This is the fastest and most efficient way to check for WSL. There is no need to really go through grep or read /proc or call external executable file like uname. – Daniel Kim Mar 02 '21 at 15:01
  • 1
    This method is 100% better than the accepted answer because it also works when running a custom compiled kernel under WSL2. Other environment variables that are present are `WSL_INTEROP` and `WSLENV`. – aizimmer Jul 23 '21 at 19:34
  • Mmh I cannot found any environment variable starting with WSL. *WINDOWS 10 20H2 build 19042.1165, UBUNTU 18.04.5 LTS, kernel 5.10.16.3-microsoft-standard-WSL2* – Massimo Aug 26 '21 at 12:07
  • @Massimo - I'm using same Windows build and above works fine. I have env var set as `WSL_DISTRO_NAME=Ubuntu-20.04`. Use command `printenv` to check. – Shital Shah Sep 15 '21 at 19:31
9

I've been looking for ways to detect that as well. So far I've found 2.

  • /proc/sys/kernel/osrelease is "3.4.0-Microsoft"

  • /proc/version is "Linux version 3.4.0-Microsoft (Microsoft@Microsoft.com) (gcc version 4.7 (GCC) ) #1 SMP PREEMPT Wed Dec 31 14:42:53 PST 2014"

If you just use the Ubuntu distribution installed by default there should be no problems with using them, as they said that it would be unlikely for them to set either to something that doesn't contain "Microsoft" or "WSL".

However, if you were to install a different Linux distribution, I'm pretty sure that the contents of /proc/sys/kernel/osrelease and /proc/version will change, since the distro wouldn't have been compiled by Microsoft.

Adno
  • 193
  • 2
  • 10
  • Because the procfs is emulated by Windows, it should (in principle, as stated on the Github comment) always contain those Microsoft strings, regardless of the distribution used, so the last paragraph seems confused to me. – Guillem Jover Sep 21 '16 at 23:05
  • 1
    @GuillemJover, that depends on whether the hypothetical other distribution is using WSL or not, I guess. I don't know whether Cygwin emulates that functionality, but if it does, I don't imagine it would say Microsoft. (Though I guess the phrase "Microsoft Windows" might appear in the string. I bet "Microsoft@Microsoft.com" won't!) – Harry Johnston Apr 09 '17 at 21:30
  • @HarryJohnston Under Cygwin, `/proc/version` contains a string starting with "CYGWIN_NT", and `/proc/sys/kernel/osrelease` doesn't exist at all. –  May 05 '17 at 01:04
6

I just came up with this for my .bashrc for adding some WSL items to $PATH.

Works in 1703. Not sure if earlier versions.

if [[ $(uname -r) =~ Microsoft$ ]]; then
    foo
fi
Ryan
  • 345
  • 3
  • 8
4

Without me doing anything special, these environment variables seem to be set already:

$ set | grep WSL
IS_WSL='Linux version 4.4.0-18362-Microsoft (Microsoft@Microsoft.com) (gcc version 5.4.0 (GCC) ) #1-Microsoft Mon Mar 18 12:02:00 PST 2019'
WSLENV=
WSL_DISTRO_NAME=Debian

So, something like the following snippet should also work in this case (example of what I used it for myself):

if [ ! -z "$IS_WSL" ]; then
    alias code='/mnt/c/Users/per/AppData/Local/Programs/Microsoft\ VS\ Code/Code.exe'
fi

(Note that technically, -z does not check if the variable is unset, merely that it is empty; in practice, this works well enough in this case. The ! at the beginning is there to negate the check.)

Per Lundberg
  • 3,409
  • 1
  • 33
  • 44
2

I needed to test for macOS in addition to Windows Subsystem for Linux 2.

This is the simplest thing working for us.

if [[ $OSTYPE == darwin* ]]; then
  # macOS
elif [[ "$(</proc/sys/kernel/osrelease)" == *microsoft* ]]; then
  # WSL2
else
  # Other *nix distro.
fi

NOTE: The if order matters. On macOS you get this error when looking at proc/version.
/proc/version: No such file or directory


hat-tip @Niklas Holm and @Marc Cornellà in the top answer's comments for aiming me toward the correct WSL check.

GollyJer
  • 18,860
  • 14
  • 91
  • 148
2
if [[ `uname -a | grep -i linux | grep -i microsoft`  != "" ]]; then echo "microsoft wsl"; fi;

Or multi-line syntax:

if [[ `uname -a | grep -i linux | grep -i microsoft`  != "" ]]; then 
    echo "microsoft wsl" 
fi

Note: The conditions have to be wrapped in the backticks or it will produce errors such as:

zsh: parse error: condition expected: uname

Anton Krug
  • 1,333
  • 2
  • 16
  • 31
user3073309
  • 154
  • 2
  • 5
1

A fail-proof test:

grep -qi -- '-WSL' /proc/sys/kernel/osrelease || test -f /proc/sys/fs/binfmt_misc/WSLInterop

Rationale:

  1. Parsing /proc/version is dangerous. It could contain misleading info (imagine a gcc compiled by microsoft). It is better to parse only the kernel release.
  2. WSL2: there aren't any WSL... environment variables. (WINDOWS 10 20H2 build 19042.1165, UBUNTU 18.04.5 LTS, kernel 5.10.16.3-microsoft-standard-WSL2)
  3. In case the test of the kernel release fails, there is a second test.

Note: having two tests, from the first one I removed microsoft and grep only on -WSL. In this simplest form, it is almost fail-proof.

The binfmt_misc template file (to run Windows executables under linux) exists both on WSL and WSL2.

Massimo
  • 2,638
  • 2
  • 26
  • 38
  • 1
    Well, mostly fail-proof. Someone could be pathologic and put a `/proc/sys/fs/binfmt_misc/WSLInterop` on a different distribution :-). And yes, that file still exists under WSL1. – NotTheDr01ds Aug 26 '21 at 19:41
  • And really, you aren't getting any `WSL_*` variables? I thought the distro name was populated by `/init`. I'm also running 19042.1165, but I haven't updated my kernel. – NotTheDr01ds Aug 26 '21 at 19:44
0

Here's what I put in my .bashrc

if [[ $(uname -v | sed -rE 's/^#[0-9]{3,}-(\S+).+/\1/') == "Microsoft" ]]; then
  # WSL-specific code
fi
  • uname -v gets the kernel version in the format of #379-Microsoft Wed Mar 06 19:16:00 PST 2019 and the sed expression pulls out the Microsoft string.
Dan
  • 1,503
  • 2
  • 14
  • 21
0

If you in Bash and want to avoid fork:

is_wsl=0
read os </proc/sys/kernel/osrelease || :
if [[ "$os" == *Microsoft ]]; then
  is_wsl=1
fi
gavenkoa
  • 41,371
  • 15
  • 229
  • 277
0

Windows Subsystem for Linux 2 (WSL 2) in Windows 10 Pro Insider Preview Build 18917

/proc/version contains:

Linux version 4.19.43-microsoft-standard (oe-user@oe-host) (gcc version 7.3.0 (GCC)) #1 SMP...

gregln
  • 1
  • 1
0

For WSL2, we can no longer detect through kernel version because it is running an actual Linux kernel in Hyper-V. However, it still can call explorer.exe existing in every Windows installation. So we could...

if [ -x "$(command -v explorer.exe)" ]; then
echo "We are running on WSL"
fi

This should be a more generic way to detect if the script is running on WSL.

Edit: See answers above. I forgot to count Unix-like environments like Msys2 in.

march_happy
  • 320
  • 2
  • 10
0

Since the distinction between WSL1 and WSL2 is that the first runs inside a container while the second runs in a virtual machine, we can make use of "systemd-detect-virt --container" to differentiate from both environments.

if [ -n "${WSL_DISTRO_NAME}" ]; then
  # In WSL but which one?
  virt_container="$(systemd-detect-virt --container)"
  case ${virt_container} in
    wsl)
      echo "This is WSL 1"
      ;;
    none)
      echo "This is WSL 2"
      ;;
    *)
      echo "Don't known ${virt_container}"
      ;;
  esac
fi
Jose Sa
  • 1
  • 2
0

Shorter cleaner version of @Shital Shah's answer.

[ -n "$IS_WSL" ] || [ -n "$WSL_DISTRO_NAME" ] && echo 'wsl' || echo 'anything else'

A Merii
  • 465
  • 9
  • 18