Bash 以及类似的 Shell todo
文档用途
介绍类 Bash Shell 的重要概念和常用技巧, 并做有关 shell 脚本的笔记.
参考资料
推荐阅读 The Missing Semester of Your CS Education.
或观看 YouTube 视频 Missing Semester IAP 2020.
将此文档作为完成的资料 Bash Reference Manual.
快捷键
TIP
更多快捷键详见 Bash Shortcuts.
| 快捷键 | 作用 |
|---|---|
Tab | 自动补全路径 |
上方向键 | 上一条指令, 前几条指令 |
Ctrl+X Ctrl+E | 使用默认 editor 编辑命令 |
Ctrl+C | 打断当前正在执行的命令 |
Ctrl+R | 搜索历史命令, 然后 Tab 或 Enter |
Ctrl+D | 退出当前 shell (相当于输入 exit) |
Ctrl+Z | 将当前进程挂起到后台并返回 |
用于移动光标的快捷键:
| 快捷键 | 作用 |
|---|---|
Ctrl+U | 删除光标左侧的所有输入内容 |
Ctrl+W | 删除光标左侧的一个单词 |
Ctrl+A | 将光标移动至行首 |
Ctrl+K | 删除光标右侧的的所有输入内容 |
Alt+B | 以单词为步长, 向左移动光标 |
Alt+F | 以单词为步长, 向右移动光标 |

