Proper Bash scripting on Windows: Associate SH scripts to WSL (Windows 10) (enable drag-and-drop to .sh) (+other linux executables) – DEVELOPPARADISE
25/06/2018

Proper Bash scripting on Windows: Associate SH scripts to WSL (Windows 10) (enable drag-and-drop to .sh) (+other linux executables)

Proper Bash scripting on Windows: Associate SH scripts to WSL (Windows 10) (enable drag-and-drop to .sh) (+other linux executables)

Introduction

It’s finally out! After a long beta testing phase, Windows Subsystem for Linux (formerly known as Bash on Ubuntu on Windows) has finally reached its first stable release in Windows 10 version 1709, and it won’t disappoint you: with the Microsoft Store integration and new distributions gradually being released, WSL seems to be reaching its true potential, as bugs get fixed and more users start to prefer it over a multi-boot system (but let’s avoid starting a Windows vs Linux OS war, that’s not what this article is about 😉).

As you probably already know (unless you came across this article by chance), WSL – by default- still doesn’t allow you to associate an SH script (.sh files) to bash.exe or wsl.exe (or any other linux executable files like .out and .elf), nor dragging files and folder to a script to pass them as parameters. Moreover, you might be having a bad time creating/editing said scripts if you are a fresh-of-Windows Linux user.

But worry not, because the solution (for the first two issues) is quite simple: a Windows registry key!

SH/ELF file association and drag-over

Proper Bash scripting on Windows: Associate SH scripts to WSL (Windows 10) (enable drag-and-drop to .sh) (+other linux executables)

If, again, you are a Windows newbie wondering what the fudge a registry key (or the Windows registry) is and you are not a lazy reader, you will find the answer to all your questions by clicking here, I’ll just let you know you can install a key (or merge it, to use proper terminology) by simply double-clicking it; pretty convenient if you ask me.

So, if you just came here for the fix, the download is on top of the article: remember to download the latest version; that is unless you like bugs and missing features (who am I to judge you). After merging the registry key, just set bash.exe or wsl.exe as default program to open the .sh files or other linux executables (like .elf).

The script also supports the following distro-specific executables: ubuntu.exeSLES-12.exe, openSUSE-42.exe, kali.exe and debian.exe.

The registry key will also enable dragging files over scripts/executables to use them as parameters, a right-click > run as admin option, comes with an optional right-click > edit with Vim editor key, as well as an extra right-click into folder > open WSL here key.

Please note that you can now also associate any kind of linux executable to WSL: that includes .out files created after compiling some code, ELF executables and other executable binary files. Since Linux OS, as opposed to Windows, does not require using an extension for such files, you could give them any extension, a still unused file extension (i propose .lxx) or just .elf would be fine.

Creating and editing .sh files

Proper Bash scripting on Windows: Associate SH scripts to WSL (Windows 10) (enable drag-and-drop to .sh) (+other linux executables)

You could acknowledge this for the easy step, but you’d be mistaken… at least until you install a good text editing software. It is, indeed, common knowledge, that Windows’ Notepad is THE SPAWN OF SATAN itself and you shall never use it UNDER ANY CIRCUMSTANCES.

Jokes aside, Windows Notepad is only designed to work with Windows line endings (“/r/n”) and won’t recognise Unix line endings (“/n”), but there are a lot of alternatives, like Notepad++, or my personal favourite, Programmer’s Notepad.

Alternatively, the rar archive downloadable above will provide an optional key to add the edit (linux Vim editor) option in the context menu (file right-click). Vim isn’t the most practical editor, but it gets the job done (might be buggy on some distros).

Once installed, just create a new.txt file, rename the extension to .sh, open it with [NOT_NOTEPAD], find the file_properties/line_endings somewhere (ex: File>Properties in Programmer’s Notepad) and change line endings to Unix ones (You don’t need to worry if using Vim); You only need to do this for a new file.

How the SH association script works

We are going to work with the keys inside [HKEY_CLASSES_ROOT/Applications/bash.exe] and [HKEY_CLASSES_ROOT/Applications/wsl.exe].

First of all, let’s enable drag-over for all files opened with bash.exe or wsl.exe by adding a DropHandler in ".../shellex/DropHandler" key; I won’t go as far as creating a new drop handler, so i’ll just use vbs files’ drop handler:

{60254CA5-953B-11CF-8C96-00AA00B8708C}

The ".../shell/open/command" key of the Windows registry is able to – you guessed it- execute a command; for the open-with command this is, in most cases:

