常用终端命令

快捷键

  1. 取消命令,并且换行:Ctrl + c
  2. 清空本行命令:Ctrl + u
  3. Tab 可以补全命令和文件名,如果补全不了快速按两下 Tab 键,可以显示备选选项
  4. 复制文本Ctrl + Ins
  5. 粘贴文本Shift + Ins
  6. 退出服务:Ctrl + d

文件处理

  1. 列出当前目录下所有文件,蓝色的是文件夹,白色的是普通文件,绿色的是可执行文件
    1
    ls
    常用参数

    -l:列出长数据串,包含文件的属性与权限数据等
    -a:列出全部的文件,连同隐藏文件(开头为.的文件)一起列出来(常用)
    -d:仅列出目录本身,而不是列出目录的文件数据

  2. 显示当前路径
    1
    pwd
  3. 进入XXX目录下
    1
    2
    3
    4
    5
    cd XXX
    #返回上层目录
    cd ..
    #返回上一次目录
    cd -
  4. 将XXX文件复制成YYY,XXX和YYY可以是一个路径,比如…/dir_c/a.txt,表示上层目录下的dir_c文件夹下的文件a.txt
    1
    cp XXX YYY
    常用参数

    -i:覆盖前询问(使前面的 -n 选项失效)
    -n:不要覆盖已存在的文件(使前面的 -i 选项失效)

  5. 创建目录XXX
    1
    mkdir XXX
    常用参数

    -p:此时若路径中的某些目录尚不存在,加上此选项后,系统将自动建立好那些尚不存在的目录,即一次可以建立多个目录
    -v:每次创建新目录都显示信息

  6. 删除文件,支持简单的正则表达式*通配符
    1
    2
    3
    4
    #删除普通文件
    rm XXX
    #删除文件夹
    rm XXX -r
    常用参数

    -f:忽略不存在的文件,从不给出提示
    -r:指示rm将参数中列出的全部目录和子目录均递归地删除
    -v:详细显示进行的步骤

  7. 将XXX文件移动到YYY,和cp命令一样,XXX和YYY可以是一个路径;重命名也是用这个命令
    1
    mv XXX YYY
    常用参数

    -b:若需覆盖文件,则覆盖前先行备份
    -f:force 强制的意思,如果目标文件已经存在,不会询问而直接覆盖
    -i:若目标文件 (destination) 已经存在时,就会询问是否覆盖

  8. 创建一个文件
    1
    touch XXX
  9. 展示文件XXX中的内容
    1
    cat XXX
  10. 查看"当前工作目录"的完整路径。
    1
    pwd

文件检索

  1. which 可执行文件名称:搜索某个系统命令的位置,并且返回第一个搜索结果。
  2. find /path/to/directory/ -name '*.py':搜索某个文件路径下的所有*.py文件
  3. grep xxx:从stdin中读入若干行数据,如果某行中包含xxx,则输出该行;否则忽略该行。
  4. wc:统计行数、单词数、字节数
    常用参数

    -l:统计行数
    -w:统计单词数
    -c:统计字节数

  5. tree:展示当前目录的文件结构
    1
    2
    tree /path/to/directory/ # 展示某个目录的文件结构
    tree -a # 展示隐藏文件
  6. ag xxx:搜索当前目录下的所有文件,检索xxx字符串
  7. cut:分割一行内容
    1
    2
    3
    4
    echo $PATH | cut -d ':' -f 3,5 # 输出PATH用:分割后第3、5列数据
    echo $PATH | cut -d ':' -f 3-5 # 输出PATH用:分割后第3-5列数据
    echo $PATH | cut -c 3,5 # 输出PATH的第3、5个字符
    echo $PATH | cut -c 3-5 # 输出PATH的第3-5个字符
  8. sort:将每行内容按字典序从小到大排序
  9. xargs:将stdin中的数据用空格或回车分割成命令行参数
    1
    2
    # 管道
    find . -name '*.py' | xargs cat | wc -l # 统计当前目录下所有python文件的总行数

文件权限设置

  1. 改变linux系统文件或目录的访问权限
    1
    2
    3
    4
    #增加文件所有用户组可执行权限
    chmod a+x test.log
    #删除所有用户的可执行权限
    chmod a-x test.log
    常用参数

    <权限范围>+<权限设置> 使权限范围内的目录或者文件具有指定的权限
    <权限范围>-<权限设置> 删除权限范围的目录或者文件的指定权限
    <权限范围>=<权限设置> 设置权限范围内的目录或者文件的权限为指定的值

    1. 权限范围:
      • u :目录或者文件的当前的用户
      • g :目录或者文件的当前的群组
      • o :除了目录或者文件的当前用户或群组之外的用户或者群组
      • a :所有的用户及群组
    2. 权限代号:
      • r:读权限,用数字4表示
      • w:写权限,用数字2表示
      • x:执行权限,用数字1表示
      • -:删除权限,用数字0表示

系统状况

  1. top:查看所有进程的信息(Linux的任务管理器)
    • 打开后,输入M:按使用内存排序
    • 打开后,输入P:按使用CPU排序
    • 打开后,输入q:退出
  2. df -h:查看硬盘使用情况
  3. free -h:查看内存使用情况
  4. du -sh:查看当前目录占用的硬盘空间
  5. ps aux:查看所有进程
  6. kill -9 pid:杀死编号为pid的进程
    • 传递某个具体的信号:kill -s SIGTERM pid
  7. netstat -nt:查看所有网络连接
  8. w:列出当前登陆的用户
  9. ping www.baidu.com:检查是否连网

查看文件内容

  1. more:浏览文件内容
    • Enter:下一行
    • Space:下一页
    • b:上一页
    • q:退出
  2. less:与more类似,功能更全
    • Enter:下一行
    • y:上一行
    • Page Down:下一页
    • Page Up:上一页
    • q:退出
  3. head -3 xxx:展示xxx的前3行内容
  4. tail -3 xxx:展示xxx末尾3行内容

