bash 速描

  • bash 速描

  • 资料来源:

    https://wangdoc.com/bash/index.html

  • 更新

    1
    2
    3
    4
    20.05.19 初始化
    20.05.22 变量
    20.05.23 字符串
    20.05.24 行操作

导语

  • 重刷一遍 bash.
  • 最开始接触 bash 是跟着 鸟哥的linux私房菜,之后没有太多应用就放弃了.然而现在又到了大量需要时候..
  • 正好阮一峰老师的 bash 教程 发布了,wsl2 也随着 win10 2004 到了正式版中,条件成熟,开搞.

简介

  • shell,这个词听说了很久,所谓 shell ,相对于 kernel 而言,就是壳的意思.
  • shell 可以说是用户通过命令与内核交互的途径.shell 可以有很多种,我们熟悉的 sh bash zsh 就是几种常见的 shell 类型.
  • 使用最广的自然是 bash 了(其实挺想学学 zsh 的,但是别着急).
  • 2019 年 bash 最新版本是 5.0.3(1)-release.
  • 这一篇仅仅是 bash 的速描,而非教程.

基础语法

  • echo(ow新英雄😂)

    • 文本输出到屏幕
    • 多行需要 “”
    • -n: 去掉默认会在文本末尾添加的 \n 换行符.
    • -e: 解释 “” 内部的特殊字符,没有-e 例如 \n 就会直接输出 \n 字符,而不是换行.
  • 命令格式

    • 基本上是 $ command [ arg1 ... [ argN ]]
    • 命令 + 参数1 ..参数N
    • 一般参数有两种
      • -: 短形式,简写,在命令行居多.
      • --: 长形式,脚本中居多.
    • 多行编辑时,通过 \ 连接多行命令,执行时会合并成一行.
  • 空格

    • bash 的不同参数之间以空格区分
    • 会忽略多个空格
  • 分号

    • 分割两个命令
    • 即使第一个命令执行失败,第二个命运也会继续执行.
  • 命令组合

    • Command1 && Command2: 只有命令1执行成功,才执行命令2.
    • Command1 || Command2: 不管命令1执行如何,始终执行命令2.
  • type命令

    • 可以区分命令本身来源于内置命令,还是外部程序.
    • type 本身也是内置命令
    • -a: 输出命令的全部定义
    • -t: 返回命令的全部类型,
      • 别名 alias
      • 关键字 keyword
      • 函数 function
      • 内置命令 builtin
      • 文件 file
  • 快捷键

    • Ctrl + L: 清屏,把当前行移动到页面最上位置.
    • Ctrl + C: 中断命令
    • Ctrl + U: 由光标位置,删除到行首
    • Ctrl + K: 由光标位置,删除到行末
    • Ctrl + D: 等于 exit
    • Shift + PageUP: 向上滚动
    • Shift + PageDown: 向下滚动
  • TAB 补全,如果遇到无法补全,检查一下 bash-completion 这个插件,装没装.

