0

I am trying to ask WSL to check if the distribution exists or doesn't exist, for example, wsl.exe -l -v outputs:

  NAME                   STATE           VERSION
  Arch-Linux             Running         2
* Ubuntu                 Running         2
  docker-desktop         Running         2
  docker-desktop-data    Running         2

I need to ask WSL to check if desktop-desktop-data or Arch exists.

Here is my small Bash code. I tested with several different ways, nothing worked.

# Distribution name: Ubuntu
wsl_distro_name="docker-desktop"

# WSL command to find the distribution name
wsl_command=`wsl.exe -l -v | grep -iq '^$wsl_distro_name' | awk '{print $2}'`

if [[ "$wsl_command" ]]; then
    echo "Distro found"
    exit 0
else
    echo "Not found"
    exit 1
fi
Oo'-
  • 203
  • 6
  • 22

1 Answers1

2

Updated answer:

The core issue behind this question has been fixed, or at least improved, in the latest WSL Preview release (0.64.0), but note that this is currently only available for Windows 11 users.

To avoid breaking older code that relies on (or works around) the issue, the fix is opt-in. Setting a WSL_UTF8 environment variable with a value of 1 (and only, in my testing, that value) will result in correct/expected output from wsl.exe.

Note that if using this environment variable inside WSL, you'll need to also add it to WSLENV so that it gets passed "back" to Windows through Interop.

For your use, for example, the following will now work:

export WSL_UTF8=1
WSLENV="$WSLENV":WSL_UTF8

wsl_distro_name="docker-desktop-data"
wsl.exe -l -v | grep -q "\s${wsl_distro_name}\s" && echo "Found" || echo "Not found"f

For older WSL installations

Short answer:

wsl_distro_name="docker-desktop-data"
wsl.exe -l -v | iconv -f UTF-16 | grep -q "\s${wsl_distro_name}\s" && echo "Found" || echo "Not found"

Explanation:

There are a few things going on with your example:

  • First, the thing that probably has you stymied the most is a WSL bug (covered in more detail in this question) that causes the output to be in a mangled UTF-16 encoding. You can see this to some degree with wsl.exe | hexdump -C, which will show the null byte characters after every regular character.

    The solution to that part is to run it through iconv -f utf16. For example:

    wsl.exe -l -v | iconv -f UTF-16
    

I'm guessing you probably introduced some of the following errors into your code while trying to work around the preceding bug:

  • You have $wsl_distro_name in single quotes in your grep, which disables string interpolation in Bash. You need double-quotes there.

  • The ^ won't work at the beginning of the regex since there is whitespace (and/or an asterisk) in the first couple of characters of the wsl.exe -l -v output. Better to use "\s$wsl_distro_name\s to find the distro name that is surrounded by whitespace.

    This will also prevent the expression from finding a "partial" distribution name. Without it, "docker-desktop" would match both "docker-desktop" and/or "docker-desktop-data".

  • grep -q disables output and only returns a status code of 0 when found or 1 if not. Since you are attempting to capture the grep output into $wsl_command, the result will always be empty. You either remove the -q to capture the output or continue to use -q and test the status code.

  • Given your if statement, it seems like you may be expecting the output to be a status result anyway, but testing "$wsl_command" won't work for that, nor will capturing the output via backticks. That would look more like:

    wsl_distro_name="docker-desktop-data"
    wsl.exe -l -v | iconv -f UTF-16 | grep -q "\s${wsl_distro_name}\s"
    if [[ $? -eq 0 ]]; then
        echo Found
    fi
    

    The $? variable holds the exit code of the last command.

    Alternatively:

    wsl_distro_name="docker-desktop-data"
    wsl_distro_result=$(wsl.exe -l -v | iconv -f UTF-16 | grep -o "\s${wsl_distro_name}\s.*" | awk '{print $2}')
    if [[ -n "$wsl_distro_result" ]]; then
        echo "Found ${wsl_distro_name} and it is ${wsl_distro_result}."
    else
        echo "${wsl_distro_name} not found."
    fi
    

    Note that I added the -o option to grep in that snippet. The default distribution will have an asterisk in the first field, which means that we need to "normalize" it so that the second field for awk is always the Status.

  • Finally, recommend using $() rather than backticks. See this question, but note that the POSIX spec says, "the backquoted variety of command substitution is not recommended."

  • Side note: If for some reason you ever need to run this in Alpine, then make sure to install the gnu-libiconv package in order for iconv to handle this properly as well.

NotTheDr01ds
  • 15,620
  • 5
  • 44
  • 70
  • I don't understand how `\s` possibly can work in your code. You are requesting grep to use _simple regular expressions_, which means that \s matches a literal character `s`. For instance, `echo absc | grep -o \s` matches exactly the _s_. – user1934428 Jun 27 '22 at 06:03
  • @user1934428 So it's a bit weird. It does work to match whitespace, but I should have read more closely when Googling/scanning last night when coming up with it. I use so many regex versions that I pretty much have to look up the details each time. I Googled, [grep whitespace](https://www.google.com/search?q=grep+whitespace), for which the top result is [this SO question](https://stackoverflow.com/q/4233159/11810933). I saw the `\s`, tried it, and it worked. I didn't read further into the comments that it was reported as a bug, declined as not-a-bug, but never documented (to this day, AFAIK). – NotTheDr01ds Jun 27 '22 at 13:01
  • So maybe `[[:space:]]` would be better, but `\s` has worked for the last decade or so, and any version of `grep` in a WSL installation should be okay. – NotTheDr01ds Jun 27 '22 at 13:02
  • You **can** make \s work with grep. You just need the `-P` option. But of course [[:space:]] is preferable. – user1934428 Jun 27 '22 at 13:02
  • @user1934428 Oh, it works without `-P`. I just retested it. My question now would be why it matches *both* a literal `s` in your example but whitespace in mine. – NotTheDr01ds Jun 27 '22 at 13:03
  • @user1934428 And it turns out that when the expression is *quoted*, it matches whitespace, not the literal. `echo "absc" | grep -o "\s"` does not match. `echo "absc " | grep -o "\s"` does match. `echo "abc " | grep -o \s` does not match. `echo "abc " | grep -o \s` does not match. Same with single or double-quotes. – NotTheDr01ds Jun 27 '22 at 13:06
  • AFAICT, that's the *shell* changing the unquoted `\s` to a literal `s`, right? E.g. `echo \s | grep "^s$"` – NotTheDr01ds Jun 27 '22 at 13:10
  • 1
    Git Bash and WSL don't recognise `utf16`. I switched for `UTF-16`. They recognised. Git Bast didn't find the distribution, but the inner WSL found it. – Oo'- Jun 27 '22 at 13:17
  • @GustavoReis Good to know. `iconv -l` on Ubuntu shows both forms, but I can imagine that's a convenience that isn't available in all versions. I'll update the answer. Thanks! – NotTheDr01ds Jun 27 '22 at 13:21
  • @NotTheDr01ds : Correct. That's why I used quotes in my counter-example, `echo "absc " | grep -o "\s"`. That is, _grep_ sees the backslash, but for grep, a backslash followed by a letter `s` does not have a special meaning. That's why the space after the string _absc_ is not matched. The input string would also have to contain a backslash, followed by a space, in order to match. – user1934428 Jun 28 '22 at 05:29
  • 1
    @user1934428 For me, the example you just provided (in quotes) **is** matched. Unless you are using a very old grep, since the `\s` behavior was [changed in 2.6.3](http://savannah.gnu.org/bugs/?30515) over a decade ago. – NotTheDr01ds Jun 28 '22 at 05:40