apt命令

  1. sudo apt update:列出所有可更新的软件清单命令
  2. sudo apt upgrade:升级软件包
    • apt list --upgradeable:列出可更新的软件包及版本信息
    • sudo apt full-upgrade:升级软件包,升级前先删除需要更新软件包
  3. sudo apt install <package_name>:安装指定的软件命令
    • sudo apt install <package_1> <package_2> <package_3>:安装多个软件包
  4. sudo apt update <package_name>:更新指定的软件命令
  5. sudo apt show <package_name>:显示软件包具体信息,例如:版本号,安装大小,依赖关系等等
  6. sudo apt remove <package_name>:删除软件包命令
  7. sudo apt autoremove:清理不再使用的依赖和库文件
  8. sudo apt purge <package_name>:移除软件包及配置文件
  9. sudo apt search <keyword>:查找软件包命令
  10. apt list --installed:列出所有已安装的包
  11. apt list --all-versions:列出所有已安装的包的版本信息

其他操作

  1. 压缩和打包文件。
    1
    2
    3
    4
    tar [必要参数] [选择参数] [文件]
    tar -cvf test.tar test.log # 仅打包,不压缩!
    tar -zcvf test.tar.gz test.log # 打包后,以 gzip 压缩
    tar -zcvf test.tar.bz2 test.log # 打包后,以 bzip2 压缩
  2. 解压文件
    1
    tar -zxvf test.tar.gz
  3. md5sum:计算md5哈希值
  4. time command:统计command命令的执行时间
  5. ipython3:交互式python3环境。可以当做计算器,或者批量管理文件。
  6. watch -n 0.1 command:每0.1秒执行一次command命令
  7. adduser jezer:创建用户jezer
  8. usermod -aG sudo jezer:给用户jezer分配sudo权限
  9. passwd:修改密码

tumx

  1. tmux:新建一个session,其中包含一个window,window中包含一个pane,pane里打开了一个shell对话框。
  2. 按下Ctrl + B后手指松开:进入tmux命令模式
  3. 按下Ctrl + B后手指松开,然后按%:将当前pane左右平分成两个pane。
  4. 按下Ctrl + B后手指松开,然后按"(注意是双引号"):将当前pane上下平分成两个pane。
  5. Ctrl + D:关闭当前pane;如果当前window的所有pane均已关闭,则自动关闭window;如果当前session的所有window均已关闭,则自动关闭session。
  6. 按下Ctrl + B后手指松开,然后按方向键:选择相邻的pane。
  7. 按住Ctrl + B同时方向键,可以调整pane之间分割线的位置。
  8. 按下Ctrl + B后手指松开,然后按z:将当前pane全屏/取消全屏。
  9. 按下Ctrl + B后手指松开,然后按d:挂起当前session。
  10. tmux a:打开之前挂起的session。
  11. 按下Ctrl + B后手指松开,然后按s:选择其它session。
    :选择上一项 session/window/pane
    :选择下一项 session/window/pane
    :展开当前项 session/window
    :闭合当前项 session/window
    ESC:退出选择
  12. 按下Ctrl + B后手指松开,然后按c:在当前session中创建一个新的window。
  13. 按下Ctrl + B后手指松开,然后按w:选择其他window。
  14. 按下Ctrl + B后手指松开,然后按PageUp:翻阅当前pane内的内容。
  15. 在tmux中选中文本时,需要按住shift键。(仅支持Windows和Linux,不支持Mac,不过该操作并不是必须的,因此影响不大)
  16. tmux中复制/粘贴文本的通用方式:
    (1) 按下Ctrl + B后松开手指,然后按[
    (2) 用鼠标选中文本,被选中的文本会被自动复制到tmux的剪贴板
    (3) 按下Ctrl + B后松开手指,然后按],会将剪贴板中的内容粘贴到光标处

vim

模式

  1. 一般命令模式(默认模式):类似于打游戏放技能,按不同字符,即可进行不同操作。可以复制、粘贴、删除文本等。
  2. 编辑模式: 在一般命令模式里按下i,会进入编辑模式。按下ESC会退出编辑模式,返回到一般命令模式。
  3. 命令行模式:在一般命令模式里按下:/?三个字母中的任意一个,会进入命令行模式。命令行在最下面。可以查找、替换、保存、退出、配置编辑器等。

操作

以下操作均在一般命令模式(默认模式)进行

  1. i:进入编辑模式
  2. ESC:进入一般命令模式
  3. h左箭头键:光标向左移动一个字符
  4. j向下箭头:光标向下移动一个字符
  5. k向上箭头:光标向上移动一个字符
  6. l向右箭头:光标向右移动一个字符
  7. n<Space>:n表示数字,按下数字后再按空格,光标会向右移动这一行的n个字符
  8. 0功能键[Home]:光标移动到本行开头
  9. $功能键[End]:光标移动到本行末尾
  10. G:光标移动到最后一行
  11. :nnG:n为数字,光标移动到第n行
  12. gg:光标移动到第一行,相当于1G
  13. n<Enter>:n为数字,光标向下移动n行
  14. /word:向光标之下寻找第一个值为word的字符串。
  15. ?word:向光标之上寻找第一个值为word的字符串。
  16. n:重复前一个查找操作
  17. N:反向重复前一个查找操作
  18. :n1,n2s/word1/word2/g:n1与n2为数字,在第n1行与n2行之间寻找word1这个字符串,并将该字符串替换为word2
  19. :1,$s/word1/word2/g:将全文的word1替换为word2
  20. :1,$s/word1/word2/gc:将全文的word1替换为word2,且在替换前要求用户确认。
  21. v:选中文本
  22. d:删除选中的文本
  23. dd: 删除当前行
  24. y:复制选中的文本
  25. yy: 复制当前行
  26. p: 将复制的数据在光标的下一行/下一个位置粘贴
  27. u:撤销
  28. Ctrl + r:取消撤销
  29. 大于号 >:将选中的文本整体向右缩进一次
  30. 小于号 <:将选中的文本整体向左缩进一次
  31. :w 保存
  32. :w! 强制保存
  33. :q 退出
  34. :q! 强制退出
  35. :wq 保存并退出
  36. :set paste 设置成粘贴模式,取消代码自动缩进
  37. :set nopaste 取消粘贴模式,开启代码自动缩进
  38. :set nu 显示行号
  39. :set nonu 隐藏行号
  40. gg=G:将全文代码格式化
  41. :noh 关闭查找关键词高亮
  42. Ctrl + q:当vim卡死时,可以取消当前正在执行的命令
  43. Ctrl + s:禁用编辑