模式拓展

  • 如果要完成一项复杂的工作,纯粹的 shell 命令会烦死的,由此引入的模式拓展.

  • 模式拓展,大概是将 ?*等等通过预定的模式展开成原始的命令,最大的作用是省纸.

  • 有点类似于正则啊,实际上模式拓展要早于正则,但是模式拓展要简单方便很多.

  • 当然模式拓展是可关闭的.set -f 关闭,set +f 打开.

  • 波浪线拓展

    • ~: 用户当前的主目录
    • ~user: 用户 user 的主目录.
    • ~+: =pwd 当前目录
  • ?字符拓展

    • ?: 文件路径中任意的单字符,不包括空字符.
    • 如果匹配的文件存在才进行转换,否则只是输出成 ? 本身.
  • *字符拓展

    • *: 文件路径中任意数量的任意字符(类似正则的 .*)
    • .*: 匹配隐藏文件.
    • .[!.]*: 排除 . .. 以外的隐藏文件,以 . 开头,第二个字符不是 .,之后是任意字符.
    • **/*: 匹配包括任意层级子目录的任意文件.
    • 只有文件确实存在时,才会进行转换,否则只是输出 * 本身.
  • 方括号拓展

    • [..]: 一个方括号内任意字符.
    • [^..][!..]: 任何一个不在方括号内任意字符
    • [[..]: 匹配 [ 这个字符本身.
    • [-..][..-]: 匹配 - 这个字符时,只能放在开头或结尾.
    • 只有文件确实存在时,才会进行转换,否则只是输出 [..] 本身.
  • [start-end] 拓展

    • [a-z]: 所有小写字母任意一个
    • [a-zA-Z]: 所有小写大写字母任意一个
    • [a-zA-Z0-9]: 所有大小写字母和数字任意一个
    • [!a-zA-Z]: 除了英文字母以外的任意一个字符
  • 大括号拓展

    • 非文件拓展,会自动转换成所有值
    • {...}: 自动拓展成大括号内所有值,各个值期使用逗号隔开.逗号前后不能有空格.
    • {j{p,pe}g,png}: 大括号嵌套,可以一定程度上当生成器了
    • {cat,d*}: 大括号可以与其它模式连用,但是大括号总是优先于其他模式.这里就是匹配 catd* 的文件.
  • {start..end} 扩展,类似中括号 [start-end]

    • {a..c}: a b c
    • {1..4}: 1 2 3 4
    • {c..b}: c b a
    • {4..1}: 4 3 2 1
    • {01..5}: 01 02 03 04
    • {0..8..2}: 0 2 4 6 8 (步长是2)
    • 无法转换的简写,原样输出.
  • 变量拓展

    • 以一个 $ 代表变量值..嗯..kotlin 有这味.
    • $value: 拓展成对应变量值
    • ${value}: 同上.
    • ${!string*}${!string@}: 返回所有匹配给定字符串string的变量名
  • 子命令拓展

    • $(...): 将括号内的命令运行结果作为拓展输出.
    • $ `…`: 同上.
    • 可以嵌套.
  • 算术拓展

    • echo $((2 + 2)): 结果拓展为 4 .
  • 字符类

    • [[:class:]]: 表示一个字符类,扩展成某一类特定字符之中的 一个.

    • 当没有匹配时,会直接输出.

    • [[:alnum:]]:匹配任意英文字母与数字

    • [[:alpha:]]:匹配任意英文字母

    • [[:blank:]]:空格和 Tab 键

    • [[:cntrl:]]:ASCII 码 0-31 的不可打印字符

    • [[:digit:]]:匹配任意数字 0-9

    • [[:graph:]]:A-Z、a-z、0-9 和标点符号

    • [[:lower:]]:匹配任意小写字母 a-z

    • [[:print:]]:ASCII 码 32-127 的可打印字符

    • [[:punct:]]:标点符号(除了 A-Z、a-z、0-9 的可打印字符)

    • [[:space:]]:空格、Tab、LF(10)、VT(11)、FF(12)、CR(13)

    • [[:upper:]]:匹配任意大写字母 A-Z

    • [[:xdigit:]]:16进制字符(A-F、a-f、0-9)

    • 例子:

      • $ echo [[:upper:]]*: 所有以大写字母开头的文件.
      • $ echo [![:digit:]]*: 所有不以数字开头的文件
  • 量词语法

    • 量词语法控制拓展的匹配次数.由 extglob 参数控制,一般是开着的.
    • 当没有匹配时,原样输出.
    • ?(pattern-list):0 或 1
    • *(pattern-list):0 或 多
    • +(pattern-list):1 或 多
    • @(pattern-list):1
    • !(pattern-list):匹配零个或一个以上的模式,但不匹配单独一个的模式(??)
    • 例子
      • abc?(.)txt: 不取 或 只取第一个符合开头 abc 结尾 .txt 的文件
      • abc+(.txt|.php): 不取 或 匹配多个以 adc 开头,结尾是 .txt 或 .php 的文件
  • 匹配注意点

    • 通配符是先拓展再执行.
    • 文件名拓展不存在匹配时,基本都是按照原样输出.
    • 通配符文件名拓展一般只适用于本层路径.子路径需要声明子路径才行xx/xx.也可以使用 **/xx,直接匹配所有的文件(不可控).
    • Bash 允许文件名使用通配符,即文件名包括特殊字符,这时引用文件名,需要把文件名放在单引号里面.fo*.
  • shopt 命令

    • shopt命令可以调整 Bash 的行为.
      • $ shopt -s [optionname]: 打开某个参数
      • $ shopt -u [optionname]: 关闭某个参数
      • $ shopt [optionname]: 查询某个参数关闭还是打开
    • dotglob: 拓展结果包含隐藏文件
    • nullglob: 拓展没有结果时,返回空字符.
    • failglob: 拓展没有结果时,直接报错,不再执行.
    • extglob: Bash 支持 ksh 的一些扩展语法,量词拓展就是之一.
    • nocaseglob: 通配符拓展不再区分大小写.
    • globsta: ** 匹配零个或多个子目录,默认是关闭的.

