Based on the Linux Master 1 exam syllabusReport an error
#!/bin/bash) is valid only on line 1, column 1 of the file. ./script.sh runs in a child shell, while source runs in the current shell.$0 (name) $1~$9 (arguments) $# (count) "$@" (each separated) $? (exit status, 0=success).test = [ ]. Use = for strings and -eq for numbers, and spaces are required around the brackets and operators.if ... fi and case ... esac (each block ends with ;;, and *) is the default). Loops use for/while/until.return gives only an exit status of 0~255. Pass data with echo + $(function), and isolate internal variables with local.If you do daily backups and log cleanup by hand every time, people get tired and make mistakes. A shell script is an interpreted program that the shell reads and executes one line at a time, with no separate compilation. This area is the center of the Level 1 practical exam.
#! is called the shebang (also hashbang).#!/bin/bash
# The line above is the shebang — it means this script should be run with /bin/bash
echo "Hello, Shell Script"#!/bin/bash # run with bash
#!/bin/sh # run with POSIX sh
#!/usr/bin/env python3 # find python3 in PATH and run it (more portable)There are three ways to run a script.
chmod +x script.sh # grant execute permission
./script.sh # ① run directly after granting permission (uses the shebang)
bash script.sh # ② pass it to bash as an argument (no permission needed)
source script.sh # ③ run in the current shell (variables remain in the current shell)./script.sh (direct execution) and source script.sh (sourcing) are decisively different. Direct execution launches a new child shell and runs inside it, so variables changed inside the script disappear when it ends. source, on the other hand, runs inside the current shell, so the variables and functions the script defines remain as they are. That is why you use source when applying environment setup scripts (the same principle as sourcing .bashrc on Day 1).
⚠️ Trap: The shebang's
#!must come at the very first line of the file, at the very start (line 1, column 1). If there is a blank line or space before it, it is treated as an ordinary comment and becomes invalid. Also, the interpreter path after#!must be an absolute path.
Variables in a script are the same as the shell variables you learned on Day 1. However, when assigning, there must be no spaces on either side of =.
#!/bin/bash
NAME="linux" # correct
COUNT=10
# NAME = "linux" # wrong! with spaces it is interpreted as a command named NAME
echo "$NAME version $COUNT"When you pass arguments to a script, they are received as positional parameters. This is the key to making scripts flexible.
| Variable | Meaning |
|---|---|
$0 | Script name |
$1 ~ $9 | The first ~ ninth arguments |
${10} | The tenth and later arguments (braces required) |
$# | The number of arguments passed |
$* | All arguments as one string |
$@ | All arguments, each separated |
$? | The exit status of the previous command (0=success) |
$$ | The PID of the current script |
$! | The PID of the last background job |
#!/bin/bash
echo "Script name: $0"
echo "First argument: $1, second argument: $2"
echo "Number of arguments: $#"
echo "All arguments: $@"
# Run: ./test.sh apple banana
# First argument: apple, second argument: banana / Number of arguments: 2Without quotes, $* and $@ seem to behave the same, but they differ decisively when wrapped in double quotes. "$*" becomes one string with all arguments joined by spaces ("apple banana"), while "$@" keeps each argument as an independent word ("apple" "banana"). When passing arguments on to another command as they are, you should almost always use "$@" so that arguments containing spaces don't break.
$? (exit status) is the core of a script's flow control. Every command leaves an exit status of 0~255 when it finishes, where 0 is success and anything other than 0 is failure. Conditionals branch by looking at this exit status. You can check the success or failure of the previous command immediately, as in grep "root" /etc/passwd; echo $?.
To decide "does the file exist", "are the numbers equal", or "is the string empty" in a conditional, use the test command or its alias [ ]. The two are exactly the same.
test -f /etc/passwd # exit status 0 if the file exists
[ -f /etc/passwd ] # exactly the same as above
echo $? # 0 (true) or 1 (false)| Operator | Condition for true |
|---|---|
-e file | The file exists (exists) |
-f file | It is a regular file |
-d file | It is a directory |
-r / -w / -x | Has read/write/execute permission |
-s file | Size is greater than 0 (not empty) |
-L file | It is a symbolic link |
| Operator | Condition for true |
|---|---|
-z str | String length is 0 (empty) |
-n str | String length is not 0 |
str1 = str2 | The two strings are equal |
str1 != str2 | The two strings differ |
| Operator | Meaning | Explanation |
|---|---|---|
-eq | equal | Equal |
-ne | not equal | Not equal |
-gt | greater than | Greater than |
-ge | greater or equal | Greater than or equal |
-lt | less than | Less than |
-le | less or equal | Less than or equal |
⚠️ Trap: Use
=and!=for string comparison, and-eqand-nefor numeric comparison. Do not mix them.[ "$a" = "$b" ]compares as strings, and[ "$a" -eq "$b" ]compares as numbers. One more thing — inside[ ]there must always be spaces on both sides of the brackets and of every operator.[$a=$b]and[ $a=$b ]are syntax errors. Also, it is safe to wrap variables in double quotes like"$a"so the syntax does not break when the value is empty.
bash also has [[ ]] (double brackets), which is more powerful than [ ]. [[ ]] is a bash-specific extension; inside it you can use &&, ||, pattern matching (== with wildcards), and regular expressions (=~), and word splitting and globbing of variables are suppressed automatically, making it safer. However, if POSIX sh compatibility is required, you must use [ ]. For Level 1, it is good to know both notations.
#!/bin/bash
score=$1
if [ "$score" -ge 90 ]; then
echo "Grade A"
elif [ "$score" -ge 80 ]; then
echo "Grade B"
elif [ "$score" -ge 70 ]; then
echo "Grade C"
else
echo "Retake"
fiif condition; then ... elif condition; then ... else ... fi.fi is "if" written backward and marks the end of the block (in the same way, case is closed with esac).# A practical example branching on whether a file exists
if [ -f /etc/nginx/nginx.conf ]; then
echo "The configuration file exists"
else
echo "The configuration file does not exist" >&2 # errors go to stderr
exit 1
fiif actually does not accept only [ ]; it accepts any command and branches on its exit status. You can use a command directly as the condition, as in if grep -q "root" /etc/passwd; then .... If the exit status is 0 it goes to then, otherwise to else. This is possible because [ ] itself is a command called test.
When a value is one of several cases, case is cleaner than stringing together a long if-elif.
#!/bin/bash
case "$1" in
start)
echo "Starting service"
;;
stop)
echo "Stopping service"
;;
restart|reload) # match several patterns with |
echo "Restarting service"
;;
*) # every other case (default)
echo "Usage: $0 {start|stop|restart}"
exit 1
;;
esac;;, and *) is the default that catches all remaining cases.*, ?, [ ]).Linux init scripts (the service control scripts in /etc/init.d/) are almost all written with the structure case "$1" in start|stop|restart as above. That is why commands like service nginx start and /etc/init.d/nginx restart work. The case statement is the standard pattern for building such a "command dispatcher".
#!/bin/bash
# Iterate over a list
for fruit in apple banana cherry; do
echo "Fruit: $fruit"
done
# Iterate over files with a wildcard
for file in *.log; do
echo "Processing: $file"
gzip "$file"
done
# Iterate over a sequence
for i in $(seq 1 5); do
echo "Iteration $i"
done
# C style (bash extension)
for ((i=1; i<=5; i++)); do
echo "Count $i"
done#!/bin/bash
count=1
while [ "$count" -le 5 ]; do
echo "Round: $count"
count=$((count + 1)) # increment with arithmetic expansion
done
# Read a file line by line (a very frequently used idiom)
while read line; do
echo "Line: $line"
done < /etc/hostsuntil is the opposite of while: it repeats while the condition is false and stops when it becomes true.
count=1
until [ "$count" -gt 5 ]; do # until count becomes greater than 5
echo "$count"
count=$((count + 1))
doneAlso learn the loop control keywords.
| Keyword | Behavior |
|---|---|
break | Exit the loop immediately |
continue | Skip the current iteration and go to the next |
exit n | Terminate the entire script (exit status n) |
In while read line; do ...; done < file, it is easy to get confused about the position where the input is connected with < after done. Also, if you feed the while through a pipe, as in cat file | while read line, the right side of the pipe runs in a subshell, so variables changed inside the loop are not reflected outside the loop (subshell variable isolation). So when reading a file, it is safer to use the done < file redirection instead of a pipe.
Repeated blocks of code are bundled into functions for reuse.
#!/bin/bash
# Function definition (two syntaxes)
greet() {
echo "Hello, $1" # $1 is the function's first argument
}
function backup() { # function keyword style (bash)
cp "$1" "$1.bak"
return 0 # return an exit status
}
# Call the functions — arguments separated by spaces, no parentheses
greet "admin"
backup /etc/hosts
echo "Backup result code: $?" # check the function's return value with $?$1, $2, $#, just like a script's.$1 inside a function is not the script's $1, but the argument passed to the function.When a shell function gives back a value with return, it can return only an exit status of 0~255 (it cannot return ordinary data). To return a string or a calculation result, print it with echo inside the function and capture it on the calling side with command substitution: result=$(my_func). Remember the division of roles: return is for success/failure signals, and echo+$() is for passing data.
Variables declared inside a function are also global by default, so they affect the whole script. To make a local variable valid only inside the function, add the local keyword, as in local var=value. To prevent bugs where a function unintentionally overwrites a global variable in a large script, the habit of using local for variables inside functions is important.
The key to preparing for the practical exam is to write and run scripts yourself and run into errors. Tomorrow we do a comprehensive review of all of Week 3.
#!) : The first line that tells which interpreter should run this file. It is invalid unless on line 1, column 1.$1, $2... that receive the arguments passed to a script or function.$# / $@ / $* : Number of arguments / arguments each separated / arguments as one string. When passing them on, "$@" is safe.$? : The 0~255 success/failure signal left by the previous command. 0 is success, and conditionals branch on it.test / [ ] : A command that evaluates a condition and answers with an exit status. They are the same thing, and spaces on both sides are required.[[ ]] : The bash-only enhanced conditional expression. It allows &&, ||, pattern matching, and regex (=~), and is safer.fi / esac : Keywords that close if and case blocks. They are if and case written backward.;; : The marker that ends each pattern block in a case. *) is the default that catches everything else.until : The opposite of while. It runs while the condition is false and stops when it becomes true.break / continue : Exit the loop immediately / skip only this iteration.local : The keyword that declares a local variable valid only inside a function. Without it, the variable becomes global.Click a choice to reveal the answer and explanation.
Question 1
What does #!/bin/bash on the first line of a shell script do?
Question 2
Which positional parameter holds the number of arguments passed to the script?
Question 3
Which operator in the test command compares whether two numbers are equal?
Question 4
In the following case statement, which pattern handles all remaining cases (default)? ```bash case "$1" in start) echo "Start";; stop) echo "Stop";; ???) echo "Other";; esac ```
Question 5
Which variable holds the success status (exit status) of the previous command, and which value means "success"?
Question 6
Which is the correct cause of a syntax error when using [ ] (test)?
Question 7
What is the correct way to pass a calculated result string from a shell function to the caller?