异常处理

每次用vim编辑文件时,会自动创建一个.filename.swp的临时文件。
如果打开某个文件时,该文件的swp文件已存在,则会报错。此时解决办法有两种:

  1. 找到正在打开该文件的程序,并退出
  2. 直接删掉该swp文件即可

shell

概论

  1. Linux系统中一般默认使用bash,文件开头需要写#! /bin/bash,指明bash为脚本解释器。
  2. 运行方式
    • 作为可执行文件
      1
      2
      3
      4
      5
      6
      7
      acs@9e0ebfcd82d7:~$ chmod +x test.sh  # 使脚本具有可执行权限
      acs@9e0ebfcd82d7:~$ ./test.sh # 当前路径下执行
      Hello World! # 脚本输出
      acs@9e0ebfcd82d7:~$ /home/acs/test.sh # 绝对路径下执行
      Hello World! # 脚本输出
      acs@9e0ebfcd82d7:~$ ~/test.sh # 家目录路径下执行
      Hello World! # 脚本输出
    • 用解释器执行
      1
      2
      acs@9e0ebfcd82d7:~$ bash test.sh
      Hello World! # 脚本输出

注释

  1. 单行注释
    1
    2
    # 这是一行注释
    echo 'Hello World' # 这也是注释
  2. 多行注释(其中EOF可以换成其它任意字符串),不建议使用

变量

  1. 定义变量,不需要加 $符号,例如:
    1
    2
    3
    name1='jezer'  # 单引号定义字符串
    name2="jezer" # 双引号定义字符串
    name3=jezer # 也可以不加引号,同样表示字符串
    注意=两侧不能有空格
    定义变量的时候变量都是字符串,但当变量需要是整数时,会自动把变量转换成整数
  2. 使用变量,需要加上$符号,或者${}符号。花括号是可选的,主要为了帮助解释器识别变量边界。
    1
    2
    3
    4
    name=jezer
    echo $name # 输出jezer
    echo ${name} # 输出jezer
    echo Hello, ${name} # 输出Hello, jezer
  3. 使用readonly或者declare可以将变量变为只读。
    1
    2
    3
    4
    name=jezer
    readonly name
    declare -r name # 两种写法均可
    name=abc # 会报错,因为此时name只读
    declare

    declare [+/-][rxi][变量名称=设置值] 或 declare -f
    参数说明:

    • +/-  "-“可用来指定变量的属性,”+"则是取消变量所设的属性。
    • -f  仅显示函数。
    • r  将变量设置为只读。
    • x  指定的变量会成为环境变量,可供shell以外的程序来使用。
    • i  [设置值]可以是数值,字符串或运算式。
  4. unset可以删除变量。
    1
    2
    3
    name=jezer
    unset name
    echo $name # 输出空行
    被声明为只读的变量无法被unset删除
  5. 自定义变量(局部变量)改成环境变量(全局变量):
    1
    2
    3
    acs@9e0ebfcd82d7:~$ name=jezer  # 定义变量
    acs@9e0ebfcd82d7:~$ export name # 第一种方法
    acs@9e0ebfcd82d7:~$ declare -x name # 第二种方法
  6. 环境变量改为自定义变量:
    1
    2
    acs@9e0ebfcd82d7:~$ export name=jezer  # 定义环境变量
    acs@9e0ebfcd82d7:~$ declare +x name # 改为自定义变量

