Reputation: 226
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
Upvotes: 0
Views: 1900
Reputation: 20795
Adding another alternative based on @manuth's good point that grep
ing can potentially fail if the text output format changes in the future.
In this version, we use PowerShell to query the Windows Registry for the list of installed distributions:
wsl_distro_name="docker-desktop"
ps_script="
Get-ChildItem HKCU:\Software\Microsoft\Windows\CurrentVersion\Lxss\ |
ForEach-Object {
(Get-ItemProperty \$_.PSPATH)
} |
Where-Object {
\$_.DistributionName -eq '$wsl_distro_name'
}
"
res="$(powershell.exe -c $ps_script | tr -d '[:space:]')"
[ -n "$res" ] && echo "Distro found" || echo "Not found"
Upvotes: 1
Reputation: 91
In my personal opinion, grepping the output might be quite an unstable solution, as the format of the output might change at any arbitrary time.
I found myself able to detect whether a specific distribution is installed by running this:
wsl --distribution "{distribution}" --exec true
or for short:
wsl -d "{distribution}" -e true
This will try to run the true
-command in a distribution called {distribution}
.
If the distribution wasn't found, wsl
exits with error code -1
.
If the distribution exists, the command true
is executed which always exits with code 0
.
Upvotes: 2
Reputation: 20795
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
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.
Upvotes: 2