1. 常用命令

1.1 文件管理

  1. CTRL + C : 取消命令,并且换行
  2. CTRL + U : 清空本行命令
  3. TAB : 补全命令和文件目录,快速双击,显示备选选项
  4. ls : 列出当前目录下所有文件及目录
  5. pwd : 显示当前完整路径
  6. cd XXX : 进入 XXX 目录,cd .. 进入上一层目录
  7. cp A B : 将文件 A 复制成 BAB 可以是路径
  8. mkdir X : 创建目录 X
  9. rm XXX : 删除普通文件,rm -r XXX 删除文件夹
  10. mv A B : 将文件 A 移动到 B,可用于重命名
  11. touch : 创建文件
  12. cat XXX : 显示文件 XXX 中的内容

文件权限

  1. chmod

    • chmod +x xxx 添加执行权限
    • chmod -x xxx 去除执行权限
    • chmod 777 xxx 设置 777 权限
    • chmod 777 xxx -R 给目录设置权限

1.2 系统状况

  1. top 查看所有进程信息,(Linux 系统的任务管理器)
    • 打开后,输入 M :按使用内存排序(M = memory),大写
    • 打开后,输入 P :按使用 CPU 排序(P = process)
    • 打开后,输入 m :切换内存占用展示模式
    • 打开后,输入 q :退出
  2. df -h 查看硬盘使用情况
  3. free -h 查看内存使用情况
  4. du -sh 查看当前目录占用硬盘空间
  5. ps aux 查看所有进程
  6. kill -9 pid 杀死进程编号为 pid 的进程,(结束运行)
  7. netstat -nt 查看所有网络连接
  8. w 列出当前登录用户
  9. ping www.baidu.com ping 测试

1.3 文件检索

  1. find /path/to/directory/ -name '*.py' 搜索某个路径下的所有 python 文件

  2. grep xxxstdin 中读入若干行数据,如果某行包含 xxx 输出该行

  3. wc 统计行数、单词数、字节数

    • 既可以从 stdin 中读入数据,也可以在命令行参数中传入文件名列表
    • wc -l -w -c 分别是统计行数(line)、单词数(word)、字节数(char)
  4. tree 展示当前目录结构

    • tree /path/to/directory/
    • tree -a 展示隐藏文件
  5. ag xxx 搜索当前目录下所有文件,检索 xxx 字符串,需要安装 silversearcher-ag

  6. cut 分割一行内容

    • stdin 中读入多行数据

    • echo $PATH | cut -d ':' -f 3,5 输出 PATH 中用 : 分割的字符串的第三个和第五个,仍然保留 : 间隔,相当于:

      1
      2
      3
      4
      5
      6
      7
      // javascript
      const str = 'aaa:bbb:ccc:ddd:eee';
      const arr = str.split(':'); // 分割成数组
      const l = 3;
      const r = 5;
      const res1 = arr[l - 1] + ':' + arr[r - 1];
      console.log(res1); // ccc:ddd
    • echo $PATH | cut -d ':' -f 3-5 输出 PATH 中用 : 分割的字符串的第三个到第五个,仍然保留 : 间隔,相当于:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      const str = 'aaa:bbb:ccc:ddd:eee';
      const arr = str.split(':');
      const l = 3;
      const r = 5;
      res2 = '';
      for (let i = l - 1; i < r; i++) {
      if (i === r - 1) {
      res2 += arr[i];
      } else {
      res2 += arr[i] + ':';
      }
      }
      console.log(res2); // ccc:ddd:eee
    • echo $PATH | cut -c 3,5 输出 $PATH 的第三个和第五个字符,-c(char)

    • echo $PATH | cut -c 3-5 输出 $PATH 的第三个到第五个字符,相当于:

      1
      2
      3
      4
      5
      const str = 'aaa:bbb:ccc:ddd:eee';
      const l = 3;
      const r = 5;
      // string.slice 方法,截取下标 l-1 至 r,但不包含 r
      console.log(str.slice(l - 1, r));
  7. sort 将每行内容按字典序排列

    • 可以从 stdin 中读取多行数据
    • 可以从命令行参数中读取文件名列表
  8. xargsstdin 中的数据用空格或者回车分割成命令行参数,可以用于删除文件

    1
    2
    3
    4
    5
    find test/ -name '*.js' | xargs cat | wc -l # 查找 test/ 目录下的 js 文件,统计代码行数
    # 这里代码行数会比真实行数少一行

    # 删除全部 js 文件
    find test/ -name '*.js' | xargs rm

1.4 查看文件内容

  1. more 浏览文件内容

    • 回车:下一行
    • 空格 | KeyDown :下一页
    • b 上一页
    • q 退出
  2. lessmore 类似,功能更全面

    • 回车:下一行
    • y 上一行
    • Page Down 下一页
    • Page Up 上一页
    • q 退出
  3. head -3 XX 展示 XX 的前三行内容

    支持从 stdin 读入内容

  4. tail -3 XX 展示 XX 末尾三行内容

    支持从 stdin 读入内容