字符串

  1. 字符串可以用单引号,也可以用双引号,也可以不用引号。字符串中,不加引号和双引号效果相同
  2. 单引号与双引号的区别:
    • 单引号中的内容会原样输出,不会执行、不会取变量;
    • 双引号中的内容可以执行、可以取变量;
    1
    2
    3
    name=jezer  # 不用引号
    echo 'hello, $name \"hh\"' # 单引号字符串,输出 hello, $name \"hh\"
    echo "hello, $name \"hh\"" # 双引号字符串,输出 hello, jezer "hh"
  3. 获取字符串长度
    1
    2
    name="jezer"
    echo ${#name} # 输出3
  4. 提取子串
    1
    2
    name="hello, jezer"
    echo ${name:0:5} # 提取从0开始的5个字符

默认变量

  1. 文件参数变量
    在执行shell脚本时,可以向脚本传递参数。$1是第一个参数,$2是第二个参数,以此类推。特殊的,$0是文件名(包含路径)。例如:
    创建文件test.sh
    1
    2
    3
    4
    5
    6
    7
    #! /bin/bash

    echo "文件名:"$0
    echo "第一个参数:"$1
    echo "第二个参数:"$2
    echo "第三个参数:"$3
    echo "第四个参数:"$4
    然后执行该脚本:
    1
    2
    3
    4
    5
    6
    7
    acs@9e0ebfcd82d7:~$ chmod +x test.sh
    acs@9e0ebfcd82d7:~$ ./test.sh 1 2 3 4
    文件名:./test.sh
    第一个参数:1
    第二个参数:2
    第三个参数:3
    第四个参数:4
    向脚本传递参数时,参数个数超过一位需要用大括号括起来。
  2. 其它参数相关变量
参数说明
$#代表文件传入的参数个数,如上例中值为4
$*由所有参数构成的用空格隔开的字符串,如上例中值为"$1 $2 $3 $4"
$@每个参数分别用双引号括起来的字符串,如上例中值为"$1" “$2” “$3” “$4”
$$脚本当前运行的进程ID
$?上一条命令的退出状态(注意不是stdout,而是exit code)。0表示正常退出,其他值表示错误
$(command)返回command这条命令的stdout(可嵌套)
`command`返回command这条命令的stdout(不可嵌套)

数组

  1. 数组中可以存放多个不同类型的值,只支持一维数组,初始化时不需要指明数组大小。
  2. 数组下标从0开始。
  3. 数组用小括号表示,元素之间用空格隔开。例如:
    1
    array=(1 abc "def" jezer)
    也可以直接定义数组中某个元素的值:
    1
    2
    3
    4
    array[0]=1
    array[1]=abc
    array[2]="def"
    array[3]=jezer
  4. 读取数组中某个元素的值
    1
    2
    3
    4
    5
    array=(1 abc "def" jezer)
    echo ${array[0]}
    echo ${array[1]}
    echo ${array[2]}
    echo ${array[3]}
  5. 读取整个数组
    1
    2
    3
    array=(1 abc "def" jezer)
    echo ${array[@]} # 第一种写法
    echo ${array[*]} # 第二种写法
  6. 数组长度,类似于字符串
    1
    2
    3
    array=(1 abc "def" jezer)
    echo ${#array[@]} # 第一种写法
    echo ${#array[*]} # 第二种写法
  7. 在读取整个数组时,未定义的部分就不会显示。比如定义了array[0]array[1]array[2]array[1000],利用echo ${array[@]}输出时,只会显示我们定义的数组那4个元素的值,其长度结果是4,而不是1000

expr命令

  1. expr命令用于求表达式的值,格式为:expr 表达式
  2. 表达式说明:
    • 用空格隔开每一项
    • 用反斜杠放在shell特定的字符前面(发现表达式运行错误时,可以试试转义)
    • 对包含空格和其他特殊字符的字符串要用引号括起来
    • expr会在stdout中输出结果。如果为逻辑关系表达式,则结果为真,stdout为1,否则为0。
    • expr的exit code:如果为逻辑关系表达式,则结果为真,exit code为0,否则为1。
  3. 字符串表达式
    • length STRING:返回STRING的长度
    • index STRING CHARSET:CHARSET中任意单个字符在STRING中最前面的字符位置,下标从1开始。如果在STRING中完全不存在CHARSET中的字符,则返回0。
    • substr STRING POSITION LENGTH:返回STRING字符串中从POSITION开始,长度最大为LENGTH的子串。如果POSITION或LENGTH为负数,0或非数值,则返回空字符串。
    1
    2
    3
    4
    str="Hello World!"
    echo `expr length "$str"` # ``不是单引号,表示执行该命令,输出12
    echo `expr index "$str" aWd` # 输出7,下标从1开始
    echo `expr substr "$str" 2 3` # 输出 ell
  4. 整数表达式
    • expr支持普通的算术操作,算术表达式优先级低于字符串表达式,高于逻辑关系表达式。
    • + -加减运算。两端参数会转换为整数,如果转换失败则报错。
    • * / %乘,除,取模运算。两端参数会转换为整数,如果转换失败则报错。
    • () 可以该表优先级,但需要用反斜杠转义
    1
    2
    3
    4
    5
    6
    7
    8
    a=3
    b=4
    echo `expr $a + $b` # 输出7
    echo `expr $a - $b` # 输出-1
    echo `expr $a \* $b` # 输出12,*需要转义
    echo `expr $a / $b` # 输出0,整除
    echo `expr $a % $b` # 输出3
    echo `expr \( $a + 1 \) \* \( $b + 1 \)` # 输出20,值为(a + 1) * (b + 1)
  5. 逻辑关系表达式
    • |&<<====!=>=>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    a=3
    b=4
    echo `expr $a \> $b` # 输出0,>需要转义
    echo `expr $a '<' $b` # 输出1,也可以将特殊字符用引号引起来
    echo `expr $a '>=' $b` # 输出0
    echo `expr $a \<\= $b` # 输出1
    c=0
    d=5
    echo `expr $c \& $d` # 输出0
    echo `expr $a \& $b` # 输出3
    echo `expr $c \| $d` # 输出5
    echo `expr $a \| $b` # 输出3

read命令

  1. read命令用于从标准输入中读取单行数据。当读到文件结束符时,exit code为1,否则为0。
  2. 参数说明
    • -p: 后面可以接提示信息
    • -t:后面跟秒数,定义输入字符的等待时间,超过等待时间后会自动忽略此命令
1
2
3
4
5
6
7
8
acs@9e0ebfcd82d7:~$ read name  # 读入name的值
hello jezer # 标准输入
acs@9e0ebfcd82d7:~$ echo $name # 输出name的值
hello jezer #标准输出
acs@9e0ebfcd82d7:~$ read -p "Please input your name: " -t 30 name # 读入name的值,等待时间30秒
Please input your name: hello jezer # 标准输入
acs@9e0ebfcd82d7:~$ echo $name # 输出name的值
hello jezer # 标准输出

echo命令

  1. 显示普通字符串

    1
    2
    echo "Hello AC Terminal"
    echo Hello AC Terminal # 引号可以省略
  2. 显示转义字符

    1
    2
    echo "\"Hello AC Terminal\""  # 注意只能使用双引号,如果使用单引号,则不转义
    echo \"Hello AC Terminal\" # 也可以省略双引号
  3. 显示变量

    1
    2
    name=jezer
    echo "My name is $name" # 输出 My name is jezer
  4. 显示换行

    1
    2
    echo -e "Hi\n"  # -e 开启转义
    echo "jezer"

    输出结果:

    1
    2
    3
    Hi

    jezer

    \\ \a \b \c \d \e \f \n \r \t \v 这些是要在有 - e 的时候才能起作用, 其他时候的转义是不用- e也能转义的。

  5. 显示不换行

    1
    2
    echo -e "Hi \c" # -e 开启转义 \c 不换行
    echo "jezer"

    输出结果:

    1
    Hi jezer
  6. 显示结果定向至文件

    1
    echo "Hello World" > output.txt  # 将内容以覆盖的方式输出到output.txt中
  7. 原样输出字符串,不进行转义或取变量(用单引号)

    1
    2
    name=jezer
    echo '$name\"'

    输出结果

    1
    $name\"
  8. 显示命令的执行结果

    1
    echo `date`

    输出结果:

    1
    Wed Sep 1 11:45:33 CST 2021

printf命令

  1. printf命令用于格式化输出,类似于C/C++中的printf函数。默认不会在字符串末尾添加换行符。
  2. 命令格式:printf format-string [arguments...]
1
2
3
4
printf "%10d.\n" 123  # 占10位,右对齐
printf "%-10.2f.\n" 123.123321 # 占10位,保留2位小数,左对齐
printf "My name is %s\n" "jezer" # 格式化输出字符串
printf "%d * %d = %d\n" 2 3 `expr 2 \* 3` # 表达式的值作为参数

输出结果

1
2
3
4
      123.
123.12 .
My name is jezer
2 * 3 = 6

判断命令

test语句

  1. 逻辑运算符&&和||具有短路原则:

    • expr1 && expr2:当expr1为假时,直接忽略expr2
    • expr1 || expr2:当expr1为真时,直接忽略expr2
    • 表达式的exit code为0,表示真;为非零,表示假。(与C/C++中的定义相反)
  2. test命令

    • 在命令行中输入man test,可以查看test命令的用法。
    • test命令用于判断文件类型,以及对变量做比较。
    • test命令用exit code返回结果,而不是使用stdout。0表示真,非0表示假。
    1
    2
    test 2 -lt 3  # 为真,返回值为0
    echo $? # 输出上个命令的返回值,输出0
    1
    2
    3
    4
    5
    6
    acs@9e0ebfcd82d7:~$ ls  # 列出当前目录下的所有文件
    homework output.txt test.sh tmp
    acs@9e0ebfcd82d7:~$ test -e test.sh && echo "exist" || echo "Not exist"
    exist # test.sh 文件存在
    acs@9e0ebfcd82d7:~$ test -e test2.sh && echo "exist" || echo "Not exist"
    Not exist # testh2.sh 文件不存在
  3. 文件类型判断

    1
    test -e filename  # 判断文件是否存在
    测试参数代表意义
    -e文件是否存在
    -f是否为文件
    -d是否为目录
  4. 文件权限判断

    1
    test -r filename  # 判断文件是否可读
    测试参数代表意义
    -r文件是否可读
    -w文件是否可写
    -x文件是否可执行
    -s是否为非空文件
  5. 整数间的比较

    1
    test $a -eq $b  # a是否等于b
    测试参数代表意义
    -eqa是否等于b
    -nea是否不等于b
    -gta是大于b
    -lta是小于b
    -gea是大于等于b
    -lea是小于等于b
  6. 字符串比较

    测试参数代表意义
    test -z STRING判断STRING是否为空,如果为空,返回true
    test -n STRING判断STRING是否为非空空,如果非空,返回true
    test str1 == str2判断str1是否等于str2
    test str1 != str2判断str1是否不等于str2
  7. 多重条件判定

    1
    test -r filename -a -x filename
    测试参数代表意义
    -a两条件是否同时成立
    -o两条件是否至少一个成立
    !取反。如 test ! -x file,当file不可执行时,返回true
  8. 判断符号[]

    1. []与test用法几乎一模一样,更常用于if语句中。另外[[]]是[]的加强版,支持的特性更多。
    2. []内的每一项都要用空格隔开
    3. 中括号内的变量,最好用双引号括起来
    4. 中括号内的常数,最好用单或双引号括起来
    1
    2
    [ 2 -lt 3 ]  # 为真,返回值为0
    echo $? # 输出上个命令的返回值,输出0
    1
    2
    3
    name="hello jezer"
    [ $name == "hello jezer" ] # 错误,等价于 [ hello jezer == "hello jezer" ],参数太多
    [ "$name" == "hello jezer" ] # 正确

if语句

  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}在范围内
    fi
  2. 单层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
    9
    a=3
    b=4
    if ! [ "$a" -lt "$b" ]
    then
    echo ${a}不小于${b}
    else
    echo ${a}小于${b}
    fi

  3. 多层if-elif-elif-else

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    if condition
    then
    语句1
    语句2
    ...
    elif condition
    then
    语句1
    语句2
    ...
    elif condition
    then
    语句1
    语句2
    else
    语句1
    语句2
    ...
    fi
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    a=4
    if [ $a -eq 1 ]
    then
    echo ${a}等于1
    elif [ $a -eq 2 ]
    then
    echo ${a}等于2
    elif [ $a -eq 3 ]
    then
    echo ${a}等于3
    else
    echo 其他
    fi
  4. case…esac形式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    case $变量名称 in
    值1)
    语句1
    语句2
    ...
    ;; # 类似于C/C++中的break
    值2)
    语句1
    语句2
    ...
    ;;
    *) # 类似于C/C++中的default
    语句1
    语句2
    ...
    ;;
    esac
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    a=4
    case $a in
    1)
    echo ${a}等于1
    ;;
    2)
    echo ${a}等于2
    ;;
    3)
    echo ${a}等于3
    ;;
    *)
    echo 其他
    ;;
    esac

