The Missing Semester of Your CS Education 阅读笔记.


1. The Shell

Shell: 允许你执行程序,输入并获取某种半结构化的输出。

Bash Reference Manual Advanced Bash-Scripting Guide Bash Guide

1
kumiko:~$
  • kumiko:主机名
  • ~:当前工作目录
  • $:表示您现在的身份不是 root 用户

1.1. 在程序间创建连接

在 shell 中,程序有两个主要的“流”:输入流和输出流。通常,一个程序的输入输出流都是终端(键盘作为输入,显示器作为输出);但是,我们也可以重定向这些流。

  • < file & > file: 将程序的输入输出流分别重定向至文件
  • >>:向一个文件追加内容
  • |:将一个程序的输出和另外一个程序的输入连接起来

值得注意的是,|>< 是通过 shell 执行的,而不是被各个程序单独执行。因此会有以下情况发生

1
2
3
4
5
6
7
# error
sudo echo 3 > brightness
An error occurred while redirecting file 'brightness'
open: Permission denied

# ok
echo 3 | sudo tee brightness

1.2. shell 脚本 (bash)

可以使用 shellcheck 定位 shell 脚本中的错误。

在 shell 脚本中使用空格以分割参数:

  • foo=bar:变量 foo 赋值为 bar
  • foo = bar:调用程序 foo 并将 =bar 作为参数

Bash 中的字符串通过 '" 分隔符来定义:

  • ' 定义的字符串为原义字符串,其中的变量不会被转义
  • " 定义的字符串会将变量值进行替换
1
2
3
4
5
6
7
foo=bar

# bar
echo "$foo"

# $foo
echo '$foo'

函数

1
2
3
4
mcd () {
  mkdir -p "$1"
  cd "$1"
}
  • $0: 脚本名
  • $1-$9: 脚本的参数列表
  • $@:所有参数
  • $#:参数个数
  • $?:前一个命令的返回值
  • $$:当前脚本的进程识别码
  • !!:完整的上一条命令,包括参数

    当你因为权限不足执行命令失败时,可以使用 sudo !! 再尝试一次

  • $_:上一条命令的最后一个参数
    • 如果你正在使用的是交互式 shell,可以通过按下 Esc 之后键入 . 来获取这个值

命令输出

1
2
3
4
5
6
7
8
# 黄前久美子世界第一!
false || echo "黄前久美子世界第一!"

# nothing
false && echo "黄前久美子世界第一!"

# 黄前久美子世界第一!
false ; echo "黄前久美子世界第一!"
  • 命令通常使用 STDOUT 来返回输出值,使用 STDERR 来返回错误及错误码
  • 返回值 0 表示正常执行,其他所有非 0 的返回值都表示有错误发生
  • 退出码可以搭配 &&|| 使用,用来进行条件判断,决定是否执行其他程序
  • 程序 true 的返回码永远是 0,false 的返回码永远是 1
  • 同一行的多个命令可以用 ; 分隔

Command/Process Substitution

1
2
3
4
5
# date 会被替换成日期和时间
echo "Starting program at $(date)"

# 显示文件夹 foo 和 bar 中文件的区别
diff <(ls foo) <(ls bar)
  • Command Substitution $(cmd):执行 cmd,并使用输出结果替换 $(cmd)
  • Porcess Substitution <(cmd):执行 cmd,将结果输出到一个临时文件中,并将 <(cmd) 替换成临时文件名

比较

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#!/bin/bash

for file in $@; do
    grep foobar $file > /dev/null 2> /dev/null
    # 如果模式没有找到,则grep退出状态为 1
    # 我们将标准输出流和标准错误流重定向到Null,因为我们并不关心这些信息
    if [[ $? -ne 0 ]]; then
        echo "File $file does not have any foobar, adding one"
        echo "# foobar" >> "$file"
    fi
done
  • 在 bash 中进行比较时,尽量使用双方括号 [[ ]] 而不是单方括号 [ ],这样会降低犯错的几率,尽管这样并不能兼容 sh
  • 可通过 man test 查看 Bash 中的 test 手册,下面给出常见的比较操作说明:
    • 布尔运算

      1
      2
      3
      4
      
      ( expression )      # 返回表达式值,输入注意括号前反斜杆
      exp1 -a exp2        # 与
      exp1 -o exp2        # 或
      ! expression        # 非
      
    • 字符串判断

      1
      2
      3
      4
      
      -n str1             # 字符串不为空(长度不为零),等同于 str1
      -z str1             # 字符串为空(长度等于零)
      str1 = str2         # 相等
      str1 != str2        # 不等
      
    • 整型比较

      1
      2
      3
      4
      5
      6
      
      num1 -eq num2       # == 
      num1 -ne num2       # !=
      num1 -lt num2       # <
      num1 -le num2       # <=
      num1 -gt num2       # >
      num1 -ge num2       # >=
      
    • 文件判断

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      
      -e file             # file exists
      -d file             # file exists and is a directory
      -f file             # file exists and is a regular file
      -h file             # file exists and is a symbolic link
      -L file             # same as -h file
      -S file             # file exists and is a socket
      
      file1 -ef file2     # file1 and file2 have the same device and inode numbers
      file1 -nt file2     # file1 is newer(modification date) than file2
      file1 -ot file2     # file1 is older than file2
      

通配(globbing)

  • 通配符

    • ? 匹配一个字符
    • * 匹配任意个字符
  • 花括号 {}:当你有一系列的指令,其中包含一段公共子串时,可以用花括号来自动展开这些命令

    1
    2
    3
    
    convert image.{png,jpg}
    # 会展开为
    convert image.png image.jpg
    

1.3. Shell 工具

查看命令如何使用

  • 命令行输入

    1
    2
    3
    
    cmd_name -h
    cmd_name --help
    man cmd_name
    
  • man 获得的使用手册太过详实,推荐使用 TLDR pages

查找文件

locate vs find

查找文件内容

查找 shell 历史命令

  • history 命令允许您以程序员的方式来访问 shell 中输入的历史命令
  • 可以使用 Ctrl+R 对命令历史记录进行回溯搜索
  • 输入命令时,如果您在命令的开头加上一个空格,它就不会被加进 shell 记录中。当你输入包含密码或是其他敏感信息的命令时会用到这一特性。如果你不小心忘了在前面加空格,可以通过编辑 bash_history.zhistory 来手动地从历史记录中移除那一项

目录切换

  • 使用 fasd 可以查找最常用和/或最近使用的文件和目录
  • 也可以使用 autojump