"PROGRAM_TO_OPEN_THE_FILE_WITH'S_PATH/program.exe" "%L" "%*"

The command will run everytime a file associated to program.exe is opened; %L is the path of the file we are opening, and %* are the paths of the dragged files we get thanks to the drop handler; this WON’T WORK for WSL.

We can, however, exploit this system: bash.exe and wsl.exe can both be called to silently execute a bash script: by using the -c option for the former, and by simply writing the commands afterwards for the latter. What we CAN do is, therefore, writing a small bash script that parses all the arguments from Windows paths to Unix paths, and then run the actual .sh script / linux executable file.

#Gets all file paths without expansion/substitution read -r -d '' path_param <<'EOF' PARAMETERS_PATHS_HERE EOF read -r -d '' path_exec <<'EOF' SCRIPT_PATH_HERE EOF  #Parses all dragged files' paths from Windows paths to unix paths path_param=$(echo $path_param | tr -d '"' | sed 's/[[:space:]]/([A-Z]:/)//n/1/g' | sed 's/[A-Z]:///mnt///L&/g' | tr '//' '//'/'); mapfile -t path_param <<< "$path_param"; path_param=("${path_param[@]//:}");  #Same, but with the .sh script path path_exec=$(echo $path_exec | sed 's/[[:space:]]/([A-Z]:/)//n/1/g' | sed 's/[A-Z]:///mnt///L&/g' | tr '//' '//'/');  path_exec="${path_exec//:}";  #Sets working directory to the folder where the script is located cd "${path_exec%//*}";  #Executes script with or without parameters if [[ ${path_param[@]} == "" ]];     then "$path_exec";     else "$path_exec" "${path_param[@]/#${path_exec%//*}//}"; fi;  #Leaves WSL console open after the .sh script finishes executing (optional) cd ~; bash;

To fit all this code inside a single Windows registry key we must replace all % characters with %% and all newlines with ;. That with the exception of the newline characters between heredoc commands (initial read commands), where we’ll use a linefeed character, which i will represent with the poop emoji for convenience (💩).

Let’s thus turn the (relatively) beautiful code you see above into the following abominations for bash.exe and wsl.exe command keys (REG_EXPAND_SZ types):

"%SYSTEMROOT%/System32/bash.exe" -c printf/ /"/"💩read -r -d '' path_param <<'EOF'💩%*💩EOF💩read -r -d '' path_exec <<'EOF'💩%L💩EOF💩path_param=$(echo $path_param | tr -d '"' | sed 's/[[:space:]]/([A-Z]:/)//n/1/g' | sed 's/[A-Z]:///mnt///L&/g' | tr '//' '//'/'); mapfile -t path_param <<< "$path_param"; path_param=("${path_param[@]//:}"); path_exec=$(echo $path_exec | sed 's/[[:space:]]/([A-Z]:/)//n/1/g' | sed 's/[A-Z]:///mnt///L&/g' | tr '//' '//'/'); path_exec="${path_exec//:}"; cd "${path_exec%%//*}"; if [[ ${path_param[@]} == "" ]]; then "$path_exec"; else "$path_exec" "${path_param[@]/#${path_exec%%//*}//}"; fi; cd ~; bash;
"%SYSTEMROOT%/System32/wsl.exe" read -r -d '' path_param <<'EOF'💩%*💩EOF💩read -r -d '' path_exec <<'EOF'💩%L💩EOF💩path_param=$(echo $path_param | tr -d '"' | sed 's/[[:space:]]/([A-Z]:/)//n/1/g' | sed 's/[A-Z]:///mnt///L&/g' | tr '//' '//'/'); mapfile -t path_param <<< "$path_param"; path_param=("${path_param[@]//:}"); path_exec=$(echo $path_exec | sed 's/[[:space:]]/([A-Z]:/)//n/1/g' | sed 's/[A-Z]:///mnt///L&/g' | tr '//' '//'/'); path_exec="${path_exec//:}"; cd "${path_exec%%//*}"; if [[ ${path_param[@]} == "" ]]; then sudo "$path_exec"; else sudo "$path_exec" "${path_param[@]/#${path_exec%%//*}//}"; fi; cd ~; bash;

Welcome to the Dark Side of programming.

History

[08/11/2017] CodeProject first upload (script version 8)

[15/11/2017] Greatly improved parser script, switched to VBS DropHandler (script version 9)

[16/11/2017] Added distro-specific executable support, fixed open-here bugs (script version 10)

[25/06/2018] Support for new distros and fixed icon related bugs (script version 11)