循环语句

  1. for…in…do…done

    1
    2
    3
    4
    5
    6
    for var in val1 val2 val3
    do
    语句1
    语句2
    ...
    done
    • 输出a 2 cc,每个元素一行:

      1
      2
      3
      4
      for i in a 2 cc
      do
      echo $i
      done
    • 输出当前路径下的所有文件名,每个文件名一行:

      1
      2
      3
      4
      for file in `ls`
      do
      echo $file
      done
    • 输出1-10

      1
      2
      3
      4
      for i in $(seq 1 10)
      do
      echo $i
      done
    • {1…10} 或者 {a…z}

      1
      2
      3
      4
      for i in {a..z}
      do
      echo $i
      done
  2. for ((…;…;…)) do…done

    1
    2
    3
    4
    5
    for ((expression; condition; expression))
    do
    语句1
    语句2
    done
    1
    2
    3
    4
    5
    # 输出1-10,每个数占一行:
    for ((i=1; i<=10; i++))
    do
    echo $i
    done
  3. while…do…done循环

    1
    2
    3
    4
    5
    6
    while condition
    do
    语句1
    语句2
    ...
    done
    1
    2
    3
    4
    5
    # 文件结束符为Ctrl+d,输入文件结束符后read指令返回false。
    while read name
    do
    echo $name
    done
  4. until…do…done循环

    1
    2
    3
    4
    5
    6
    until condition
    do
    语句1
    语句2
    ...
    done
    1
    2
    3
    4
    5
    # 当用户输入yes或者YES时结束,否则一直等待读入。
    until [ "${word}" == "yes" ] || [ "${word}" == "YES" ]
    do
    read -p "Please input yes/YES to stop this program: " word
    done
  5. break命令

    • 跳出当前一层循环,注意与C/C++不同的是:break不能跳出case语句。
    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
    • 该示例每读入非EOF的字符串,会输出一遍1-7。
  6. 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

    该程序输出1-10中的所有奇数。

  7. 死循环的处理方式

    • 如果Terminal可以打开该程序,则输入Ctrl+c即可。
    • 否则可以直接关闭进程:
      1. 使用top命令找到进程的PID
      2. 输入kill -9 PID即可关掉此进程