1.5 工具

  1. md5sum 计算 md5 哈希值

    • 可以从 stdin 读入内容
    • 可以从命令行参数传入文件名列表
  2. time command 统计 command 命令执行时间

  3. ipython3 交互式 python3 环境,可以当做计算器,或者批量管理文件

  4. watch -n 0.1 command0.1 秒执行一次 command 命令

  5. tar 解压缩文件

    • -c 创建压缩文件(create)
    • -x 解压(extract)
    • -f 指定归档(需要被压缩)的文件名,一般是命令最后的文件名
    • -v 显示操作过程(verbose)
    • -t 查看(list)压缩文件的内容而不解压
    • -z 使用 gzip 压缩或者解压缩 .tar.gz 文件
    • -j 使用 bzip2 压缩或者解压缩 .tar.bz2 文件
    • -J 使用 xz 压缩或者解压 .tar.xz 文件,==高压缩率==
    • -C 指定解压的目录
    • --exclude 在归档时拍出某个目录或者文件

    常用组合

    • 创建 .tar.gz 压缩文件

      1
      tar -czvf archive.tar.gz /path/to/directory
    • 解压 .tar.xz 文件到指定目录

      1
      tar -xJvf archive.tar.xz -C /path/to/destination
    • 仅查看 .tar 文件内容

      1
      tar -tf archive.tar
  6. diff XX YY 查看文件 XXYY 的不同点

1.6 管道 |

简单来说管道就是重定向,可以将前一个命令的 stdout 重定向到下一个命令的 stdin

注意事项

  • 管道命令只对 stdout 处理,不处理 stderr
  • 管道右边命令必须接受 stdin
  • 多个管道可以串联

与文件重定向的区别

  • 文件重定向:左边是命令,右边是文件
  • 管道:左右两边都是命令,左边开启了 stdout ,右边开启了 stdin

示例

统计当前目录下所有 js 文件中行内包含字符串 const 的行数

1
2
3
4
# 合并 xargs grep,但是 xargs 必须在 grep 左边
find ./ -name '*.js' | xargs grep 'const ' | wc -l
# 分开写法
find ./ -name '*.js' | xargs cat | grep 'const ' | wc -l

2. Vim 使用

2.1 三种模式

2.1.1 一般命令模式

默认模式,按 ESC 键进入一般命令模式,命令输入方式:按不同字符即可进行不同操作,可以复制、粘贴、删除等。

2.1.2 编辑模式

在一般命令模式下按 I 键进入编辑模式。

2.1.3 命令行模式

在一般命令模式下按 :/? 三个按键中任意一个进入命令行模式,可以进行查找、替换、保存、退出、配置编辑器等。

2.2 操作

2.2.1 一般命令模式

  • 0 或者 HOME 键,光标移动到当前行的第一个字符前
  • $ 或者 END 键,光标移动到当前行的最后一个字符前
  • n<SPACE> 输入数字加空格,光标将右移到光标当前位置的第 n 个字符
  • n<>ENTER 输入数字加回车,光标将下移到光标当前行以下第 n 行第一个非空?字符前
  • G 键,光标移动到最后一行,区分大小写
  • nG 键,光标移动到第 n 行,区分大小写
  • gg 相当于 1G ,光标移动到第一行
  • n 键,用于命令行模式的查询,查询后按下 N 键重复上一次查询,区分大小写
  • N 键,用于命令行模式的查询,查询后按下 N 键反向重复上一次查询,区分大小写
  • v 键,选中文本,结合方向键及其他上述切换光标位置命令使用
  • d 键,删除选中的文本,删除时会自动复制删除的内容
  • dd 键,删除当前行,复制同上
  • y 键,复制选中的文本
  • yy 键,复制当前行
  • p 键,将复制的数据在光标的下一行或下一个位置粘贴
  • u 键,撤销
  • CTRL + R,取消撤销,无懈可击
  • > 键,将选中的文本整体向右缩进一次
  • < 键,向左缩进
  • gg=G,四个字符均需要输入,全文代码格式化
  • CTRL + Q, 取消正在执行的命令

2.2.2 命令行模式

  • :n,冒号+数字,同 nG ,将光标移动到第 n 行
  • /word ,向光标以下查找第一个 word
  • ?word,向光标以上查找第一个 word
  • :n1,n2s/word1/word2/g,在第 n1 行与 n2 行之间寻找 word1 并替换为 word2
  • :1,$s/word1/word2/g,将全文的 word1 替换为 word2
  • :1,$s/word1/word2/gc,将全文的 word1 替换为 word2 ,但替换前需要手动确认
  • :w 保存
  • w! 强制保存
  • :q 退出,常用
  • :q! 强制退出,常用
  • :wq 保存并退出,常用
  • :set paste 设置成粘贴模式,取消代码自动缩进
  • :set nopaste 取消粘贴模式,开启代码自动缩进
  • :set nu 显示行号
  • :set nonu 隐藏行号
  • noh 关闭查找关键词高亮

3 Shell 语法

3.1 概述

Linux 中常见的 Shell 脚本有很多种,包括:

  • Bourne Shell /usr/bin/sh 或者 /bin/sh
  • Boume Again Shell /bin/bash
  • zsh
  • ······

文件头部写明 #! /bin/bash 指定运行使用的解释器

执行方式:

  • 赋予执行权限:chmod +x test.sh
  • 相对路径运行 ./test.sh
  • 绝对路径运行 /root/test.sh
  • 家目录运行 ~/test.sh
  • 解释器运行 bash ./test.sh

