Migrate to RunxBuild and earn up to $50 in hosting credit on your first deposit.

Calculate your savings
unxBuild

Make a Python Script Executable in Linux: The Shebang and chmod +x Together

Sean

Platform Writer

Jul 18, 2026
6 min read

To run a Python script directly as ./script.py instead of python3 script.py, you need two things: a shebang line at the very top of the file, and the executable permission set with chmod +x. The shebang - #!/usr/bin/env python3 - tells the system which interpreter to use. chmod +x script.py gives the file permission to run. Miss either one and it fails: no shebang and the shell tries to run Python as shell script; no execute bit and you get permission denied. Both together, and your script behaves like a real command.

Make a Python Script Executable in Linux: The Shebang and chmod +x Together

Table of contents

The two required steps

#!/usr/bin/env python3
print("Hello from an executable script")
chmod +x script.py       # give it execute permission
./script.py              # now run it directly

Two steps, both required. The first line of the file is the shebang, #!/usr/bin/env python3, which must be the very first line - no blank lines or comments before it. Then chmod +x marks the file executable.

The ./ in ./script.py matters. It tells the shell to run the script in the current directory; without it, the shell searches your PATH and does not find your script (which is not in a PATH directory), giving command not found. So ./script.py runs a script in the current folder, and a bare script.py looks for it on the PATH. Both steps and the ./ together are what turn a .py file into something you run like any command.

Why use env python3 in the shebang

There are two common shebang forms, and one is more portable:

#!/usr/bin/env python3      # portable - finds python3 on the PATH
#!/usr/bin/python3          # hardcoded - only works if python3 is exactly there

#!/usr/bin/env python3 asks env to locate python3 on the user’s PATH. This is the portable choice, because Python lives in different places on different systems - /usr/bin on one, /usr/local/bin on another, a pyenv shim or a virtualenv on a third. The env form finds whichever python3 is active.

#!/usr/bin/python3 hardcodes the path. It works only if Python is exactly there, which is not guaranteed, and it ignores virtual environments entirely. If your script runs on your machine but fails on a colleague’s with bad interpreter or no such file, a hardcoded shebang pointing at a path that does not exist there is the usual cause. Use the env form and the script finds the right Python wherever it runs.

What chmod +x actually does

Linux file permissions have three bits - read, write, execute - for three groups - owner, group, others. chmod +x adds the execute bit:

chmod +x script.py           # add execute for everyone
chmod u+x script.py          # execute for the owner only
ls -l script.py              # -rwxr-xr-x  - the x's are execute

The x characters in ls -l output are the execute permissions. Without any x, the file cannot run and you get permission denied no matter how correct the shebang is.

chmod +x (with no group letter) adds execute for owner, group, and others. For a personal script, chmod u+x (owner only) is slightly tighter and often enough. Either way, the execute bit is the second half of the puzzle - the shebang says how to run it, and the execute bit says it is allowed to run. You need both; a perfect shebang on a non-executable file still fails.

Running it from anywhere with PATH

Once a script is executable, you still have to type ./script.py from its directory. To run it by name from anywhere - like a real command - put it on your PATH.

The common approach is a personal bin directory:

mkdir -p ~/bin
mv script.py ~/bin/mytool          # rename, drop the .py if you like
# ensure ~/bin is on PATH (add to ~/.bashrc if not):
export PATH="$HOME/bin:$PATH"

Now mytool runs from any directory, because the shell finds it on the PATH. Dropping the .py extension is conventional for a command-line tool - users of ls and grep do not type .c, and your tool can follow suit.

Alternatively, symlink it into a directory already on your PATH so you can keep editing it in place:

ln -s ~/projects/mytool/script.py ~/bin/mytool

The symlink means edits to the original are live immediately - handy while you are still developing the tool. Either way, PATH is what turns ./script.py into a command you can call by name.

Common failures and fixes

The handful of errors you will actually hit:

  • permission denied - the execute bit is missing. Run chmod +x script.py.
  • command not found - you typed script.py without ./, and it is not on your PATH. Use ./script.py or add it to PATH.
  • bad interpreter: No such file or directory - the shebang points at a Python that does not exist there, or the file has Windows line endings. Switch to #!/usr/bin/env python3, and if the file came from Windows, fix line endings with dos2unix script.py.
  • : No such file or directory on the shebang line - almost always Windows carriage returns (\r\n) on the shebang. dos2unix fixes it.

That last one is sneaky: a script written or edited on Windows carries \r\n line endings, and the trailing \r on the shebang line makes the kernel look for an interpreter named python3\r, which does not exist. The error is baffling until you know it. dos2unix script.py or configuring your editor to save Unix line endings is the fix, and it is worth checking first whenever a shebang that looks perfect still fails.

How this fits the rest of the stack

Making a script run like a real command - shebang, execute bit, on the PATH - is the same packaging discipline that makes code portable across machines. When that script becomes a service or a scheduled job, the platform handles the interpreter and the entry point, but the habit of declaring how a thing runs is exactly what makes it deployable. The RunxBuild hosting calculator lays out the service, database, storage, and bandwidth as separate line items, and the RunxBuild dashboard is where the team watches deploys, logs, and restarts as they happen.

Useful related references:

FAQ

How do I make a Python script executable in Linux?

Add a shebang line #!/usr/bin/env python3 as the very first line of the file, then run chmod +x script.py to set the execute permission. After that, run it directly with ./script.py. You need both the shebang and the execute bit.

What is a shebang line in Python?

It is the first line of a script, starting with #!, that tells the system which interpreter to run the file with. #!/usr/bin/env python3 uses env to find python3 on the PATH, which is the portable choice. It must be the very first line, with nothing before it.

Why should I use env python3 instead of a hardcoded path?

Because #!/usr/bin/env python3 finds whichever python3 is on the user’s PATH, working across machines where Python lives in different locations and respecting virtual environments. A hardcoded #!/usr/bin/python3 only works if Python is exactly there, and fails elsewhere with a bad interpreter error.

Why do I get permission denied running my Python script?

The file does not have the execute permission. Run chmod +x script.py to add it, then run ./script.py. A correct shebang alone is not enough - the execute bit is what allows the file to run at all.

Why does my script fail with bad interpreter even though the shebang looks right?

Usually the file has Windows line endings, so a trailing carriage return makes the kernel look for an interpreter named python3\r. Run dos2unix script.py to convert to Unix line endings, or set your editor to save with LF endings. This is a common and confusing cause.

#make python script executable linux#python#linux#shebang#dev-infra