函数

  1. bash中的函数类似于C/C++中的函数,但return的返回值与C/C++不同,返回的是exit code,取值为0-255,0表示正常结束。
  2. 如果想获取函数的输出结果,可以通过echo输出到stdout中,然后通过$(function_name)来获取stdout中的结果。
  3. 函数的return值可以通过$?来获取。
1
2
3
4
5
[function] func_name() {  # function关键字可以省略
语句1
语句2
...
}
1
2
3
4
5
6
7
# 不获取 return值和stdout值
func() {
name=jezer
echo "Hello $name"
}

func

输出结果:

1
Hello jezer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 获取 return值和stdout值
# 不写return时,默认return 0
func() {
name=jezer
echo "Hello $name"

return 123
}

output=$(func)
ret=$?

echo "output = $output"
echo "return = $ret"

输出结果:

1
2
output = Hello jezer
return = 123
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
# 函数的输入参数
# 在函数内,$1表示第一个输入参数,$2表示第二个输入参数,依此类推
# 注意:函数内的$0仍然是文件名,而不是函数名
func() { # 递归计算 $1 + ($1 - 1) + ($1 - 2) + ... + 0
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)

输出结果:

1
55
1
2
3
4
5
6
7
8
9
10
# 函数内的局部变量
# local 变量名=变量值
# 第一行为函数内的name变量,第二行为函数外调用name变量,会发现此时该变量不存在。
func() {
local name=jezer
echo $name
}
func

echo $name

输出结果:

1
jezer

exit命令

  1. exit命令用来退出当前shell进程,并返回一个退出状态;使用$?可以接收这个退出状态。
  2. exit命令可以接受一个整数值作为参数,代表退出状态。如果不指定,默认状态值是 0。
  3. exit退出状态只能是一个介于 0~255 之间的整数,其中只有 0 表示成功,其它值都表示失败。