3.2 变量

3.2.1 注释

注释分为单行注释(行尾注释),多行注释

单行注释

1
2
# 单行注释
echo "Hello World!" # 行尾也可以使用 # 进行注释

多行注释,其中 EOF 可以成对用其他字符串替换

1
2
3
4
5
:<<EOF # 冒号也可有可无
第一行注释
第二行注释
第三行注释
EOF

3.2.2 变量

定义变量

定义变量不需要向 PHP 脚本一样使用 $ 符,注意等号两边不要有空格

变量名限制为常规限制(不能数字开头,不能是保留字等等)

1
2
3
name='applepine'
_name2='aaa'
age=18

使用变量

使用变量要用 $ 符,但最好包裹 {} ,识别变量边界

1
2
3
4
name='applepine'
echo $name
echo ${name}
echo ${name}age

只读变量

使用 readonly 或者 declare 可以将变量设置只读属性

1
2
3
4
5
name='applepine'
readonly name
declare -r name # 两种写法效果一致
name='APPLEPINE' # 报错,name 只读
unset name # 报错,只读变量,因为只读所以不可写,所以不可删除

删除变量

使用 unset 删除变量

1
2
3
name='applepine'
unset name
echo ${name} # 输出空行,变量不存在时,其值为空

变量类型

  1. 自定义变量(局部变量):子进程无法访问

  2. 环境变量(全局变量):子进程可以访问

自定义变量改成环境变量:

1
2
3
name='applepine'
export name
declare -x name # 两种方法均可

环境变量改成自定义变量:

1
2
export name
declare +x name

字符串

字符串可以不用引号,可以用单引号也可以用双引号

单引号的内容不做解析,双引号会解析内部变量

1
2
3
name=applepine
echo 'hello ${name}' # 输出 hello ${name}
echo "hello ${name}" # 输出 hello applepine

获取字符串长度 #

