Script foundations
Create an executable script
Write a Bash script with an interpreter line, make it executable, and run it from a predictable directory.
10 minute lesson
A shell script is a text file containing commands. When you run it, the operating system starts an interpreter and feeds it the file. The interpreter line on the first line — often called the shebang — tells the operating system which program should read it.
Write the script
Create a file named hello:
#!/usr/bin/env bash
printf '%s\n' 'hello from a script'
The first line must start with #!. I use #!/usr/bin/env bash instead of a hardcoded /bin/bash because env finds Bash wherever it lives on PATH. On macOS the system Bash in /bin is ancient, and the current one from Homebrew sits somewhere else entirely. env picks the right one on each machine.
printf '%s\n' 'text' is the predictable way to print. Unlike echo, it never reinterprets escapes or flags hidden inside the value.
Make it executable and run it
A fresh file is not executable. Give yourself execute permission, then run it:
chmod u+x hello
./hello
# hello from a script
If you skip chmod, the shell answers Permission denied. That error means the file exists but you’re not allowed to execute it — the fix is the permission, not the path.
Why the ./ prefix
The ./ prefix names the file in the current directory. Without a slash, the shell searches directories in PATH, and the current directory is not on that list:
hello
# bash: hello: command not found
./hello
# hello from a script
This trips up everyone once. command not found for a file sitting right there means you forgot the ./.
Do not add the current directory to PATH globally to “fix” this. Any directory you cd into could then shadow real commands with a malicious file named ls. Run local scripts through an explicit path.
The shebang stays in charge
You can also run a script without execute permission by naming the interpreter yourself:
bash hello
# hello from a script
That works, but it ignores the shebang. If someone runs a Bash-only script with sh this way, the syntax breaks in confusing places. Making the file executable and running ./hello keeps the interpreter line in control.
Lesson completed