1
2
3
4
5
6
7
8
if [ $# -ne 1 ]  # 如果传入参数个数等于1,则正常退出;否则非正常退出。
then
echo "arguments not valid"
exit 1
else
echo "arguments valid"
exit 0
fi
1
2
3
4
5
6
7
8
9
acs@9e0ebfcd82d7:~$ chmod +x test.sh
acs@9e0ebfcd82d7:~$ ./test.sh jezer
arguments valid
acs@9e0ebfcd82d7:~$ echo $? # 传入一个参数,则正常退出,exit code为0
0
acs@9e0ebfcd82d7:~$ ./test.sh
arguments not valid
acs@9e0ebfcd82d7:~$ echo $? # 传入参数个数不是1,则非正常退出,exit code为1
1

文件重定向

  1. 重定向命令列表

    命令说明
    command > file将stdout重定向到file中
    command < file将stdin重定向到file中
    command >> file将stdout以追加方式重定向到file中
    command n> file将文件描述符n重定向到file中
    command n>> file将文件描述符n以追加方式重定向到file中
  2. 输入和输出重定向

    1
    2
    3
    4
    5
    6
    echo -e "Hello \c" > output.txt  # 将stdout重定向到output.txt中
    echo "World" >> output.txt # 将字符串追加到output.txt中

    read str < output.txt # 从output.txt中读取字符串

    echo $str # 输出结果:Hello World
  3. 同时重定向stdin和stdout

    • 创建bash脚本
    1
    2
    3
    read a
    read b
    echo $(expr "$a" + "$b")
    • 创建input.txt,里面的内容为
    1
    2
    3
    4
    • 执行命令
    1
    2
    3
    4
    acs@9e0ebfcd82d7:~$ chmod +x test.sh  # 添加可执行权限
    acs@9e0ebfcd82d7:~$ ./test.sh < input.txt > output.txt # 从input.txt中读取内容,将输出写入output.txt中
    acs@9e0ebfcd82d7:~$ cat output.txt # 查看output.txt中的内容
    7

引入外部脚本

1
2
3
4
5
. filename  # 注意点和文件名之间有一个空格



source filename

ssh

  1. 远程登录服务器

    1
    2
    3
    4
    # 远程登录服务器
    ssh user@hostname
    # 默认登录端口号为22。如果想登录某一特定端口:
    ssh user@hostname -p 22
  2. 配置文件,创建文件~/.ssh/config,然后在文件中输入:

    1
    2
    3
    4
    5
    6
    7
    8
    Host myserver1
    HostName IP地址或域名
    User 用户名
    Port 20002

    Host myserver2
    HostName IP地址或域名
    User 用户名

    之后再使用服务器时,可以直接使用别名myserver1、myserver2。

  3. 创建密钥:

    1
    ssh-keygen

    然后一直回车即可。
    执行结束后,~/.ssh/目录下会多两个文件:

    • id_rsa:私钥
    • id_rsa.pub:公钥
  4. 复制密钥:

    1
    ssh-copy-id myserver
  5. 执行命令:

    1
    ssh user@hostname command
    1
    2
    # 单引号中的$i可以求值
    ssh myserver 'for ((i = 0; i < 10; i ++ )) do echo $i; done'
    1
    2
    # 双引号中的$i不可以求值
    ssh myserver "for ((i = 0; i < 10; i ++ )) do echo $i; done"

    双引号是在本地服务器进行转义了,所以传过去命令不是echo $a,而是echo 1,单引号传过去的是echo $a

  6. scp传文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # 将source路径下的文件复制到destination中
    scp source destination
    # 一次复制多个文件
    scp source1 source2 destination
    # 复制文件夹
    scp -r ~/tmp myserver:/home/acs/
    # 指定服务器的端口号
    scp -P 22 source1 source2 destination
    # 使用scp配置其他服务器的vim和tmux
    scp ~/.vimrc ~/.tmux.conf myserver:

    scp的-r -P等参数尽量加在source和destination之前

  7. 配置新服务器的工作环境
    AC Terminal的配置传到新服务器上:

    1
    scp .bashrc .vimrc .tmux.conf server_name:  # server_name需要换成自己配置的别名

git

  1. 全局设置
    • git config --global user.name xxx:设置全局用户名,信息记录在~/.gitconfig文件中
    • git config --global user.email xxx@xxx.com:设置全局邮箱地址,信息记录在~/.gitconfig文件中
    • git init:将当前目录配置成git仓库,信息记录在隐藏的.git文件夹中
  2. 常用命令
    • git add XX :将XX文件添加到暂存区
    • git commit -m "给自己看的备注信息":将暂存区的内容提交到当前分支
    • git status:查看仓库状态
    • git log:查看当前分支的所有版本
    • git push -u (第一次需要-u以后不需要):将当前分支推送到远程仓库
    • git clone git@git.acwing.com:xxx/XXX.git:将远程仓库XXX下载到当前目录下
    • git branch:查看所有分支和当前所处分支
  3. 查看命令
    • git diff XX:查看XX文件相对于暂存区修改了哪些内容
    • git status:查看仓库状态
    • git log:查看当前分支的所有版本
    • git log --pretty=oneline:用一行来显示
    • git reflog:查看HEAD指针的移动历史(包括被回滚的版本)
    • git branch:查看所有分支和当前所处分支
    • git pull:将远程仓库的当前分支与本地仓库的当前分支合并
  4. 删除命令
    • git rm --cached XX:将文件从仓库索引目录中删掉,不希望管理这个文件
    • git restore --staged xx:将xx从暂存区里移除
    • git checkout — XXgit restore XX:将XX文件尚未加入暂存区的修改全部撤销
  5. 代码回滚
    • git reset --hard HEAD^git reset --hard HEAD~:将代码库回滚到上一个版本
    • git reset --hard HEAD^^:往上回滚两次,以此类推
    • git reset --hard HEAD~100:往上回滚100个版本
    • git reset --hard 版本号:回滚到某一特定版本(7位)
  6. 远程仓库
    • git remote add origin git@git.acwing.com:xxx/XXX.git:将本地仓库关联到远程仓库
    • git push -u (第一次需要-u以后不需要):将当前分支推送到远程仓库
    • git push origin branch_name:将本地的某个分支推送到远程仓库
    • git clone git@git.acwing.com:xxx/XXX.git:将远程仓库XXX下载到当前目录下
    • git push --set-upstream origin branch_name:设置本地的branch_name分支对应远程仓库的branch_name分支
    • git push -d origin branch_name:删除远程仓库的branch_name分支
    • git checkout -t origin/branch_name: 将远程的branch_name分支拉取到本地
    • git pull:将远程仓库的当前分支与本地仓库的当前分支合并
    • git pull origin branch_name:将远程仓库的branch_name分支与本地仓库的当前分支合并
    • git branch --set-upstream-to=origin/branch_name1 branch_name2:将远程的branch_name1分支与本地的branch_name2分支对应
  7. 分支命令
    • git branch branch_name:创建新分支
    • git branch:查看所有分支和当前所处分支
    • git checkout -b branch_name:创建并切换到branch_name这个分支
    • git checkout branch_name:切换到branch_name这个分支
    • git merge branch_name:将分支branch_name合并到当前分支上
    • git branch -d branch_name:删除本地仓库的branch_name分支
    • git push --set-upstream origin branch_name:设置本地的branch_name分支对应远程仓库的branch_name分支
    • git push -d origin branch_name:删除远程仓库的branch_name分支
    • git checkout -t origin/branch_name: 将远程的branch_name分支拉取到本地
    • git pull:将远程仓库的当前分支与本地仓库的当前分支合并
    • git pull origin branch_name:将远程仓库的branch_name分支与本地仓库的当前分支合并
    • git branch --set-upstream-to=origin/branch_name1 branch_name2:将远程的branch_name1分支与本地的branch_name2分支对应
  8. stash暂存
    • git stash:将工作区和暂存区中尚未提交的修改存入栈中
    • git stash apply:将栈顶存储的修改恢复到当前分支,但不删除栈顶元素
    • git stash drop:删除栈顶存储的修改
    • git stash pop:将栈顶存储的修改恢复到当前分支,同时删除栈顶元素
    • git stash list:查看栈中所有元素

docker

  1. 为了避免每次使用docker命令都需要加上sudo权限,可以将当前用户加入安装中自动创建的docker用户组(可以参考官方文档):
    1
    sudo usermod -aG docker $USER
  2. 镜像(images)
    1. docker pull ubuntu:20.04:拉取一个镜像
    2. docker images:列出本地所有镜像
    3. docker image rm ubuntu:20.04docker rmi ubuntu:20.04:删除镜像ubuntu:20.04
    4. docker [container] commit CONTAINER IMAGE_NAME:TAG:创建某个container的镜像
    5. docker save -o ubuntu_20_04.tar ubuntu:20.04:将镜像ubuntu:20.04导出到本地文件ubuntu_20_04.tar中
    6. docker load -i ubuntu_20_04.tar:将镜像ubuntu:20.04从本地文件ubuntu_20_04.tar中加载出来
  3. 容器(container)
    1. docker [container] create -it ubuntu:20.04:利用镜像ubuntu:20.04创建一个容器。
    2. docker ps -a:查看本地的所有容器
    3. docker [container] start CONTAINER:启动容器
    4. docker [container] stop CONTAINER:停止容器
    5. docker [container] restart CONTAINER:重启容器
    6. docker [contaienr] run -itd ubuntu:20.04:创建并启动一个容器
    7. docker run -p 20000:22 --name my_docker_server -itd docker_lesson:1.0:创建并运行docker_lesson:1.0镜像,端口转发
    8. docker [container] attach CONTAINER:进入容器
    9. 先按Ctrl+p,再按Ctrl+q可以挂起容器
    10. docker [container] exec CONTAINER COMMAND:在容器中执行命令
    11. docker [container] rm CONTAINER:删除容器
    12. docker container prune:删除所有已停止的容器
    13. docker export -o xxx.tar CONTAINER:将容器CONTAINER导出到本地文件xxx.tar中
    14. docker import xxx.tar image_name:tag:将本地文件xxx.tar导入成镜像,并将镜像命名为image_name:tag
    15. docker export/importdocker save/load的区别:export/import会丢弃历史记录和元数据信息,仅保存容器当时的快照状态;save/load会保存完整记录,体积更大
    16. docker top CONTAINER:查看某个容器内的所有进程
    17. docker stats:查看所有容器的统计信息,包括CPU、内存、存储、网络等信息
    18. docker cp xxx CONTAINER:xxxdocker cp CONTAINER:xxx xxx:在本地和容器间复制文件
    19. docker rename CONTAINER1 CONTAINER2:重命名容器
    20. docker update CONTAINER --memory 500MB:修改容器限制docker
  4. 增加容器的映射端口:80与443
    1. 登录容器,关闭所有运行中的任务
    2. 登录运行容器的服务器,然后执行:
      1
      2
      3
      4
      5
      6
      docker commit CONTAINER_NAME IMAGE_NAME:TAG  # 将容器保存成镜像,将CONTAINER_NAME替换成容器名称,IMAGE_NAME:TAG为保存后的镜像名称
      docker stop CONTAINER_NAME # 关闭容器
      docker rm CONTAINER_NAME # 删除容器

      # 使用保存的镜像重新创建容器
      docker run -p 20000:22 -p 8000:8000 -p 80:80 -p 443:443 --name CONTAINER_NAME -itd IMAGE_NAME:TAG
    3. 去云服务器控制台,在安全组配置中开放80和443端口。

扩展

Linux docker容器实现ssh开机自启

  1. 进入容器,安装openssh-server
    1
    2
    3
    4
    5
    6
    7
    8
    9
    sudo apt update
    sudo apt install openssh-server
    # 启动之前需手动创建/var/run/sshd,不然启动sshd的时候会报错
    mkdir -p /var/run/sshd
    # sshd以守护进程运行
    /usr/sbin/sshd -D &
    # 安装netstat,查看sshd是否监听22端口
    apt-get install net-tools
    netstat -apn | grep ssh
  2. /root 目录下新建一个 start_ssh.sh文件,并给予该文件可执行权限
    1
    2
    touch /root/start_ssh.sh
    chmod +x /root/start_ssh.sh
  3. 编辑start_ssh.sh,输入以下内容
    1
    2
    3
    4
    5
    6
    #!/bin/bash

    LOGTIME=$(date "+%Y-%m-%d %H:%M:%S")
    echo "[$LOGTIME] startup run..." >>/root/start_ssh.log
    service ssh start >>/root/start_ssh.log
    #service mysql start >>/root/star_mysql.log //其他服务也可这么实现
  4. start_ssh.sh脚本添加到启动文件中,即在 /root/.bashrc 文件末尾加入如下内容
    1
    2
    3
    4
    # startup run
    if [ -f /root/start_ssh.sh ]; then
    ./root/start_ssh.sh
    fi
  5. 保存后,等下次重启容器的时候,添加的服务也就跟着重启了