Your first script
Shebang, chmod +x, and running it.
You already know how to type commands. You run ls, you run date, you hit Enter, something happens. A shell script takes that same skill and saves it. Instead of retyping the same five commands every morning, you write them down once in a file and run the file. That is the whole idea, and everything else in this lesson is detail stacked on top of it.
A script is your commands, saved in a file
A recipe card is a good picture of it. A recipe is an ordered list of steps that anyone can follow to get the same dish. A shell script is an ordered list of commands the shell follows to get the same result. The shell is the program reading your typing right now (on Linux that is usually bash, the Bourne Again Shell). A script is a plain text file holding the exact commands you would have typed, and the shell runs them top to bottom, one line at a time. Let us make the smallest one that does something.
# hello.sh - my very first scriptecho "Hello from my first script"
Two lines, two ideas. The second line uses echo, a command that prints whatever you hand it straight back to the screen. The first line starts with a # (hash), which marks a comment: the shell ignores everything from the # to the end of that line. Comments are notes for humans, not instructions for the machine. Use them to explain why a line exists, not what it obviously does, because the person reading your script at 3 a.m. during an outage might be you.
That worked because you handed the file to bash directly and told it to read the file as a list of commands. It runs with no special setup. But typing bash in front of the name every time gets old, and it is not how real tools on your system behave. You do not type bash /usr/bin/ls, you type ls. To bring your script up to that level, where you run it by name, you need two more things: a shebang line and the execute permission.
The shebang: telling the system what runs this file
A text file does not announce what language it is written in. Open one cold and it could be Bash, Python, Perl, or a shopping list. The shebang is the label that settles the question, the way a language sticker on a boxed appliance tells you which manual to reach for. When you run a file directly, the operating system reads the very first line, and if it begins with the two characters #! (pronounced "shebang", short for hash-bang), it treats the rest of that line as the path to the program that should run the file. So the first line is a message to the system, not a command for Bash.
#!/usr/bin/env bash# hello.sh - my very first scriptecho "Hello from my first script"
Read #!/usr/bin/env bash from left to right: run this file with bash, and find bash by asking env. You could hardcode #!/bin/bash instead, and that often works, but Bash sits in different places on different machines (/bin/bash on most Linux, and a /usr/local/bin or Homebrew path on a Mac). /usr/bin/env is a small program that looks up bash in your PATH and starts the first one it finds, so the same script runs on more machines without editing. One catch: because # normally starts a comment, the shebang only does its special job on the literal first line of the file. Put a blank line or a comment above it and it turns into an ordinary comment, and the effect is lost.
chmod +x: giving the file permission to run
Every file on Linux carries a set of permissions, like a keycard that records who may read it, change it, and run it. A fresh text file can be read and written, but it is not marked runnable, because most files are data, not programs. chmod (change mode) is the command that edits those permissions, and +x adds the execute permission: the "yes, this may be launched as a program" flag. Watch the file's first column before and after.
Before, the permissions read -rw-r--r--. After, they read -rwxr-xr-x. Those new x characters are the execute permission you just added, granted to the owner, the group, and everyone else. From the system's point of view, this file is now a program it is allowed to start. From a security point of view, hand out only the access a file truly needs. It is tempting to reach for chmod 777, which lets every account on the machine read, write, and run the file, but that also lets any other user rewrite your script, so next time you run it you run their version. chmod +x does the job without that risk. To lock it down further, chmod 750 keeps full read, write, and run for you, lets your group read and run the file but not change it, and shuts everyone else out completely.
Two ways to run it, and why the ./ is there
Now you can run the script by pointing at the file with ./hello.sh. That leading ./ trips up almost everyone at first, so here is exactly what it means and why it is not optional.
./ means "in this folder, right here". The single dot is shorthand for the current directory, and the slash separates it from the filename. So ./hello.sh is a precise address: the file named hello.sh in the directory you are standing in. Give the shell a path like that and it goes straight to the file. Now try the same command without the dot-slash.
Same file, same folder, and the shell says it cannot find it. Here is why. When you type a bare name like ls or hello.sh, the shell does not look in your current folder. It searches PATH, an environment variable (a named setting the shell keeps in memory) that holds a list of directories separated by colons, and it runs the first matching program it finds there. Your current folder is not on that list, so a bare hello.sh is invisible to the search even with the file right in front of you. Writing ./ skips the search entirely by naming the file's location outright. You can print your own PATH to see the list.
Leaving the current directory off PATH is a deliberate safety decision, and it matters more than it looks. Suppose your current folder were searched. An attacker who could drop a file into a shared directory could name it ls, or git, or date. The moment you changed into that folder and typed the real command out of habit, you would run their file instead of the trusted system tool. Keeping the current directory off PATH means typing a command name always finds the real program, never a stray file sitting in whatever directory you happen to be in. The small annoyance of writing ./ is the visible edge of that protection.
So you have two ways to run a script, and both are fine. bash hello.sh hands the file to Bash as input and needs neither a shebang nor the execute bit, which makes it handy for a quick test. ./hello.sh runs the file as a program in its own right, using the shebang to pick the interpreter and the execute bit for permission. Real tools ship the second way.
Your greeting script
Put the pieces together into something with a little life in it. This script greets whoever runs it and prints the current date and time.
#!/usr/bin/env bash# greet.sh - say hello to the current user and show the datename="$(whoami)" # whoami prints the username running this scriptecho "Hello, $name!"echo "Today is $(date)"
Two new pieces here. whoami is a command that prints the name of the user running it. Wrapping it in $( ... ) is command substitution: Bash runs the command inside the parentheses and drops its output right into the line, so name ends up holding your username. Then $name inside the double quotes pastes that value into the greeting. The same trick prints the clock: $(date) runs the date command and inserts whatever it reports. Make it executable and run it.