Today we discuss one of a few questions a lot of sysadmins and IT Admins ask?
Table of Contents
The Windows Subsystem for Linux (WSL) was a big step forward in this regard, allowing developers to call Linux commands from Windows via wsl.exe (e.g. wsl ls). While an improvement, the experience falls short in various ways:
As a result of these flaws, Linux commands appear to Windows as second-class citizens and are more difficult to use than they should be. These difficulties must be addressed in order for a command to feel like a native Windows command.
With PowerShell function wrappers, we can eliminate the need to prefix commands with wsl, handle the conversion of Windows paths to WSL paths, and allow command completion. The wrappers’ essential requirements are as follows:
Each Linux command should have one function wrapper with the same name as the command.
The wrapper should be able to recognise and translate Windows paths supplied as arguments into WSL paths.
The wrapper should call wsl with the appropriate Linux command, providing any pipeline input and any command line arguments to the function.
We can abstract the design of these wrappers and build them dynamically from a list of commands to import because this template may be applied to any command.
# The commands to import.
$commands = "awk", "emacs", "grep", "head", "less", "ls", "man", "sed", "seq", "ssh", "tail", "vim"# Register a function for each command.
$commands | ForEach-Object { Invoke-Expression @"
Remove-Alias $_ -Force -ErrorAction Ignore
function global:$_() {
for (`$i = 0; `$i -lt `$args.Count; `$i++) {
# If a path is absolute with a qualifier (e.g. C:), run it through wslpath to map it to the appropriate mount point.
if (Split-Path `$args[`$i] -IsAbsolute -ErrorAction Ignore) {
`$args[`$i] = Format-WslArgument (wsl.exe wslpath (`$args[`$i] -replace "\\", "/"))
# If a path is relative, the current working directory will be translated to an appropriate mount point, so just format it.
} elseif (Test-Path `$args[`$i] -ErrorAction Ignore) {
`$args[`$i] = Format-WslArgument (`$args[`$i] -replace "\\", "/")
}
}if (`$input.MoveNext()) {
`$input.Reset()
`$input | wsl.exe $_ (`$args -split ' ')
} else {
wsl.exe $_ (`$args -split ' ')
}
}
"@
}
The $command above code will list/ specifies which commands should be imported. Then, using the Invoke-Expression command, we dynamically build the function wrapper for each (first removing any aliases that would conflict with the function).
The function goes through the command line parameters, identifying Windows paths with the Split-Path and Test-Path commands, and then converting them to WSL paths. We pass the paths through Format-WslArgument, a helper function that we’ll build later to escape special characters like spaces and parentheses that might otherwise be misconstrued.
Finally, we send wsl pipeline input as well as any command line parameters.
With these function wrappers in place, we can now call our favourite Linux commands without needing to prefix them with wsl or worry about how they’ll be executed.
In Linux, it’s customary to utilise login profiles to specify aliases and/or environment variables to set default parameters for programmes you use regularly (for example, alias ls=ls -AFh or export LESS=-i). One disadvantage of using wsl.exe to proxy through a non-interactive shell is that no login profiles are loaded, therefore these default options are unavailable (i.e. ls within WSL and wsl ls would behave differently with the alias defined above).
$PSDefaultParameterValues is a common technique for defining default parameter values in PowerShell, although it is only available for cmdlets and advanced functions. It’s possible to turn our function wrappers into advanced functions, but there are certain drawbacks (for example, PowerShell matches partial parameter names (like -a for -ArgumentList), which would conflict with Linux commands that take partial names as arguments).
We can allow default parameters for Linux commands with a little tweak to our function wrappers, similar to $PSDefaultParameterValues!
function global:$_() {`$defaultArgs = ((`$WslDefaultParameterValues.$_ -split ' '), "")[`$WslDefaultParameterValues.Disabled -eq `$true]
if (`$input.MoveNext()) {
`$input.Reset()
`$input | wsl.exe $_ `$defaultArgs (`$args -split ' ')
} else {
wsl.exe $_ `$defaultArgs (`$args -split ' ')
}}
$WslDefaultParameterValues["grep"] = "-E"
$WslDefaultParameterValues["less"] = "-i"
$WslDefaultParameterValues["ls"] = "-AFh --group-directories-first"
We can incorporate Linux commands into Windows as though they were native applications using PowerShell and WSL. There’s no need to look for Win32 versions of Linux utilities or interrupt your work to enter a Linux shell. Simply download WSL, create a PowerShell profile, then specify the commands you want to import. Even native Windows commands don’t give the extensive argument completion displayed here for both command parameters and Linux and Windows file paths.
https://github.com/mikebattista/PowerShell-WSL-Interop has the whole source code as well as extra instructions for implementing it into your workflow.
Which Linux commands are the most beneficial to you? What additional aspects of your developer workflow are you unhappy with?
Any questions or new topics to discuss please comment on this post… 🙂
How to install of FireFox browser using PowerShell Sometimes you would like to install Firefox…
Quick way to install chrome on windows with Powershell? Launch the Powershell and run below…
How to Fix PowerShell Script Not Digitally Signed Error? When a script with a .ps1…
Powershell Cheat Sheet for beginners PowerShell has become something of an ace in the hole…
Backing Up an SQL Database with PowerShell Before making any changes in the production environment,…
Get-EventLog We use PowerShell to parse your Server’s/computers event logs using the Get-EventLog cmdlet. There…
This website uses cookies.