1
2
name=applepine
echo ${#name} # 9

截取字符串

1
2
name=applepine
echo ${name:0:5} # 截取下标为 0 开始的 5 个字符 apple

文件参数变量

执行 shell 脚本时可以向脚本传递参数供脚本使用,$1 是第一个参数,$2 是第二个参数,依次类推。

$0 是执行脚本的文件路径,例如 ./test.sh /root/test.sh

1
2
3
4
5
6
7
8
9
10
#! /bin/bash
echo "文件名:"$0
echo "第一个参数:"$1
echo "第二个参数:"$2
echo "参数个数:"$#
echo '$*:'$*
echo '$@:'$@
echo '$$:'$$
echo '$?:'$?
$(cp ./test.sh ./test.sh.bak)

相关参数

  • $# 参数个数,不算文件路径,只算传参个数
  • $* 用空格将各个参数分隔的字符串
  • $@ 用双引号括起来的各个参数字符串
  • $$ 脚本当前运行的进程 ID
  • $? 上一条命令的退出状态,0 为正常退出
  • $(command) 返回 command 这条命令的 stdout ,可以嵌套
  • `command` 返回 command 这条命令的 stdout ,不可嵌套

3.3 数组

3.3.1 数组基础

只支持一维数组,初始化不需要指明大小

定义数组

1
2
3
4
5
array=(1 "def" applepine false) # 用小括号  空格分开
array[0] = 1
array[1] = pine
array[2] = "apple"
array[3] = false

读取数组

1
2
3
4
5
6
# 读取整个数组 三种均可以
echo ${array}
echo ${array[@]}
echo ${array[*]}
# 读取部分值
echo ${array[1]}

数组长度

1
echo ${#array[@]}

3.4 基础命令

3.4.1 expr

expr 用于求表达式的值,expr 表达式

字符串表达式

  • length STRING 返回 STRING 长度
  • index STRING CHARSET 返回字符串 CHARSET 中任意一个字符在 STRING 中首次出现的位置,下标从 1 开始,不存在返回 0
  • substr STRING POSITION LENGTH 返回 STRING 字符串中从 POSITION 开始,长度最大为 LENGTH 的子串,如果 POSITION LENGTH 不是正整数或者不是数值将返回空字符串
1
2
3
4
str="Hello World"
echo `expr length "${str}"`
echo `expr index "${str} eWo"` # 返回 2,因为 e 最先出现
echo `expr substr "${str}" 7 5` # 返回 World

整数表达式

expr 支持基础算术表达式。过时了,现代 Shell 用 $(())

  • + -
  • * / % 乘法 * 需要使用转义,或者单引号
  • ()
  • **
1
2
3
4
5
6
7
8
9
a=3
b=4
c=5
echo `expr ${a} + ${b}`
echo `expr ${b} - ${a}`
echo `expr ${a} \* ${b}` # 或者 echo `expr ${a} '*' ${b}`
echo `expr ${c} / ${a}`
echo `expr '(' ${a} + 1 ')' \* '(' ${b} + 1 ')'`
echo `expr ${a} % ${b}`

逻辑关系表达式

  • |

    如果第一个参数非空且非 0,则返回第二个参数,否则返回第二个参数,但需要非空且非 0,否则返回 0,如果第一个参数非空且非 0,则不会计算第二个表达式

  • &

    如果两个参数都非空且非 0,则返回第一个参数,否则返回 0,如果第一个参数为 0 或 空,则不会计算第二个表达式

  • < <= == != >= >

    比较两端的参数,如果为 true 则返回 1,否则返回 0。expr 首先尝试将两端参数转化为整数,并做算术比较,如果转化失败,按照字符集排列顺序比较

  • ()

    可以改变优先级,需要反斜杠转义,或者使用单引号

1
2
3
4
5
6
7
8
9
10
a=3
b=4
echo `expr ${a} \> ${b}` # 0
echo `expr ${a} '<=' ${b}` # 1
c=0
d=5
echo `expr ${c} \& ${d}` # 0
echo `expr ${c} '|' ${d}` # 5
echo `expr ${a} \& ${d}` # 3
echo `expr ${a} '|' ${d}` # 3

3.4.2 read

read 命令从标准输入中读取单行数据,当读到文件结束符时,exit code 为 1,否则为 0.

参数:

  • -p 后面接提示信息,部分 shell 不支持,比如 zsh
  • -t 后面接秒数,等待时间
1
2
3
4
root@applepine-work:~# read -p "输入用户名" -t 30 name
输入用户名applepine
root@applepine-work:~# echo ${name}
applepine

3.4.3 echo

echo 用于输出字符串,echo STRING

显示普通字符串

1
echo "Hello World"

显示转义字符

1
echo "\"Hello World\"" # "Hello World"

显示变量

1
2
name=zhangsan
echo "My name is ${name}" # My name is zhangsan

显示换行

1
echo "Hi\nMike"

显示不换行 , 取消 echo 默认换行

1
2
echo -e "Hello \c"  # 最好开启转义,否则不同 shell 效果不一
echo "World" # Hello World

显示原样字符串,不解释变量和转义,单引号

1
2
a=World
echo '\"Hello ${a}\"' # \"Hello ${a}\"

显示命令执行的结果

1
echo `date`		# Fri Oct 25 02:13:10 PM CST 2024

3.4.4 printf

默认不会在字符串末尾加换行符 printf format-string [arguments]

1
2
3
4
printf "%10d.\n" 123				# 10 位 右对齐
printf "%-10.2f.\n" 123.123321 # 10 位 左对齐,两位小数
printf "My name is %s\n" "applepine"
printf "%d * %d = %d\n" 2 3 `expr 2 '*' 3`
1
2
3
4
       123.
123.12 .
My name is applepine
2 * 3 = 6

3.4.5 test 命令与判断符号[]

逻辑运算符 && 和 ||

  • && 表示与,|| 表示或

  • 二者具有短路原则:

    expr1 && expr2expr1 为假时直接忽略 expr2

    expr1 || expr2expr1 为真时直接忽略 expr2

  • 表达式的 exit code 为 0, 表示真,为非 0, 表示假

test 命令

1
2
3
4
5
6
➜  ~ ls
code lcmp proxy.sh test.sh
➜ ~ test -e test.sh && echo "exist" || echo "Not exist"
exist
➜ ~ test -e test2.sh && echo "exist" || echo "Not exist"
Not exist
  1. 文件类型判断

    test -e filename 判断文件是否存在

    • -e 文件是否存在
    • -f 是否是文件
    • -d 是否是目录
  2. 文件权限判断

    test -r filename 判断是否可读

    • -r 是否可读
    • -w 是否可写
    • -x 是否可执行
    • -s 是否为非空
  3. 整数间比较

    test ${a} -eq ${b} 判断 a 是否等于 b

    • -eq a == b
    • -ne a != b
    • -gt a > b
    • -lt a < b
    • -ge a >= b
    • -le a<= b
  4. 字符串比较

    • test -z STRING 判断是否为空,为空返回 true
    • test -n STRING 是否非空
    • test str1 == str2 是否相同
    • test str1 != str2 是否不同
  5. 多重条件判定

    test -r filename -a -x filename

    • -a 两条件是否同时成立
    • -o 是否至少一个成立
    • ! 取反,如 test ! -x test.sh 不可执行时返回 true

判断符号

[]test 用法几乎一致,更常用于 if 语句中,另外 [[]][] 的加强版,支持特性更多

1
2
[2 -lt 3]	# 为真,返回 0
echo $? # 输出上一个命令返回值,0
1
2
3
4
➜  ~ [ -e proxy.sh ] && echo "exist" || echo "Not exist"
exist
➜ ~ [ -e proxy2.sh ] && echo "exist" || echo "Not exist"
Not exist

注意事项:

  • [] 中的每一项都要空格隔开
  • [] 中的变量要用双引号括起来
  • [] 中的常数用单引号或者双引号括起来
1
2
3
4
5
6
7
8
9
root@applepine-work:~# str="apple pine"
root@applepine-work:~# [ ${str} == "apple pine" ]
bash: [: too many arguments
root@applepine-work:~# [ "${str}" == "apple pine" ] && echo "Yes" || echo "No"
Yes


# 因为 ${str} == "···" 相当于 apple pine == "···"
# 而加上括号后相当于 "apple pine" == "apple pine"

3.4.6 exit 命令

exit 命令用来退出当前 shell 进程,并返回一个退出状态,使用 $? 可以接收这个退出状态。

exit 命令可以接收一个整数值作为参数,代表退出状态,如果不指定,默认状态值为 0.

exit 退出状态只能是一个介于 0~255 之间的整数,其中只有 0 表示成功,其他值都表示失败。

1
2
3
4
5
6
7
8
#! /bin/bash
if [ $# -ne 1 ] # 如果参数为一个,正常退出,否者非正常退出
then
echo "arguments not valid"
exit 1
else
echo "arguments valid"
fi

3.4.7 文件重定向

每一个进程默认打开了三个文件描述符:

  • stdin 标准输入,从命令行读入数据,文件描述符为 0
  • stdout 标准输出,向命令行输出数据,文件描述符为 1
  • stderr 标准错误输出,向命令行输出错误,文件描述符为 2

可以用文件重定向将这三个内容重定向到其他文件中

重定向命令

  • command > filestdout 重定向到 file ,即写入 file
  • command < filestdin 重定向到 file,即从 file 中读入
  • command >> filestdout 以追加方式重定向到 file 即向 file 尾部写入数据
  • command n> file 将文件描述符 n 重定向到 file
  • command n>> file 将文件描述符以追加方式重定向到 file
1
2
3
4
echo -e "Hello World\nApplepine" > output.txt
cat output.txt
Hello World
Applepine

同时重定向输入和输出

  1. test.js 文件

    1
    2
    3
    4
    5
    6
    const str = 'aaa:bbb:ccc:ddd:eee';
    const arr = str.split(':');
    const l = 3;
    const r = 5;
    const res1 = arr[l - 1] + ':' + arr[r - 1];
    console.log(res1); // ccc:ddd
  2. 执行命令

    1
    2
    3
    4
    # 1
    echo $(<test.js) > test1.js
    # 2
    echo "$(<test.js)" > test2.js
  3. 结果

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // test1.js
    const str = "aaa:bbb:ccc:ddd:eee" const arr = str.split(':') const l = 3 const r = 5 const res1 = arr[l - 1] + ":" + arr[r - 1] console.log(res1) // ccc:ddd

    // test2.js
    const str = "aaa:bbb:ccc:ddd:eee"
    const arr = str.split(':')
    const l = 3
    const r = 5
    const res1 = arr[l - 1] + ":" + arr[r - 1]
    console.log(res1) // ccc:ddd

逻辑: 总体逻辑是 $() 通过 < 读入 stdinecho 开启 stdout 通过 > 将内容重定向到 test1.js | test2.js ,但是两者有些许差异。前者写入到 test1.js 的内容没有换行符,后者原封不动的复制了 test.js 的内容到 test2.js ,这是 echo 的原因,如果没有用引号包裹变量或者命令替换结果,如 echo $(<test.js)echo 会将内容视为单个参数,将所有换行符当做空格处理,而 "" 的使用会告诉 echo 保留文本的原始格式,包括换行符。

3.4.8 include

bash 中可以引入外部文件

1
2
3
4
5
. filename # 点和文件之间有空格

# 或者

source filename

示例

  1. 创建 test1.sh

    1
    2
    3
    #! /bin/bash

    name="applepine"
  2. 创建 test2.sh

    1
    2
    3
    4
    5
    #! /bin/bash

    source test1.sh

    echo "My name is: " ${name}

3.5 基础语句

3.5.1 判断语句

单层 if 形式

1
2
3
4
5
6
if condition
then
语句1
语句2
···
fi
1
2
3
4
5
6
a=3
b=4
if [ "${a}" -lt "${b}" ] && [ "${a}" -gt 2 ]
then
echo ${a}在2~4之间
fi

单层 if-else 形式

1
2
3
4
5
6
7
8
9
10
if condition
then
语句1
语句2
···
else
语句1
语句2
···
fi
1
2
3
4
5
6
7
8
a=7
b=4
if [ "${a}" -lt "${b}" ] && [ "${a}" -gt 2 ]
then
echo ${a}在2~4之间
else
echo ${a}不在2~4之间
fi

多层 if-elif-elif-else 形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if condition
then
语句1
···
elif condition
then
语句1
···
elif condition
then
语句1
···
else
语句1
···
fi
1
2
3
4
5
6
7
8
9
10
a=7
if [ ${a} == 0 ]
then
echo '${a}=0'
elif [ ${a} -lt 0 ]
then
echo '${a}<0'
else
echo '${a}>0'
fi

case···esac 形式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
case ${变量名} in
值1)
语句1
···
;; # 类似 break
值2)
语句1
···
;;
*) # 类似 default
语句1
···
;;
esac

3.5.2 循环语句

for…in…do…done 形式

1
2
3
4
5
for var in val1 val2 val3
do
语句1
···
done

示例 1,输出 a 2 “applepine”,不换行

1
2
3
4
for i in a 2 "applepine"
do
echo -e "${i} \c"
done

示例 2,输出当前路径所有文件名

1
2
3
4
for file in `ls`
do
echo ${file}
done

示例 3,输出 1-10

1
2
3
4
for i in $(seq 1 10)
do
echo ${i}
done

示例 4,使用{1..10} 或者 {a..z}

1
2
3
4
for i in {a..z}		#是两个点
do
echo ${i}
done

for((…;…;…)) do …done 形式

1
2
3
4
5
6
for ((expression; condition; expression))
do
语句1
语句2
···
done

示例,输出 1-10

1
2
3
4
for ((i=1; i<= 10; i++))
do
echo ${i}
done

while…do…done 形式

1
2
3
4
5
6
while condition
do
语句1
语句2
···
done

示例,文件结束符为 CTRL + D,输入结束符后 read 指令返回 false

1
2
3
4
while read name
do
echo ${name}
done

until…do…done 形式

1
2
3
4
5
6
until condition
do
语句1
语句2
···
done
1
2
3
4
until [ "${word}" == "yes" ] || [ "${word}" == "YES" ]
do
read -p "Please input yes/YES to stop this program: " word
done

break 命令

跳出==当前一层循环==

1
2
3
4
5
6
7
8
9
10
11
12
13
14
while read name
do
for ((i=1; i<=10; i++))
do
case ${i} in
8)
break;
;;
*)
echo ${i}
;;
esac
done
done

