bashpowershellwindows-subsystem-for-linux

How to run a bash script on wsl with powershell?


On my current directory on Windows, I have the following script file simple_script.sh:

#!/bin/bash
echo "hi from simple script"

I wish to run this script on wsl via the powershell command line.

Using the wsl command, I could not find a way to tell wsl to invoke the script code.

The following command works (I think)

wsl bash -c "echo hi from simple script"

However when trying to load the script content into a variable and running it does not work as expected:

$simple_script = Get-Content ./simple_script.sh
wsl bash -c $simple_script

Fails with:

bash: -c: option requires an argument

I tried a few variants. using Get-Content with the -Raw flag seems to make the first word in the string to print (but not the whole string). commands that don't contain '"' characters seems to work sometimes. But I haven't found a consistent way.

A similar looking question doesn't seem to work directly with the wsl, and doesn't seem to run a script file that resides on the Windows file system.


Solution

  • The robust and efficient way to execute your shebang-line-based shell scripts from Windows is via wsl.exe -e

    wsl -e ./simple_script.sh # !! ./ is required
    

    Note:


    As for what you tried:

    # !! WRONG
    $simple_script = Get-Content ./simple_script.sh
    wsl bash -c $simple_script
    

    The primary problem is that Get-Content by default returns an array of lines, and attempting to use that array as an argument to an external program such as wsl causes the array's elements to be passed as individual arguments.

    The immediate fix is to use the -Raw switch, which makes Get-Content return the file content as a single, multi-line string.

    However, due to a highly unfortunate, long-standing bug, PowerShell - up to v7.2.x - requires manual \-escaping of embedded " characters in arguments passed to external programs.

    Therefore:

    # Using -Raw, read the file in full, as a single, multi-line string.
    $simple_script = Get-Content -Raw ./simple_script.sh
    
    # !! The \-escaping is needed up to PowerShell 7.2.x
    wsl bash -c ($simple_script -replace '"', '\"')
    

    Note that while it is tempting to try to bypass the need to escape by providing the script text via the pipeline (stdin), this does not work as expected, as of PowerShell 7.3.3:

    # !! Tempting, but does NOT work.
    Get-Content -Raw ./simple_script.sh | wsl -e bash
    

    The reason this doesn't work is that PowerShell invariably appends a Windows-format newline (CRLF) to what is being sent to external programs via the pipeline, which Bash doesn't recognize.


    [1] System error 8 is ENOEXEC, the Exec format error, indicating that while the target file has the appropriate permissions for being executed, its format isn't recognized. This could be due to file corruption, being an executable for the wrong platform or - in the case at hand - due to being a text file that lacks a well-formed shebang line.