重要概念
管道概念, 以及stdin,stdout,stderr环境变量概念, 以及如何设置临时或持久的环境变量
通过 export 命令可以打印当前 shell 会话中所有的环境变量和它们的值.
输入技巧
- 使用 sudo 执行上一条命令
sudo !!. - 返回上次跳转的路径
cd -. - 进入当前用户目录
cd ~或cd. - 如何多行编辑命令 (行尾
\, 进一步可使用EDITOR). - 命令与文件的输入输出, 例如
>,<<EOF,tee. - 约定俗成的命令 (例如查参命令帮助
-h,--help,help). - 若打算为某个选项提供多个参数, 则用逗号将参数分隔开.
- 使用
--来表示命令行选项到此为止, 后面的都是 non-option arguments. - 通过
&&连接两个命令, 则会让前一个命令成功执行后, 再执行后一个命令. - 通过
&连接两个命令, 会让两个命令同时执行.
若将&放在行尾, 则会将此命令放入后台执行, 效果类似nohup命令.
Shell 脚本
遇到错误时立即退出
使脚本在遇到任何非零退出状态的命令时立即退出:
set -e覆盖文本文件内容
当然可以使用 vim, 不过有些时候 docker 镜像被裁剪得厉害, 因此需要别的手段:
# 将 nice.txt 文件中的内容替换为 write content here.
echo "write content here" > nice.txt
# 将 good 添加在 nice.txt 的底部 (保留文件中原有的内容) .
echo good >> nice.txt用双层中括号做判断
INFO
参考 stackoverflow 中的 这条回答.
简单地讲, 如果所写的 shell 只用于 bash 及更新的 shell 中, 则建议使用双层中括号.
如果所写的 shell 可能会被 sh 执行, 那么不能使用双侧中括号. 此时应使用 " 包裹其中的项.
# bash 支持双层中括号语法.
if [[ $VARIABLE ]]; then
echo "VARIABLE is empty"
fi
# sh 不支持双侧中括号语法, 因此要用双引号包裹.
if [ -z "$VARIABLE" ]; then
echo "VARIABLE is empty"
fi判断文件是否存在
使用 -e, 当文件存在时, 返回 true.
if [ -e "file-path" ]; then
echo "nice"
echo "very nice"
else
echo "nonono"
fiif [ -e "file-path" ]; then echo "nice" && echo "very nice"; else echo "nonono"; fi判断路径是否为文件夹
使用 -d, 若路径为文件夹, 则返回 true.
如果路径为文件, 或路径不存在, 这返回 false.
if [ -d "dir-path" ]; then
echo "direcotry"
else
echo "not a directory"
fi判断字符串是否为空
使用 -z, 当字符串为空时, 返回 true.
string1=""
if [ -z "$string1" ]; then
echo "string1 is empty"
else
echo "string1 is not empty"
fi例如我们可以创建一个函数, 用于询问是否确定.
当直接回车时, 或输入 y 并回车时, 表示确定, 返回 true.
function ask() {
read -p "$1 (y/n): " resp
[ -z "$resp" ] || [ "$resp" = "y" ]
}判断返回值是否为 0
if ! my command; then
echo "Build failed"
fimy command
if [ $? -eq 0 ]; then
echo "返回值为 0, 说明上个命令执行成功"
fi
if [ $? -ne 0 ]; then
echo "返回值不为 0, 说明上个命令执行失败"
fiWARNING
不建议使用 $? 这种写法, 应该直接判断命令的返回值, 详见 SC2181.
显色不同颜色
# 以红色在命令中显示
red_echo() {
printf "\033[31m%s\033[0m" "$*"
}
# 使用方法
red_echo "这是红色文字"# 以黄色在命令行中显示
yellow_echo() {
printf "\033[33m%s\033[0m" "$*"
}
# 使用方法
yellow_echo "这是黄色文字"# 以绿色在命令行中显示
green_echo() {
printf "\033[32m%s\033[0m" "$*"
}
# 使用方法
green_echo "这是绿色文字"处理子命令
case $1 in
morning)
echo "Good Morning, $2!"
;;
evening)
echo "Good Evening, $2!"
;;
*)
echo "Hello, $1!"
;;
esac# 假设脚本位于 ./script.sh
$ sh ./script.sh morning Yahaha
Good Morning, Yahaha!
$ sh ./script.sh Amiao
Hello, Amiao!选项与参数
# 如果 flag 必须提供参数, 则应该用 `:` 将参数包裹, 例如这里的 a 和 b.
# 如果 flag 不接受参数, 那么其右侧不应该存在 `:` 字符.
while getopts ":a:b:cd" opt; do
case $opt in
a)
echo "-a was triggered, Parameter: $OPTARG"
;;
b)
echo "-b was triggered, Parameter: $OPTARG"
;;
c)
echo "-c was triggered."
;;
d)
echo "-d was triggered."
;;
# 用于匹配 "参数未定义" 的情况
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
# 用于匹配 "参数缺失" 的情况
:)
echo "Option -$OPTARG requires an argument." >&2
exit 1
;;
esac
done# 假设脚本位于 ./script.sh
$ sh ./script -a dummy
-a was triggered, Parameter: dummy
$ sh ./script -a
Option -a requires an argument.
$ sh ./script -c
-c was triggered.
$ sh ./script -z
Invalid option: -zfor 语句
# 列出当前路径下所有文件名
for file in ./*; do
echo $(basename $file)
doneif 语句
如何做相反的条件判断:
if [ -e "$FILEPATH" ]; then
echo "文件存在"
fi
# 在恰当的位置添加 ! 字符.
if [ ! -e "$FILEPATH" ]; then
echo "文件不存在"
fi如何将多个判断写在一起:
if [ -e "$FILEPATH_B" ] && [ -e "$FILEPATH_B" ]; then
echo "两个条件同时为 true"
fi
if [ -e "$FILEPATH_B" ] || [ -e "$FILEPATH_B" ]; then
echo "至少有一个条件为 true"
fi多行输入
我们可以用 EOF 语法, 实现多行内容的输入.
适用于 "需要输入多行, 但是不想创建文件" 这种场景.
并不必须用 EOF, 只要一头一尾用相同的即可. 只是大家约定俗成地使用 EOF.
cat << EOF
line 1
line 2
EOFcat << EOF > output.txt
line 1
line 2
EOF# 将多行作为 tee 命令的 stdin
tee output.txt << EOF
line 1
line 2
EOF另外如果在编写 shell 脚本, 那么也可以通过这种语法:
# 将 echo 输出的三行文本追加至 demo.log 文件末尾
{
echo "line one"
echo "line two"
echo "line three"
} >>"demo.log"笔记
判断 git 工程是否存在改动
if [ "$(git status -s)" ]; then
echo "local changes detected"
exit 1
fi获取当前脚本的上一级路径
# 获取此脚本所在的文件夹的路径.
PARENT_DIR=$(dirname "$(readlink -f "$0")")