Linux & OS Intermediate

Fixing ‘Permission Denied’ for Shell Scripts in WSL2 Ubuntu

Resolve 'Permission Denied' errors when running shell scripts in WSL2 Ubuntu. Learn how file permissions, line endings, and filesystem mounts impact execution.

👨‍💻
Senior Systems Architect • Verified in Staging Labs

Resolve 'Permission Denied' errors when running shell scripts in WSL2 Ubuntu. Learn how file permissions, line endings, and filesystem mounts impact execution.

When working with development environments on Windows Subsystem for Linux 2 (WSL2), particularly Ubuntu distributions, encountering a "Permission Denied" error when attempting to execute a shell script is a common hurdle. This issue typically arises when scripts are created, modified, or stored on the Windows host filesystem (e.g., within /mnt/c/Users/YourUser/Scripts/) and then executed from within the WSL2 environment. Understanding the interplay between Linux POSIX permissions, Windows NTFS characteristics, and WSL2's filesystem mounting mechanisms is key to resolving this.

Symptom & Error Signature

Users typically experience this issue directly at the command line when attempting to run a script, often with ./ prefix.

# Attempting to execute a script directly
./my_script.sh

The primary error signature will be:

bash: ./my_script.sh: Permission denied

In some cases, especially if the script was created or heavily edited in a Windows text editor, you might also encounter a "bad interpreter" error, which often indicates an issue with line endings or the shebang:

# Secondary error due to incorrect line endings or shebang
./my_script.sh
bash: ./my_script.sh: /bin/bash^M: bad interpreter: No such file or directory

Root Cause Analysis