continue 命令

跳出==当前一次循环==

1
2
3
4
5
6
7
8
for ((i=1; i<=10; i++))
do
if [ `expr ${i} % 2` -eq 0 ]
then
continue
fi
echo ${i}
done

3.5.3 函数

bash 中的函数类似于 C/C++ 中的函数,但 return 的返回值与 C/C++ 不同,返回的是 exit code,取值为 0-255,0 表示正常结束。

如果想获取函数的输出结果,可以通过 echo 输出到 stdout 中,然后通过 $(function_name) 来获取 stdout 中的结果。

函数的 return 值可以通过 $? 来获取。

1
2
3
4
5
[function] func_name() {	# function 关键字可以省略
语句1
语句2
···
}

不获取 return 值和 stdout

1
2
3
4
5
func() {
name=applepine
echo "Hello ${name}"
}
func

获取 return 值和 stdout

1
2
3
4
5
6
7
8
9
func() {
name=applepine
echo "Hello ${name}"
return 123
}
output=$(func)
ret=$?
echo "output = ${output}"
echo "return = ${ret}"

函数的输入参数

在函数内, $1 表示第一个输入参数, $2 表示第二个输入参数,依次类推。

注意:函数的 $0 仍然为文件路径,而不是函数名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func() {
word=""
while [ "${word}" != 'y' ] && [ "${word}" != 'n' ]
do
read -p "要进入func($1)函数吗?请输入y/n:" word
done

if [ "${word}" == 'n' ]
then
echo 0
return 0
fi
if [ $1 -le 0 ]
then
echo 0
return 0
fi
sum=$(func $(expr $1 - 1))
echo $(expr ${sum} + $1)
}
echo $(func 10)

