If you’ve written bash scripts, you’ve probably used a shebang line at the top.
But which one should you use: #!/bin/bash or #!/usr/bin/env bash?
And is there really a security risk with the latter?
The Two Approaches
Hardcoded Path: #!/bin/bash
This shebang assumes bash is installed at /bin/bash. It’s direct and predictable—you know exactly which interpreter will run your script.
Pros:
- Predictable execution path
- Works consistently on most Linux distributions
- Slightly faster (no PATH lookup needed)
Cons:
- Not portable to systems where bash lives elsewhere
- Fails on some BSD systems or custom installations
- Doesn’t allow users to specify an alternative bash version
PATH Lookup: #!/usr/bin/env bash
This shebang uses the env command to search for bash in your PATH environment variable, executing the first match it finds.
Pros:
- Highly portable across different Unix-like systems
- Allows users to override with their preferred bash version
- Works regardless of where bash is installed
Cons:
- Behavior depends on the user’s PATH
- Potential security concern in specific scenarios
The Security Question
The security concern revolves around PATH injection attacks. If an attacker can manipulate the PATH environment variable before your script runs, #!/usr/bin/env bash could execute a malicious binary instead of the legitimate bash shell.
Here’s how such an attack might work:
# Attacker creates a fake bash
echo '#!/bin/sh' > /tmp/bash
echo 'malicious code here' >> /tmp/bash
chmod +x /tmp/bash
# Attacker modifies PATH
export PATH=/tmp:$PATH
# Now #!/usr/bin/env bash will find the fake bash first
./your-script.sh
When This Actually Matters
The security risk is real but contextual. It matters in these scenarios:
- Setuid/setgid scripts - Scripts running with elevated privileges
- System services - Scripts executed by systemd, init, or other system processes
- Cron jobs - Especially those running as root with potentially untrusted PATH
- CGI scripts - Web-accessible scripts where PATH might be influenced by requests
- Scripts processing untrusted input - Where an attacker has some control over the environment
When This Doesn’t Matter
For most use cases, the risk is negligible:
- Regular user scripts run in a normal shell
- Development scripts on your local machine
- Scripts where you control the entire execution environment
- Modern Linux systems with proper user privilege separation
If an attacker can already manipulate your PATH variable, you likely have a compromised environment where this is the least of your worries.
Best Practices
Here’s my recommendation based on the use case:
Use #!/usr/bin/env bash when:
- Writing portable scripts meant to run on different systems
- Creating open-source tools or utilities
- Writing scripts for development or personal use
- Portability is more important than the minimal security edge case
Use #!/bin/bash when:
- Writing system administration scripts for specific infrastructure
- Creating scripts that will run with elevated privileges
- You need absolute predictability about which bash version runs
- Security is paramount and you control the target systems
Never use shell scripts for:
- Setuid binaries—use compiled programs instead
- Security-critical operations requiring privilege escalation
- Anything where the stakes of compromise are high
The Verdict
For most developers writing most scripts, #!/usr/bin/env bash is the right choice. The portability benefits far outweigh the theoretical security risk in typical use cases.
The security concern, while valid in principle, is often overstated. If you’re writing system-level scripts or anything running with elevated privileges, use #!/bin/bash and ensure proper environment sanitization. Better yet, use a compiled language for security-critical tasks.
Remember: security is about threat modeling. Understand your environment, know your attack surface, and choose accordingly. For your average bash script, being able to run it on any Unix-like system without modification is probably more valuable than defending against an already-compromised PATH.