引号和转义

  • bash 只有字符串一直类型的变量.对应特殊字符有一套转义的规则,整体和 c/kotlin 差不多.

  • 转义

    • 输出特殊字符 \ + 特殊字符 $ * &
    • \\ 输出自身
    • \a 响铃(??); \b 退格; \n 换行; \r 回车; \t 制表符(??)
  • 单引号

    • 单引号内部所有字符均视为普通字符,不会有拓展的效果.
    • 单引号内部使用单引号,$`it\'s' 需要在外层单引号前加 $,同时在 单引号前+转义.
    • 比较合理的做法是在双引号中使用单引号.
  • 双引号

    • 大部分特殊字符,双引号会保留原意,除$ ` \除外,会自动拓展.
    • 特别留意 * 在双引号中也只是普通字符,不会进行文件名拓展.
    • 文件名包含空格,必须使用双引号.test kot.txt,而且双引号会原样保持多余空格.
    • 双引号还能保存原始命令的输出格式.如果没有双引号,基本都堆到了单行.echo "$(cal)"
  • Here 文档

    • 一种输入多行字符串的方法.

    • 格式

      1
      2
      3
      << token
      text
      token
      • << token: 开始标记,<< 打头,之后是文件名,文件名任意,但最后一个必须是换行符.
      • token: 结束标记,文件名,必须是定格.
      • 中间是多行的字符串内容
    • Here 内部变量替换,转义字符依旧有效,但拓展和单引号双引号失效.

    • 如果不希望有变量替换,可以将开始标记的文件名加上单引号.<< `example`

    • Here 文档的本质是重定向,将字符串重定向到了某个命令,相当于加了 echo.echo string | command

    • 因此 Here 文档只能适用于那些接收标准输入为参数的命令.

  • Here 字符串

    • 算是 Here 文档的变体,使用 <<< string 将字符串通过标准输入传递给命令.
    • 原理大致相同,等同于添加了 echo.

变量

  • bash 下的变量分为两种,一是环境变量,另一个是自定义变量.

    • 环境变量是 bash 自带的变量,由系统定义.
    • 自定义变量即用户自行定义,当关闭 shell 后就不再存在.
    • env: 输出所有环境变量.
    • echo xx: 输出单个变量.
    • set: 输出所有变量及 bash 函数.
  • 创建变量

    • 变量名通常是大写字母表示.(约定俗成)
    • bash区分大小写.
    • 字母/数字/下划线.
    • 开头必须是字母/下划线.
    • 不允许空格/标点符号.
    • 所有类型均为字符串.
    • 变量值有空格等,需要单/双引号.
  • 读取变量

    • $xxx${xxx}: 读取变量
    • \$: 要正常使用 $,需要转义.
    • ${!xxx}: 如果变量值本身是另一个变量,可以加 ! 读取到最终的值.
  • 删除变量

    • unset NAME: 删除变量,但是并不怎么好用..变量都是字符串类型,删除后还是空字符串.
    • NAME=''NAME=: 都等同于 unset.
  • 输出变量(更多是传递变量吧)

    • export xxx: 子 shell 可以读取到父 shell 对应的变量值.而且修改不影响父 shell.(类似继承)
  • 特殊变量

    • 由 shell 定义,用户只能使用不能赋值.
    • $?: 上一个命令的退出码,为 0 ,则上一条命令执行成功.
    • $$: 当前 shell 的 pid.
    • $_: 上一个命令的最后参数.
    • $!: 最近一个后台执行异步命令的进程 id .
    • $0: 当前 shell 的名称.
    • $-: 当前 shell 的启动参数.
    • $@ $#: 脚本的参数数量.
  • 变量的默认值

    • Bash 可以通过默认值防止变量为空.
    • ${varname:-word}: varname 空返回 word.
    • ${varname:=word}: varname 空设置 varname=word 并返回.
    • ${varname:+word}: varname 存在且不为空,返回 word,否则返回空值.这是个测试命令.
    • ${varname:?message}: varname 为空,打印 varname: message 并中断脚本的执行.防止变量未定义.
  • declare 命令

    • declare 可以为变量设置一些限制.像只读,整数类型等.
    • declare OPTION VARIABLE=value: 基本格式
    • -i: 声明整形参数,可以直接运算了.但是即使声明成整形了,依然可以被改写成其他字符串.
    • -x: 等同于 export ,可以将变量继承到子 shell.
    • -r: 声明只读变量,unset 也不行.
    • -u: 声明为大写字母,自动将变量值的小写转大写..(强迫症专用)
    • -l: 声明为小写字母,同上(强迫症专用)
    • -p: 输出变量信息,不指定会输出全部.
    • -f: 输出当前环境所有函数,包括定义.
    • -F: 输出当前环境所有函数,不包括定义.
  • readonly 命令

    • 单 readonly 等同于 declare -r 只读.
    • 参数
      • -f: 声明变量为函数名
      • -p: 打印出所有只读变量
      • -a: 声明变量为数组
  • let 命令

    • let foo=1+2: 可以直接执行运算.
    • let "foo = 1 + 2": 运算包含空格,需要引号.
    • let "v1 = 1" "v2 = v1++": 多个表达式可以用空格隔离.

