POSIX Shell Scripts: Variables, Conditions, Loops, and APIsPOSIX Shell脚本:变量、条件、循环与API
Convert digits using tr: 0-4 → <, 6-9 → >, 5 unchanged.
使用 tr 转换数字:0-4 → <, 6-9 → >, 5 保持不变。
| Part部分 | Meaning含义 |
|---|---|
tr '01234' '<' |
Map characters 0,1,2,3,4 to <将字符 0,1,2,3,4 映射为 < |
| |
Pipe output to next command将输出通过管道传给下一个命令 |
tr '6789' '>' |
Map characters 6,7,8,9 to >将字符 6,7,8,9 映射为 > |
💡 Key Insight: tr automatically repeats the last character in SET2 to match the length of SET1. So tr '01234' '<' works the same as tr '01234' '<<<<<'.
💡 关键洞察: tr 会自动重复 SET2 的最后一个字符来匹配 SET1 的长度。所以 tr '01234' '<' 等同于 tr '01234' '<<<<<'。
Write echon.sh that prints a string N times with proper error handling.
编写 echon.sh,打印字符串 N 次,并正确处理错误。
Tells the system to use dash shell to execute this script. Must be the first line, no spaces before #.
告诉系统使用 dash shell 执行此脚本。必须是第一行,# 前不能有空格。
| Variable变量 | Meaning含义 |
|---|---|
$# |
Number of arguments参数个数 |
$1, $2, ... |
1st, 2nd, ... argument第1个、第2个...参数 |
$@ |
All arguments所有参数 |
$? |
Exit status of last command上一个命令的退出状态 |
| Operator运算符 | Meaning含义 | Example示例 |
|---|---|---|
-eq |
Equal (numbers)等于(数字) | [ $a -eq 5 ] |
-ne |
Not equal (numbers)不等于(数字) | [ $# -ne 2 ] |
-lt |
Less than小于 | [ $i -lt 10 ] |
-gt |
Greater than大于 | [ $1 -gt 0 ] |
-f |
Is a regular file是普通文件 | [ -f "$file" ] |
-d |
Is a directory是目录 | [ -d "$dir" ] |
*[!0-9]* matches if the string contains any non-digit character.
*[!0-9]* 匹配字符串中包含任何非数字字符的情况。
⚠️ Common Mistakes:
⚠️ 常见错误:
[ ] must have spaces inside: [ $i -lt 10 ] not [$i -lt 10][ ] 内部必须有空格:[ $i -lt 10 ] 而不是 [$i -lt 10]i=0 not i = 0 (no spaces!)变量赋值:i=0 而不是 i = 0(不能有空格!)if needs a fi, every case needs an esac每个 if 需要一个 fi,每个 case 需要一个 esacCategorize files by line count: small (<10), medium (10-99), large (≥100).
按行数对文件分类:小文件(<10行)、中文件(10-99行)、大文件(≥100行)。
* is a wildcard that matches all files in the current directory.
* 是通配符,匹配当前目录下的所有文件。
< redirects file to stdin, so wc only outputs the count.
< 将文件重定向到标准输入,所以 wc 只输出计数。
Captures command output into a variable.
将命令输出捕获到变量中。
Append $file to the small variable.
将 $file 追加到 small 变量。
💡 Why [ -f "$file" ]? The * wildcard matches both files AND directories. We use -f to check if it's a regular file before processing.
💡 为什么用 [ -f "$file" ]? * 通配符匹配文件和目录。我们用 -f 在处理前检查它是否是普通文件。
Fetch course data from UNSW Handbook API and filter by prefix.
从 UNSW Handbook API 获取课程数据并按前缀过滤。
| Option选项 | Meaning含义 |
|---|---|
-s |
Silent mode (no progress bar)静默模式(无进度条) |
-L |
Follow redirects跟随重定向 |
| Operator运算符 | Meaning含义 | Example示例 |
|---|---|---|
. |
Current object当前对象 | .data |
[] |
Iterate array遍历数组 | .results[] |
| |
Pipe (jq internal)管道(jq内部) | .data | .results |
select() |
Filter过滤 | select(.type == "Course") |
startswith() |
String starts with字符串以...开头 | startswith("COMP") |
map() |
Transform array elements转换数组元素 | map(ascii_downcase) |
any() |
Check if any matches检查是否有任何匹配 | any(. == "x") |
💡 Debugging Tips:
💡 调试技巧:
bash -n script.sh to check syntax运行 bash -n script.sh 检查语法set -x at the top to trace execution在顶部添加 set -x 跟踪执行echo "DEBUG: $var" to see variable values使用 echo "DEBUG: $var" 查看变量值| Pattern模式 | When to Use何时使用 | Example示例 |
|---|---|---|
if [ ] |
True/false conditions真/假条件 | if [ $x -gt 5 ] |
case |
Multiple value matching多值匹配 | case "$1" in ... esac |
while |
Loop until condition false循环直到条件为假 | while [ $i -lt $n ] |
for |
Iterate over items遍历项目 | for file in * |
$() |
Command substitution命令替换 | lines=$(wc -l < file) |
$((expr)) |
Arithmetic算术运算 | i=$((i + 1)) |
Every concept the Lab 03 quiz tests, organised by category. Use this as a final-pass study sheet before taking the quiz. Lab 03 测验考查的每一个知识点,按类别整理。做测验前用这个表过一遍即可。
tr — Translate / Squeeze / Delete1. tr — 翻译 / 压缩 / 删除| Concept | Detail | Example |
|---|---|---|
| Positional mapping | SET1 char at position i → SET2 char at position iSET1 第 i 个字符 → SET2 第 i 个字符 | tr "abc" "123" → b becomes 2 |
| SET2 shorter than SET1 | tr auto-repeats SET2's last char to match SET1 lengthtr 自动用 SET2 最后一个字符填充 | tr "01234" "<" → all map to < |
-d delete | Delete every char in SET1删除 SET1 中所有字符 | tr -d "0-9" removes all digits |
-s squeeze | Collapse repeated occurrences of SET1 chars to one将 SET1 中重复出现的字符压缩为一个 | tr -s " " → multiple spaces become one |
| Pipeline order matters | Each tr operates on the output of the previous one每个 tr 处理上一个的输出 | echo 12345 | tr "0-4" "<" | tr "6-9" ">" → <<<<5 |
| Symbol | Meaning | Notes |
|---|---|---|
#!/bin/dash | Shebang — tells OS which interpreter to useShebang — 告诉系统使用哪个解释器 | Must be the first line, no leading spaces必须是第一行,不能有空格 |
$0 | Script name脚本名 | — |
$1, $2, … | 1st, 2nd positional argument第 1、第 2 位置参数 | Always quote: "$1"永远加引号 "$1" |
$# | Argument count参数个数 | — |
$@ | All arguments所有参数 | — |
name=value | Variable assignment变量赋值 | No spaces around == 两边不能有空格 |
$var | Read variable value读取变量值 | — |
$((expr)) | Arithmetic expansion (math)算术扩展(数学运算) | i=$((i + 1)) |
$(cmd) | Command substitution — capture stdout as a string命令替换 — 把 stdout 抓成字符串 | lines=$(wc -l < file) |
if / case Conditionals3. if / case 条件语句The [ ] rule: spaces are required inside [ ]. [$x -eq 5] is wrong; [ $x -eq 5 ] is correct.[ ] 规则:方括号内必须有空格。[$x -eq 5] 错;[ $x -eq 5 ] 对。
Block endings are reverse spellings: if → fi, case → esac.代码块结束用反向拼写:if → fi,case → esac。
| Operator | Meaning | Use on |
|---|---|---|
-eq | equal | numbers |
-ne | not equal | numbers |
-lt | less than | numbers |
-le | less than or equal | numbers |
-gt | greater than | numbers |
-ge | greater than or equal | numbers |
= | equal | strings |
!= | not equal | strings |
-z | string is empty | strings |
-n | string is non-empty | strings |
-f path | path exists AND is a regular file | file tests |
-d path | path exists AND is a directory | file tests |
-e path | path exists (any type) | file tests |
case patterns:case 模式:
* — match anything (zero or more chars)匹配任意字符? — match exactly one char匹配恰好一个字符[abc] — one of a, b, ca、b、c 中的一个[!0-9] — one char that is NOT a digit一个非数字字符*[!0-9]* — string contains any non-digit char字符串包含任何非数字字符;; — end of a pattern's command block (like break)一个模式命令块的结束(类似 break)while & for4. 循环 — while 与 for| Form | Pattern | Notes |
|---|---|---|
while [ cond ]; do … done | condition-controlled loop | Need do + done; spaces inside [ ]需要 do + done;[ ] 内有空格 |
for var in list; do … done | iterate over a list | for file in * — wildcards expand |
for i in 1 2 3 4 5 | POSIX-portable count | Works in dash; for ((;;)) is bash-onlydash 兼容;for ((;;)) 只支持 bash |
i=$((i + 1)) | increment counter | No i++ in POSIX shellPOSIX shell 没有 i++ |
small="$small $file" | append to a string | String concatenation, with space separator字符串拼接(带空格分隔) |
break / continue | exit / skip iteration | — |
curl & jq — APIs & JSON5. curl 与 jq — API 与 JSON| Tool / Flag | Meaning | Example |
|---|---|---|
curl -s | silent (no progress)静默(无进度条) | — |
curl -L | follow redirects跟随重定向 | — |
curl -sL | silent + follow redirects (typical pairing)静默 + 跟随重定向(常用组合) | curl -sL https://api/... |
jq -r | raw output (no JSON quotes)原始输出(不带 JSON 引号) | — |
.field | access JSON field访问 JSON 字段 | .data.results |
.array[] | iterate over array elements遍历数组元素 | .data.results[] |
select(cond) | filter — keep items where cond is true过滤 — 保留条件为真的项 | select(.type == "Course") |
startswith("X") | true if string begins with X字符串以 X 开头返回 true | select(.code | startswith("COMP")) |
| Inject shell var into jq | Break out of jq's single-quoted string, inject "$var", re-enter跳出 jq 单引号串,插入 "$var",再回到串内 | jq '... | .name == "'"$2"'"' |
| Command / Issue | Effect / Cause |
|---|---|
bash -n script.sh | Syntax check only — does not execute仅语法检查 — 不实际运行 |
set -x (or bash -x script.sh) | Trace mode — print every command before running跟踪模式 — 运行前打印每条命令 |
set -e | Exit immediately on any command failure任一命令失败就立即退出 |
"Permission denied" running ./script.sh | Missing execute bit — fix with chmod +x script.sh缺少执行权限 — 用 chmod +x script.sh 修复 |
| Run without exec bit不加执行权限运行 | Invoke interpreter explicitly: dash script.sh or bash script.sh显式调用解释器:dash script.sh 或 bash script.sh |
⚡ Top mistakes graders see⚡ 阅卷常见错误
= in assignment (x = 5 ✗ → x=5 ✓)赋值时 = 两边带空格(x = 5 ✗ → x=5 ✓)[ ][ ] 内部缺空格fi / esac / done忘了 fi / esac / done"$1" when filenames may contain spaces文件名可能含空格时没给 "$1" 加引号i++ (not POSIX) instead of i=$((i + 1))用 i++(非 POSIX)而不是 i=$((i + 1))