函数内的局部变量

可以在函数内定义局部变量,作用范围仅在该函数内部。

可以在递归函数中定义局部变量。

local 变量名=变量值

1
2
3
4
5
6
7
#! /bin/bash
func() {
local name=applepine
echo 内部${name}
}
func # 输出 内部applepine
echo 外部${name} # 输出 外部

4. SSH

4.1 远程登录

4.1.1 登录服务器

1
ssh user@hostname -p port

第一次登录会提示:

1
2
3
The authenticity of host '123.57.47.211 (123.57.47.211)' can't be established.
ECDSA key fingerprint is SHA256:iy237yysfCe013/l+kpDGfEG9xxHxm0dnxnAbJTPpG8.
Are you sure you want to continue connecting (yes/no/[fingerprint])?

输入 yes 回车后,服务器信息会保存在 ~/.ssh/know_hosts 文件中,然后输入密码即可登录

4.1.2 配置文件

创建文件 ~/.ssh/config 写入:

1
2
3
4
5
6
Host myserver1
HostName IP地址或者域名
User 用户名
Host myserver2
HostName IP地址或者域名
User 用户名

之后再使用服务器,可以直接通过 myserver1 连接服务器

4.1.3 配置密钥登录

  1. 创建密钥

    1
    ssh-keygen -t rsa -f "~/.ssh/id_rsa" -C "备注信息,可以是邮箱"
    • -t 密钥类型,可选 dsaecdsaed25519rsa
    • -f 密钥文件位置,目录默认是 ~/.ssh/ 文件默认是 id_rsa
    • -C 备注信息
  2. 免密登录

    将==公钥==上传到服务器

    1
    ssh-copy-id -i ~/.ssh/id_rsa.pub -p 22 root@127.0.0.1
  3. 执行命令

    1
    2
    3
    # ssh user@hostname command

    ssh myserver 'for ((i=0; i<10; i++)) do echo ${i}; done'

4.2 scp 命令

scp 用于传送文件,将 文件 source 下的文件复制到 destination

1
scp source destination

复制文件夹

1
2
scp -r ~/tmp myserver:/home/acs  # 将 tmp 目录传到 /home/acs 下即 /home/acs/tmp
scp -r myserver:

批量复制文件夹下的文件(夹):