字符串操作

  • 字符串是 bash 唯一的变量类型,这一节是对字符串的操作.

  • 字符串长度

    • 实例

      1
      ${#varname} : 输出字符串长度,大括号必须,否则 `$#` 会被拓展成脚本的参数个数.
  • 子串提取

    • ${varname:offset:length}: 返回 varname 的子串,从 offset 长度为 length.索引从 0 开始.
    • {varname:offset:length}: 同上,但 $ 可省略
    • ${varname:offset}: 从 offset 开始一直到结尾.
    • ${varname: offset:length}: 这里 offset 可以为负值,但是前面需要加一个空格隔开.而且 length 必须大于 0.
  • 搜索和替换(原始变量并不会被改变)

    • ${variable#pattern}: 从头开始,非贪婪匹配,删除匹配部分并返回.
    • ${variable##pattern}: 从头开始,贪婪匹配,删除匹配部分并返回.
    • ${variable%pattern}: 从尾开始,非贪婪匹配,删除匹配部分并返回.
    • ${variable%%pattern}: 从尾开始,贪婪匹配,删除匹配部分并返回.
    • 匹配部分可以有个 * ? [] 等,具体模式与上文相同.
    • 没有匹配时,会直接原样返回.
    • ${variable/pattern/string}: 贪婪匹配,但仅替换第一个匹配.
    • ${variable//pattern/string}: 贪婪匹配,替换所有.
    • ${variable/#pattern/string}: 模式必须在字符串开头
    • ${variable/%pattern/string}: 模式必须在字符串的结尾.
  • 改变大小写

    • ${varname^^}: 转为大写
    • ${varname,,}: 转为小写

算术运算

  • 虽然 bash 变量都是字符串,但是还是能运算的.

  • 算术表达式

    • ((...)): 自动运算双括号内算术,并且忽略算式空格.结果如果为 0 算是命令执行失败.$?会查询到非 0.
    • $((...)): 这样就算是算术表达式,可以返回算术的值.
    • + - * %: 没啥区别
    • /: 整除,结果是整数.
    • **: 指数
    • ++ --: 与 c 相同,有前后之分.前面有命令根据位置会有先运算还是先执行命令的差别.
    • $(( (2 + 3) * 4 )): 内部可以使用括号,优先级最高.
    • echo $(($((5**2)) * 3)): $((...)) 可以嵌套,但是只能计算整数,否则报错.
    • 算术表达式内可以加 $varname,也可以不加 varname,如果对应的变量并不存在,对应会返回空值.
    • ps: 还有一种写法是 $[...],已经过时了,不要再用了.
  • 进制

    • bash 默认是十进制,当然也能使用其他进制.
    • 0number: 8 进制
    • 0xnumber: 16 进制
    • base#number: base 进制的数.
  • 位运算(跟 c 差不多)

    • <<: 左移
    • >>: 右移
    • &: 与运算
    • |: 或运算
    • ~: 取反
    • ^: 亦或
    • 位运算一般配合 8/16 进制数.
  • 逻辑运算

    • <; >; <=; >=; ==; !=; &&; ||; !: 与 c 完全相同.
    • expr1?expr2:expr3: 支持三目运算符让我有点意外.
    • 逻辑表达式为真返回 1,假返回 0.(跟 c 一样)
  • 赋值运算

    • $((...)): 执行赋值运算.
    • 支持的运算符 =; +=; -=; *=; /=; %=; <<=; >>=; &=; |=; ^=;
    • 跟 c 也一样.
  • 求值运算

    • echo $((foo = 1 + 2, 3 * 4)): 在 $((...)) 双括号内, , 是求值运算符.
    • 上面的表达式会输出 12 ,然后对 foo 赋值.
  • expr 命令

    • 有时双括号有点眼花 (◎﹏◎).
    • 可以使用 exper xxx 代替
    • 支持变量替换,但不支持非整数参数.

行操作

  • bash 内置了 Readline 库,行操作基于此,默认是 Emacs 快捷键,没必要不需要改.

  • 光标移动(alt 可由 esc 代替)

    • Ctrl + a:移到行首
    • Ctrl + b:向行首移动一个字符,与左箭头作用相同
    • Ctrl + e:移到行尾
    • Ctrl + f:向行尾移动一个字符,与右箭头作用相同
    • Alt + f:移动到当前单词的词尾
    • Alt + b:移动到当前单词的词首
  • 清屏

    • ctrl + l: 等同于 clear
  • 编辑操作

    • 编辑行的内容
    • ctrl + d: 删除光标位置的字符.千万小心,当前行没有字符,直接退出终端
    • ctrl + w: 删除光标前的单词
    • ctrl + t: 光标位置字符与前一位交换
    • alt + t: 光标位置词与前一位词交换
    • alt + l: 光标位置到词尾转为小写
    • alt + u: 光标位置到词尾转为大写
  • 自动补全

    • Tab: 不说了,连恩两次,列出所有可能补全.
    • alt + tab: 从 bash_history 里提取补全,但是吧和多任务切换冲突了…
    • 其他用的就少了
  • 操作历史

    • 所有历史命令都保存在 ~/.bash_history 文件中.

    • 上下键切换,不多说了.

    • history: 显示 history 文件

    • history -c: 清理 history

    • ctrl + r: 会显示显示操作历史,键入命令可显示历史文件中查询的命令.常用.

    • 快捷键

      • ctrl + o: 执行当前条目,并显示下一条命令
    • !快捷键

      • !string: 相当于搜索历史,找出最近的以 string 开头的命令并执行.但只会匹配命令,没有参数什么事
      • !?string: 执行最近一条包含字符串string的命令
      • ^string1^string2: 执行最近一条包含string1的命令,将其替换成string2
      • !n: 执行 history 第 n 行的命令
      • !-n: 执行 history 末尾开始 -n 行命令
  • 其他快捷键

    • Ctrl + v: 下一个输入的特殊字符变成字面量..
    • Alt + .Alt + _: 非常重要,插入上一条命令的最后一个词.对于很长文件路径非常适用.

目录堆栈

  • 大意是有个堆栈可以把目录当作变量一样操作.进栈出栈.

  • cd -: 返回前一次进入的目录

  • pushd dir: 进入 dir 同上 dir 放入堆栈.

  • popd: 取栈顶目录,出栈并进入该目录.

  • popd -n: 仅删除栈顶目录,不改变当前 shell 路径.

  • pushd +3: 从栈顶起,第三个目录移动到栈顶.

  • pushd -3: 从栈底起,第三个目录移动到栈顶.

  • popd +3: 从栈顶起,删除第三个目录.

  • popd -3: 从栈底起,删除第三个目录.

  • dirs 命令

    • 显示目录堆栈
    • -c: 清空目录堆栈
    • -l: 用户主目录不显示波浪号前缀,而打印完整的目录(???)
    • -p: 打印目录堆栈.
    • -v: 打印目录堆栈带编号.
    • +N: 显示自栈顶起,第 N 个目录.
    • -N: 显示自栈底起,第 N 个目录.