The "Permission Denied" error for shell scripts in WSL2 Ubuntu primarily stems from a combination of the following factors:

  1. Missing Execute Permissions: In Linux, a file must have the execute bit (x) set in its permission mask for the owning user, group, or others to be directly executable. If this bit is absent (e.g., rw-r--r--), the shell cannot execute the script directly, leading to a permission error.

  2. WSL2 Filesystem Mount Options (NTFS Limitations): When files reside on a Windows filesystem mounted into WSL2 (e.g., /mnt/c/), the native NTFS filesystem does not inherently support full Linux POSIX permissions (like the execute bit). WSL2 handles this by translating permissions or by requiring specific mount options.

    • By default, if the metadata option is not enabled for automounted drives in /etc/wsl.conf, chmod +x might not persistently apply the execute bit, or the default umask applied during mounting might strip it.
    • The metadata option, when enabled, allows WSL2 to store Linux-specific metadata (including permissions, ownership, and timestamps) as hidden NTFS alternate data streams (ADS) or reparse points, making chmod operations effective and persistent on Windows files. Without it, the effective permissions are derived from the umask, fmask, and dmask options, which by default often deny execute permissions for files.
  3. Incorrect Line Endings: Windows uses Carriage Return and Line Feed (CRLF, or rn) for line endings, while Linux uses only Line Feed (LF, or n). A script created or saved on Windows with CRLF endings, when executed in Linux, can confuse the bash interpreter, especially on the shebang line (#!). The ^M character (representing r) can be interpreted as part of the interpreter path, leading to "bad interpreter" or "No such file or directory" errors for the shebang.

  4. Missing or Incorrect Shebang Line: The shebang (#!) at the very beginning of a script tells the operating system which interpreter (e.g., bash, python) should be used to execute the script. If the shebang is missing, malformed, or points to an interpreter not available at that path, the system won't know how to run the script.

Step-by-Step Resolution

Follow these steps to diagnose and resolve the "Permission Denied" error for shell scripts in your WSL2 Ubuntu environment.

1. Verify and Add Execute Permissions

The most common cause is simply the absence of the execute permission bit.

  1. Check current permissions: Use ls -l to inspect the file's permissions.

    ls -l my_script.sh
    

    An output like -rw-r--r-- indicates that the owner has read/write permissions, but not execute.

  2. Add execute permission: Use chmod to add the execute bit for the owner, or for all users if appropriate.

    # Add execute permission for the file owner
    chmod u+x my_script.sh
    
    # Or, add execute permission for owner, group, and others (common for scripts)
    chmod +x my_script.sh
    
    # Or, set specific permissions like rwxr-xr-x (owner rwx, group rx, others rx)
    chmod 755 my_script.sh
    
  3. Verify permissions again: Confirm the change with ls -l. You should now see an x in the permission string (e.g., -rwxr-xr-x).

    ls -l my_script.sh
    

    If chmod +x appears to work but the execute bit (x) does not persist after re-checking with ls -l (or the script still gives "Permission Denied"), this strongly indicates a deeper issue related to WSL2's handling of permissions on Windows-mounted filesystems. Proceed to Step 4.

2. Check and Correct Shebang Line

Ensure your script has a correct shebang line.

  1. Inspect the shebang: View the first line of your script using head.

    head -n 1 my_script.sh
    

    It should typically look like #!/bin/bash or #!/usr/bin/env bash.

  2. Correct the shebang (if necessary): If the shebang is missing or incorrect, edit your script to include it.

    • #!/bin/bash: Direct path to the bash interpreter. This assumes bash is at /bin/bash.
    • #!/usr/bin/env bash: More portable. env finds bash in the system's PATH.
    # Example for nano editor
    nano my_script.sh
    # Add or correct the first line: #!/bin/bash
    

3. Address Line Ending Issues

Scripts created or edited on Windows often have CRLF line endings, which Linux interpreters don't handle well.

  1. Detect CRLF line endings: Use cat -vE to show control characters. ^M indicates a carriage return.

    cat -vE my_script.sh
    

    Look for ^M characters, especially at the end of the shebang line or other lines.

  2. Convert to LF line endings: The dos2unix utility is the simplest and most robust way to convert line endings.

    # Install dos2unix if you don't have it
    sudo apt update && sudo apt install -y dos2unix
    
    # Convert the script
    dos2unix my_script.sh
    

    Alternatively, using sed:

    sed -i 's/r$//' my_script.sh
    

    Or within vi/vim:

    vi my_script.sh
    :set ff=unix
    :wq
    

4. Configure WSL2 Mount Options for Windows Drives (Persistent Fix)

If chmod +x doesn't stick for scripts on your Windows drive (e.g., /mnt/c/), you need to enable Linux metadata support for that drive in WSL2.

  1. Edit wsl.conf: Open the wsl.conf file in your WSL2 distribution using nano or vi.

    sudo nano /etc/wsl.conf
    
  2. Add/Modify [automount] section: Ensure the [automount] section includes metadata = true. This tells WSL2 to store Linux file metadata (like permissions, owner, group) directly on the NTFS filesystem.

    [automount]
    enabled = true
    root = /mnt/
    options = "metadata"
    

    If you have existing umask, fmask, or dmask options under [automount], the metadata option typically overrides or takes precedence for permission handling, making chmod effective. You generally want metadata enabled for consistent permission behavior.

  3. Save and Exit: Save the wsl.conf file.

  4. Shut down and restart WSL2: For changes in wsl.conf to take effect, you must completely shut down and restart the WSL2 instance. Open PowerShell (as administrator is not required for wsl --shutdown) on Windows:

    wsl --shutdown
    

    Then, restart your WSL2 distribution by opening a new Ubuntu terminal.

  5. Re-apply execute permissions: Navigate back to your script location in the WSL2 terminal and re-apply the execute permission:

    cd /mnt/c/path/to/your/script
    chmod +x my_script.sh
    ls -l my_script.sh # Verify
    

    The execute bit should now persist, and the script should run without "Permission denied."

5. Move Script to WSL2 Filesystem (Workaround/Best Practice)

For scripts that are purely for Linux operations and don't need direct Windows access, storing them within the WSL2 filesystem itself (e.g., in your home directory ~/ or /usr/local/bin/) is often the simplest and most robust solution, as it inherently uses native Linux permissions.

  1. Copy the script: Move the script from your Windows-mounted drive to a location within your WSL2 home directory.

    cp /mnt/c/path/to/my_script.sh ~/my_script.sh
    
  2. Add execute permissions (if necessary): Permissions should behave normally here.

    chmod +x ~/my_script.sh
    
  3. Execute:

    ~/my_script.sh
    

    If you frequently edit scripts using Windows-based IDEs or text editors, ensure they are configured to save files with LF (Unix) line endings to prevent issues, even when working on files within the WSL2 filesystem (accessed via \wsl$Ubuntu-22.04homeyouruser).

6. Run with an Interpreter Directly (Temporary Bypass)

As a temporary measure or for quick testing, you can explicitly tell bash to interpret the script, bypassing the file's execute permissions. This does not fix the underlying permission issue but can help isolate the problem.

bash my_script.sh
👨‍💻

Johnathon Wheeler

Senior Systems Architect & DevOps Engineer • Austin, TX

Connect on LinkedIn →

Johnathon has over 16 years of hands-on experience designing, debugging, and scaling Linux web hosting stacks, container clusters, and high-availability database architectures. Every guide on ButItWorkedLocal is independently tested against Debian 12, Ubuntu 24.04/22.04 LTS, Rocky Linux, and Docker environments to guarantee reproducibility in production.

🛡️

Our Production Verification Guarantee

Encountering a bug not covered here or running a non-standard kernel configuration? Our solutions are continually refined against real production incidents. Submit an environment trace for our editorial team to replicate.