homework
1. ls 的 -l 选项(flag)作用是什么?运行 ls -l / 并观察输出。每一行最前面的 10 个字符分别代表什么?(提示:man ls)
ls -l 中的 -l 表示 long format,作用是以长格式显示文件或目录的信息。
它会显示:文件权限、链接数、所有者、所属组、文件大小、修改时间和文件名。
最前面的 10 个字符
例如:
drwxr-xr-x
可以拆成:
d rwx r-x r-x
含义如下:
第 1 个字符:文件类型
第 2-4 个字符:文件所有者的权限
第 5-7 个字符:所属组的权限
第 8-10 个字符:其他用户的权限
第 1 个字符常见含义
d 目录
- 普通文件
l 符号链接
后 9 个字符表示权限
后 9 个字符表示权限,分成三组:
rwx r-x r-x
分别表示:
文件所有者 所属组 其他用户
权限字符含义
r 可读
w 可写
x 可执行;对目录来说表示可以进入
- 没有该权限
2. 在命令 find ~/Downloads -type f -name "*.zip" -mtime +30 中,*.zip 是一个「glob」。什么是 glob?新建一个测试目录并创建一些文件,试试 ls *.txt、ls file?.txt、ls {a,b,c}.txt 等模式。参见 Bash 手册中的 Pattern Matching。
Glob 是 Shell 的文件名匹配模式,用来按规则匹配一批文件名。
在这个命令中:
find ~/Downloads -type f -name "*.zip" -mtime +30
*.zip 表示匹配所有以 .zip 结尾的文件名。这里加引号是为了防止 Shell 提前展开,让 find 自己按 -name 的规则匹配文件名。
常见模式
* 匹配任意长度的任意字符
? 匹配单个任意字符
[abc] 匹配 a、b、c 中任意一个字符
[a-z] 匹配 a 到 z 范围内的一个字符
{a,b,c} 展开为 a、b、c 三种写法
测试命令
mkdir glob-test
cd glob-test
touch a.txt b.txt c.txt
touch file1.txt file2.txt fileA.txt file10.txt
ls *.txt
ls file?.txt
ls {a,b,c}.txt
结果理解
ls *.txt 匹配所有 .txt 文件
ls file?.txt 匹配 file 后面只有 1 个字符的 .txt 文件
ls {a,b,c}.txt 等价于 ls a.txt b.txt c.txt
3. '单引号'、"双引号" 和 $'ANSI 引号' 有什么区别?写一条命令,输出一个同时包含字面量 $、! 和换行符的字符串。参见 Quoting。
'单引号':几乎全部按字面量处理,不展开变量,也不识别 \n。
"双引号":保留整体,但仍会展开 $变量、$(命令) 等内容。
$'ANSI 引号':不做变量展开,但会识别 \n、\t 等转义字符。
关键命令:
printf '%s' $'literal $ and !\nnew line\n'
输出效果:
literal $ and !
new line
这里 $ 和 ! 是字面量,\n 会变成真正的换行。
默认优先用单引号 '...',需要变量展开时用双引号 "...",需要写换行、制表符这类转义字符时用 $'...'。
4. Shell 有三条标准流:stdin(0)、stdout(1)、stderr(2)。运行 ls /nonexistent /tmp,把 stdout 和 stderr 分别重定向到两个文件。你将如何把两者都重定向到同一个文件?参见 Redirections。
分别重定向:
ls /nonexistent /tmp >stdout.log 2>stderr.log
这里 > 等价于 1>,表示正常输出;2> 表示错误输出。
都重定向到同一个文件:
ls /nonexistent /tmp >all.log 2>&1
>all.log 先把 stdout 指向文件,2>&1 再让 stderr 跟随 stdout。顺序不能反。
5. $? 保存上一条命令的退出状态(0 表示成功)。&& 仅在前一条成功时执行后一条;|| 仅在前一条失败时执行后一条。写一个一行命令:仅当 /tmp/mydir 不存在时才创建它。参见 Exit Status。
$? 保存上一条命令的退出状态:0 表示成功,非 0 表示失败。
&&:前一条成功,才执行后一条。
||:前一条失败,才执行后一条。
关键命令:
[[ -d /tmp/mydir ]] || mkdir /tmp/mydir
含义是:如果 /tmp/mydir 已经存在,就什么都不做;如果不存在,才执行 mkdir /tmp/mydir。
6. 写一个脚本,接收文件名参数($1),用 test -f 或 [ -f ... ] 检查该文件是否存在,并根据结果输出不同提示。参见 Bash Conditional Expressions。
脚本内容:
#!/usr/bin/env bash
file="$1"
if [ -f "$file" ]; then
echo "文件存在:$file"
else
echo "文件不存在或不是普通文件:$file"
fi
运行方式:
bash check_file.sh test.txt
如果要直接用 ./check_file.sh test.txt 运行,需要先添加可执行权限:
chmod +x check_file.sh
chmod 是 change mode,表示修改文件权限;+x 表示添加可执行权限。
7. 在脚本的 set 选项(flag)里加入 -x 会发生什么?写个简单脚本试试并观察输出。参见 The Set Builtin。
set -x 会开启调试追踪模式:每执行一条命令前,Bash 会先把这条命令打印出来。
脚本内容:
#!/usr/bin/env bash
set -x
name="jasper"
echo "hello $name"
file="test.txt"
[ -f "$file" ] && echo "文件存在" || echo "文件不存在"
运行:
bash debug.sh
输出中,前面带 + 的行是 Bash 打印的执行过程;不带 + 的行才是命令本身的正常输出。
作用:看清脚本执行了哪些命令,以及变量展开后变成了什么。
8. xargs 会把 stdin 的每一行转换为命令参数。结合 find 和 xargs(不要用 find -exec),找出目录中所有 .sh 文件,并用 wc -l 统计每个文件行数。加分项:正确处理文件名中的空格。(提示:-print0 和 -0)参见 man xargs。
关键命令:
find . -type f -name '*.sh' -print0 | xargs -0 wc -l
拆解:
find .:从当前目录开始查找-type f:只找普通文件-name '*.sh':只找.sh文件wc -l:统计行数xargs:把前一个命令的输出变成后一个命令的参数-print0:find用空字符\0安全输出文件名-0:xargs用空字符\0安全读取文件名
-print0 和 -0 配套使用,可以正确处理带空格的文件名。
9. jq 是处理 JSON 的强大工具。用 curl 获取示例数据 https://microsoftedge.github.io/Demos/json-dummy-data/64KB.json,再用 jq 提取 version 大于 6 的人员姓名。(提示:先 jq . 看结构;再试 jq '.[] | select(...) | .name')
先看结构:
curl -s https://microsoftedge.github.io/Demos/json-dummy-data/64KB.json | jq .
关键命令:
curl -s https://microsoftedge.github.io/Demos/json-dummy-data/64KB.json \
| jq -r '.[] | select(.version > 6) | .name'
拆解:
curl -s URL:获取 JSON,关闭进度输出jq .:格式化查看整个 JSON.[]:遍历数组里的每一项select(.version > 6):筛选version大于6的对象.name:提取name字段-r:输出纯文本,去掉字符串双引号