Linux Shell Script: 'Permission Denied' Execution Error Troubleshooting Guide

Fix 'Permission Denied' errors when running Linux shell scripts. Debug execute permissions, file ownership, and common bash script issues.


When attempting to execute a shell script on a Linux system, encountering a “Permission denied” error can be a frustrating roadblock. This issue prevents your script from running, regardless of its content or syntax. As an experienced Systems Administrator, understanding the various facets of Linux permissions, file ownership, and filesystem attributes is crucial for swiftly diagnosing and resolving this common problem. This guide will walk you through a highly technical, step-by-step process to troubleshoot and fix “Permission denied” errors for shell scripts.

Symptom & Error Signature

The primary symptom is the script failing to execute, presenting a clear error message in your terminal. You will typically see output similar to one of these variations:

$ ./my_script.sh
bash: ./my_script.sh: Permission denied

Or, if the script is invoked directly without bash explicitly:

$ /path/to/my_script.sh
/path/to/my_script.sh: Permission denied

In some cases, especially if attempting to execute a file that is not a script or has corrupted permissions, you might also see:

$ some_executable
-bash: some_executable: Permission denied

This error specifically indicates that the operating system has denied the current user the right to execute the specified file.

Root Cause Analysis

The “Permission denied” error for shell script execution stems from the kernel’s security mechanisms. Here are the primary underlying reasons:

  1. Missing Execute Permission (Most Common):

    • Every file on a Linux filesystem has read (r), write (w), and execute (x) permissions for three categories: the file’s owner, the file’s group, and others (everyone else).
    • For a script to be executable by a user, that user must have the ‘execute’ bit set for their respective category (owner, group, or other). If the ‘x’ bit is not set, the system will deny execution.
  2. Incorrect File Ownership or Group:

    • If the script’s owner is userA and userA has execute permissions, but userB (who is trying to run it) does not belong to the file’s group (or the group lacks execute permission), userB will be denied. Similarly, if ‘others’ lack execute permission, userB will be denied if they’re neither the owner nor in the group.
  3. Filesystem Mount Options (noexec):

    • Some filesystems, or specific partitions/mount points, are mounted with the noexec option. This is a security measure that prevents any file from being executed from that particular mount point, regardless of its individual file permissions. Common examples include /tmp, /var/tmp, or user-controlled storage mounts.
  4. Incorrect Shebang (Interpreter Path) or Missing Interpreter:

    • A shell script typically begins with a “shebang” line (e.g., #!/bin/bash). This line specifies the interpreter used to execute the script.
    • While usually resulting in a “command not found” error for the interpreter itself, if the interpreter specified in the shebang does not exist, is not executable, or has incorrect permissions for the user, it can indirectly lead to execution failure. However, a direct “Permission denied” on the script itself usually points to the script’s permissions rather than the interpreter’s.
  5. SELinux or AppArmor Security Policies (Advanced):

    • Security-Enhanced Linux (SELinux) and AppArmor are mandatory access control (MAC) systems that enforce security policies beyond standard discretionary access control (DAC - traditional rwx permissions).
    • Even if file permissions seem correct, SELinux or AppArmor might restrict a process or user from executing a script based on predefined security contexts or profiles. This is more common in hardened enterprise environments.

Step-by-Step Resolution

Follow these steps to diagnose and resolve the “Permission denied” error for your shell script.

1. Verify and Set Execute Permissions

This is the most common fix. Use the ls -l command to inspect the script’s permissions.

  • Inspect Permissions:

    ls -l /path/to/my_script.sh

    Example Output:

    -rw-r--r-- 1 user group 1234 Jun 27 10:00 my_script.sh

    In this example, rw-r--r-- indicates:

    • Owner (user): Read (r), Write (w), No execute (-)
    • Group (group): Read (r), No write (-), No execute (-)
    • Others: Read (r), No write (-), No execute (-)

    The absence of the x (execute) bit for the relevant user category is the problem.

  • Add Execute Permissions: Use chmod to add the execute bit.

    # Add execute permission for the owner
    chmod u+x /path/to/my_script.sh
    
    # Add execute permission for owner and group
    chmod ug+x /path/to/my_script.sh
    
    # Add execute permission for owner, group, and others (most common for scripts)
    chmod +x /path/to/my_script.sh

    The chmod +x command is equivalent to chmod a+x (all users). A common and secure permission set for an executable script is 755: owner has read, write, execute; group and others have read and execute.

    chmod 755 /path/to/my_script.sh

    Verify the change:

    ls -l /path/to/my_script.sh
    # Expected output: -rwxr-xr-x 1 user group 1234 Jun 27 10:00 my_script.sh

    [!IMPORTANT] While chmod 777 grants full permissions to everyone, it is generally a security risk and should be avoided in production environments unless absolutely necessary for a very specific, isolated use case. Always grant the minimum necessary permissions.

  • Retry Execution:

    ./path/to/my_script.sh

2. Check File Ownership and Group

If adding execute permissions doesn’t resolve the issue, verify that the current user has the appropriate ownership or group membership to execute the script.

  • Inspect Ownership and Group:

    ls -l /path/to/my_script.sh

    Example Output:

    -rwxr-xr-x 1 root root 1234 Jun 27 10:00 my_script.sh

    Here, the script is owned by root:root. If you are running as userA, and others do not have execute permission, you’ll be denied.

  • Identify Current User/Group:

    id -un # Current username
    id -gn # Current primary group
    id     # All groups current user belongs to
  • Change Ownership (if necessary): If the script is owned by a different user and you need the current user to own it, use chown. You generally need sudo privileges for this.

    sudo chown your_user:your_group /path/to/my_script.sh

    Replace your_user and your_group with the desired username and group.

    [!WARNING] Changing file ownership, especially system files, can have significant security and operational impacts. Only change ownership if you are certain it’s required and understand the implications.

  • Retry Execution.

3. Inspect Shebang Line and Interpreter

While less likely to directly cause “Permission denied” on the script itself (more often “command not found”), an issue with the shebang or interpreter can prevent execution.

  • Check Shebang Line: The first line of your script should specify the interpreter.

    head -1 /path/to/my_script.sh

    Example Output:

    #!/bin/bash

    Ensure this path is correct for your system. Common alternatives include #!/usr/bin/env bash for better portability, or #!/bin/sh for POSIX compliance.

  • Verify Interpreter Existence and Permissions: Check if the specified interpreter exists and is executable.

    which bash # Or `which python`, `which node`, etc.
    ls -l /bin/bash # Check permissions of the interpreter itself

    The interpreter itself (/bin/bash in this example) must have execute permissions for all users (-rwxr-xr-x is typical). If it doesn’t, that’s a much larger system issue requiring root intervention.

  • Alternative Execution Method: If the shebang or permissions are problematic, you can often explicitly tell Bash to interpret the script. This bypasses the shebang and the script’s execute permission, relying only on Bash having read access to the script.

    bash /path/to/my_script.sh

    If this works, but ./my_script.sh doesn’t, the issue is definitely related to the script’s execute bit or its shebang.

4. Examine Filesystem Mount Options

If permissions are correct, the filesystem itself might be preventing execution.

  • Identify Mount Point: First, determine which filesystem the script resides on.

    df -h /path/to/my_script.sh

    This will show the mount point (e.g., /home, /var/www, /tmp).

  • Check Mount Options: Now, check the mount options for that specific mount point.

    mount | grep /path/to/mount_point

    Example Output indicating an issue:

    /dev/sda2 on /tmp type ext4 (rw,nosuid,nodev,noexec,relatime)

    The presence of noexec is the culprit here.

  • Resolution:

    • Move the script: The simplest solution is to move the script to a filesystem that does not have the noexec option (e.g., /opt, /usr/local/bin, or your home directory if it’s not noexec).
    • Remount (Temporary): You can remount the filesystem without noexec (requires sudo):
      sudo mount -o remount,exec /path/to/mount_point
      This change is temporary and will revert after a reboot.
    • Modify /etc/fstab (Permanent): For a permanent change, edit /etc/fstab to remove noexec from the mount options for that partition.
      sudo nano /etc/fstab
      Find the line corresponding to your mount point and change noexec to exec. Example: From:
      UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /tmp ext4 defaults,noexec 0 2
      To:
      UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx /tmp ext4 defaults,exec 0 2
      After saving, apply changes with:
      sudo mount -a
      Or reboot the system.

    [!WARNING] Modifying /etc/fstab incorrectly can prevent your system from booting. Always back up /etc/fstab before making changes (sudo cp /etc/fstab /etc/fstab.bak). Removing noexec from security-sensitive partitions like /tmp can introduce vulnerabilities. Only do so if you fully understand the security implications.

5. SELinux or AppArmor Diagnostics (Advanced)

If all conventional permissions and mount options are correct, a Mandatory Access Control (MAC) system might be interfering.

  • Check SELinux Status:

    sestatus

    If SELinux is enforcing, it might be the cause.

  • Check for SELinux Denials in Audit Log:

    sudo grep 'AVC' /var/log/audit/audit.log
    # Or, if auditd isn't running/configured, check dmesg:
    dmesg | grep 'SELINUX_AVC'

    Look for denied entries related to your script or the execution attempt.

  • Restore SELinux Context: Sometimes, files get incorrect SELinux contexts, especially if moved or copied from different locations.

    sudo restorecon -v /path/to/my_script.sh

    This command applies the default SELinux context to the file.

  • Check AppArmor Status:

    sudo aa-status

    If AppArmor is enabled and profiles are loaded, it might be restricting execution.

  • AppArmor Logs: AppArmor denials are typically logged in syslog or dmesg.

    sudo grep 'apparmor="DENIED"' /var/log/syslog
    # Or:
    sudo dmesg | grep 'apparmor="DENIED"'
  • Temporary SELinux/AppArmor Disablement (for testing):

    [!WARNING] Temporarily disabling SELinux or AppArmor should only be done in a controlled testing environment and never on a production server without explicit understanding of the security risks. Re-enable them immediately after testing.

    • SELinux Permissive Mode:
      sudo setenforce 0
      This switches SELinux to permissive mode, where it logs denials but doesn’t block actions. If your script runs now, SELinux was the issue. Re-enable with sudo setenforce 1.
    • AppArmor (Disabling Profiles): This is more complex and involves unloading or putting specific profiles into complain mode. Refer to AppArmor documentation for details.

By methodically following these steps, you should be able to identify and rectify the root cause of the “Permission denied” error when executing your Linux shell scripts. Always prioritize understanding the underlying permissions model to ensure both functionality and security.