1
scp -r ~/tmp/* myserver:/home/acs

5. Git

Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。

5.1 基本概念

  • 工作区:仓库的目录,工作区是独立于各个分支的。
  • 暂存区:数据暂时存放的区域,类似于工作区写入版本库前的缓存区,暂存区是独立于各个分支的。
  • 版本库,存放所有已经提交到本地仓库的代码版本。
  • 版本结构:树结构,树中没一个节点代表一个代码版本。

5.1 基础命令

  1. git config --global user.name xxx :设置全局用户名,信息记录在 ~/.gitconfig 文件中

  2. git config --global user.email xxx@xxx.com :设置全局邮件地址

  3. git init :将当前目录配置成 git 仓库,信息记录在隐藏的 .git 文件夹中

  4. git add xx :将 xx 添加到暂存区

    git add . : 将所有待加入暂存区的文件加入暂存区

  5. git rm --cached xx : 将文件从仓库索引目录中删除

  6. git commit -m "备注信息" : 将暂存区的内容提交到当前分支

  7. git status : 查看仓库状态

  8. git diff xx : 查看 xx 文件相对于暂存区修改了哪些内容

  9. git log : 查看当前分支所有版本

  10. git relog : 查看 HEAD 指针的移动历史(包括被回滚的版本)

  11. git reset --hard HEAD^ 或者 git reset --hard HEAD~ : 将代码库回滚到上个版本

    • git reset --hard HEAD^^ : 回滚两次,以此类推
    • git reset --hard HEAD~100 : 回滚一百次
    • git reset --hard 版本号 : 回滚到特定版本
  12. git checkout - xx 或者 git restore xx : 将 xx 文件尚未加入暂存区的修改全部撤销

  13. git remote add origin git@xxx:xxx/xxx.git : 将本地仓库关联到远程仓库

  14. git push -u (第一次需要 -u 以后不需要) : 将当前分支推送到远程仓库

    • git push origin branch_name : 将本地某个分支推送到远程仓库
  15. git clone git@xxx:xxx/xxx.git : 将远程仓库下载到当前目录下

  16. git checkout -b branch_name : 创建并切换到 branch_name 分支下

  17. git branch : 查看所有分支和当前所处分支

  18. git checkout branch_name : 切换到 branch_name 这个分支

  19. git merge branch_name : 将分支 branch_name 合并到当前分支上

  20. git branch -d branch_name : 删除本地仓库 branch_name 分支

  21. git branch branch_name : 创建新分支

  22. git push --set-upstream origin branch_name : 设置本地 branch_name 分支对应的远程仓库的 branch_name 分支

  23. git push -d origin branch_name : 删除远程仓库的 branch_name 分支

  24. git pull : 将远程仓库的当前分支与本地仓库的当前分支合并

    git pull origin branch_name : 将远程仓库的 branch_name 分支与本地仓库的当前分支合并

  25. git branch --set-upstream-to=origin/branch_name1 branch_name2 : 将远程的 branch_name1 分支与本地的 branch_name2 分支对应

  26. git checkout -t origin/branch_name : 将远程 branch_name 分支拉取到本地

  27. git stash : 将工作区和暂存区中尚未提交的修改存入栈中

  28. git stash apply : 将栈顶存储的修改恢复到当前分支,但不删除栈顶元素

  29. git stash drop : 删除栈顶存储的修改

  30. git stash pop : 将栈顶存储的修改恢复到当前分支,同时删除栈顶元素

  31. git stash list : 查看栈中的所有元素

5.2 常用命令

  • git init 初始化
  • git config --global user.name xxx git config --global user.email xxx 全局用户信息
  • git remote add origin xxx@xxx:xxx/xxx.git 添加远程仓库
  • git add . git add $file 加入暂存区
  • git commit -m "备注" 加入本地分支
  • git status 仓库状态
  • git log 查看当前仓库所有版本
  • git push -u origin master 推送到远程 master 分支
  • git push -d origin master 删除远程 master 分支
  • git clone xxx@xxx:xxx/XX.git 克隆远程仓库到本地
  • git branch 查看所有分支和当前分支

5.3 查看命令

  • git diff XX 查看 XX 文件相对与暂存区修改了哪些内容
  • git status 查看仓库状态
  • git log 查看当前分支所有版本
  • git log --pretty=oneline 用一行来显示
  • git reflog 查看 HEAD 指针的移动历史
  • git branch 查看所有分支和当前分支

5.4 删除命令

  • git rm --cached XX 将文件从仓库索引目录中删掉,不希望管理这个文件
  • git restore --staged XX 将 XX 从暂存区删除
  • git checkout - XX 或者 git restore XX 将 XX 尚未加入暂存区的修改全部撤销

5.5 代码回滚

  • git reset --hard HEAD^ 或者 git reset --hard HEAD~ 将代码回滚到上一个版本
  • git reset --hard HEAD^^ 回滚两次,依次类推
  • git reset --hard HEAD~100 回滚一百次
  • git reset --hard 版本号 回滚到指定版本

5.6 远程仓库

  • git remote add origin xxx@xxx:xxx/XX.git 将本地仓库关联到远程仓库
  • git push -u origin master 将 master 分支推送
  • git push -u 推送当前分支
  • git clone xxx@xxx:xxx/XX.git 克隆远程仓库到本地
  • git push --set-upstream origin branch_name 设置本地的 branch_name 对应到远程仓库的 branch_name
  • git push -d origin master 删除远程仓库的 master 分支
  • git checkout -t origin/master 将远程仓库 master 拉取到本地
  • git pull 将远程仓库当前分支与本地仓库当前分支合并
  • git pull origin master 将远程仓库 master 分支与本地仓库当前分支合并
  • git branch --set-upstream-to=origin/branch_name1 branch_name2 将远程的 branch_name1 与本地 branch_name2 对应

5.7 分支相关

  • git branch 查看所有分支和当前分支

  • git branch dev 创建新分支 dev

  • git checkout -b dev 创建并切换到分支 dev

  • git checkout dev 切换到分支 dev

  • git merge branch_name 将分支 branch_name 与当前所在分支(git branch)合并

  • git branch -d branch_name 删除本地分支

  • git branch --set-upstream-to=origin/branch_name1 branch_name2 将远程的 branch_name1 与本地 branch_name2 对应

  • git push -d origin master 删除远程 master 分支

  • git push --set-upstream origin branch_name 设置本地 branch_name 分支对应远程同名分支

  • git pull 拉取远程仓库当前分支合并

  • git pull origin branch_name 将远程 branch_name 分支与本地当前分支合并

5.8 stash 暂存

  • git stash 将工作区和暂存区尚未提交的修改存入栈中
  • git stash apply 将栈顶存储的修改恢复到当前分支,不删除栈顶
  • git stash pop 将栈顶存储的修改恢复到当前分支,删除栈顶
  • git stash drop 删除栈顶
  • git stash list 查看栈中所有元素

6. 环境变量

6.1 概念

Linux 系统中有很多环境变量来记录配置信息,环境变量类似于全局变量,可以被各个进程访问,可通过修改环境变量来修改系统配置。

6.1 查看环境变量

  1. env 查看当前用户的变量
  2. set 显示当前 shell 的变量,包括当前用户的变量
  3. export 显示当前导出成用户变量的 shell 变量
  4. echo $PATH 输出某个环境变量的值

6.2 修改环境变量

7. 五花八门的软件

7.1 第一个当然非 Docker 莫属啦

7.1.1 添加用户组

为了避免每次使用 docker 命令都需要加上 sudo 权限,可以将当前用户加入安装中自动创建的 docker 用户组

1
sudo usermod -aG docker $USER

7.1.2 镜像 (image)

  1. docker pull ubuntu:22.04 拉取镜像
  2. docker images 列出本地所有镜像
  3. docker image rm ubuntu:22.04 或者 docker rmi ubuntu:22.04 删除镜像
  4. docker [container] commit CONTAINER IMAGE_NAME:TAG 创建某个 container 对象
  5. docker save -o ubuntu_22_04.tar ubuntu:22.04 将镜像导出到本地压缩文件中
  6. docker load -i ubuntu_22_04.tar 将镜像 ubuntu:22.04 从压缩文件 ubuntu_22_04.tar 中加载出来

7.1.3 容器(container)

  1. docker [container] create -it ubuntu:22.04 利用镜像创建容器

  2. docker ps -a 查看本地所有容器

  3. docker [container] start CONTAINER 启动容器

  4. docker [container] stop CONTAINER 停止容器

  5. docker [container] reatart CONTAINER 重启容器

  6. docker [container] run -itd ubuntu:22.04 创建并启动容器

  7. docker [container] attach CONTAINER 进入容器

    先按CTRL + p 再按 CTRL + q 可以挂起容器

  8. docker [container] exec CONTAINER COMMAND 在容器中执行命令

  9. docker [container] rm CONTAINER 删除容器

  10. docker container prune 删除所有停止的容器

  11. docker export -o xxx.tar CONTAINER 将容器导出到本地压缩文件中

  12. docker import xxx.tar image_name:tag 将本地压缩文件导入成镜像并将镜像命名为 image_name:tag

  13. docker export/importdocker save/load 的区别:

    • export/import 会丢弃历史记录和元数据信息,仅保存容器当时快照
    • save/load 会保存完整记录,体积较大
  14. docker top CONTAINER 查看某个容器所有进程

  15. docker stats 查看所有容器统计信息,包括 CPU、内存、储存、网络等

  16. docker cp xxx CONTAINER:xxx 或者 docker cp CONTAINER:xxx xxx 在本地和容器间复制文件

  17. docker rename CONTAINER1 CONTAINER2 重命名容器

  18. docker update CONTAINER --memory 500MB 修改容器限制

实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 0
# 配置服务器别名,免密登录
Host myserver
HostName 127.0.0.1
User root
port 22
ssh-copy-id -i ~/.ssh/id_rsa.pub myserver
# 写入别名至 server_name.txt
echo "myserver" > server_name.txt

# 1
# 安装 docker
sudo apt update && sudo apt install ca-certificates curl
sudo install -m 755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker-archive-keyring.gpg
sudo chmod a+r /etc/apt/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update

sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo docker run hello-world
# 01 创建 docker 用户组
sudo groupadd docker
# 02 添加自己到 docker 用户组
sudo usermod -aG docker $USER

# 2
# 上传本地的镜像压缩文件到服务器
scp ./image.tar myserver:
# 提取镜像
docker load -i image.tar
# 创建镜像容器,设置端口映射
docker run -p 20000:22 --name my_docker_server -itd docker_lesson:1.0
# 进入容器
docker attach my_docker_server
# 创建账户
adduser acs
# 配置容器别名和免密登录
Host my_docker_server
HostName 127.0.0.1
User acs
port 20000
ssh-copy-id -i ~/.ssh/id_rsa.pub my_docker_server
# 写入别名至 server_name.txt
echo "my_docker_server" > server_name.txt