Basic Linux CLI

命令的基本组成

命令 子命令 [选项] [参数] [重定向] [控制符或管道]
Command Subcommand [Options] [Arguments] [Redirection] [Control Characters or Pipes]
  • 这里面除了命令是必须的, 其它的都是可选的 (就像 Python 的函数参数一样, 不写的话会有默认值!).

重要例子

git submodule update --init
  • submoduleupdate 是子命令
  • --init 是选项, 参数默认是更新所有模块. (也可以加参数: git submodule update --init --recursive third_party/python/pythondata_cpu_serv.)
# 如果系统中找不到 wget 命令,就把 wget 加入 missing 数组
missing=()

if ! which wget >/dev/null; then
    missing+=(wget)
fi
  • >: 重定向, 表示把标准输出 (即命令正常的输出) 写到某个地方.
  • /dev/null: 黑洞设备, 任何写入它的数据都会被系统丢弃.
  • 如果 which wget >/dev/null 没成功, 则执行 then 后面的语句 (即只关心是否存在 wget 命令, 不关心它的输出).
grep "apt" ./scripts/setup
  • ./scripts/setup 文件中查找包含 "apt" 字符串.
in_ci=0
if [ $# -gt 0 ] ; then
  if [ $1 == "-ci" ] ; then
    in_ci=1
  fi
fi
  • $#: 表示传递给脚本的参数个数.
  • gt: 表示大于 (greater than).
  • $1: 表示第一个参数.
cat file.txt # 打印文件内容
cat a.txt b.txt > merged.txt # 合并多个文件内容到一个新文件
mkdir -p /path/to/directory # 创建目录, 如果上级目录不存在则创建
# 假设 env.sh 有这个内容: MY_VAR="hello"
bash env.sh
echo "$MY_VAR"   # 空的
source env.sh    # 会在当前 shell 里保留变量
echo "$MY_VAR"   # 输出 hello

常见选项

  • -C: Change, 切换到指定目录.
  • -h--help: 显示帮助信息
  • -v--version: 显示版本信息
  • -l--long: 显示详细信息
  • -a--all: 显示所有内容,包括隐藏文件
  • -r--recursive: 递归操作
  • -d--directory: 仅显示目录 / 是否存在目录
  • -f:
    • --force, 强制执行操作
    • filename, 文件名
  • set -e: 如果有错误就退出 (exit) 脚本
  • -p--parents: 创建目录时如果不存在则会创建.

I/O 模型

输出/输出/错误

每个命令启动时, 系统会为它创建 3 个文件描述符 (file descriptor, FD):

FD 0  → 标准输入  (stdin)   默认为键盘
FD 1  → 标准输出  (stdout)  默认为终端
FD 2  → 标准错误  (stderr)  默认为终端

重定向 (Redirection)

  • 1>: 重定向 FD1, 将指令成功执行的结果输出到指定文件 (覆盖原文件).

    • 可以省略 1, 直接用 >.
    • 错误信息不会被写进文件, 仍然显示在终端.
    echo hello > out1.txt
    out1.txt
    hello
  • 2>: 重定向 FD2, 将指令执行时的错误信息输出到指定文件 (覆盖原文件).

    ls nonexistent_file 2> err1.txt
    err1.txt
    ls: cannot access 'nonexistent_file': No such file or directory
    • FD1, FD2 分开存:

      ls nonexistent_file > out2.txt 2> err2.txt
      out2.txt
      err2.txt
      ls: cannot access 'nonexistent_file': No such file or directory
    • FD2 当作 FD1 存:

      ls nonexistent_file > all.txt 2>&1
      all.txt
      ls: cannot access 'nonexistent_file': No such file or directory
  • >>: 追加 FD1

    echo hello >> out3.txt
    echo world >> out3.txt
    out3.txt
    hello
    world

管道 (Pipes)

Shell 预处理

正则表达式 (Regular Expressions, regex)

  • *: 匹配零个或多个字符
  • ?: 匹配单个字符
  • [abc]: 匹配方括号内的任意一个字符 (a, b, 或 c)
  • [a-z]: 匹配指定范围内的任意一个字符 (小写字母 a 到 z)
  • [^abc]: 匹配不在方括号内的任意一个字符 (不是 a, b, 或 c)
  • ^: 匹配行的开头
  • $: 匹配行的结尾
  • \: 转义字符, 用于匹配特殊字符本身 (如 \*, \?, \\)

重定向

不改变命令本身, 而是改变命令默认的输入输出方式.

Shell 脚本

TODO

其它

Command Substitution 命令替换

echo "Today is $(date)"
  • date 本身就是命令! 执行的时候将它输出的结果作为字符串替换掉 (placeholder 的感觉).
  • 还有一些经常用在命令替换的命令:
# dirname 纯纯基于字符串 (不管路径是否存在)
dirname /home/user/file.txt   → /home/user
dirname /home/user/           → /home
dirname foo/bar               → foo
dirname foo                   → .
dirname this/path/not/exist   → this/path/not
pwd # 当前工作目录
realpath # 显示当前绝对路径

常用命令

df -h # 查看磁盘使用情况
poweroff # 关机
ls -a # 列出当前目录下的所有文件和目录 (包括隐藏文件)
  • aptapt-get: 前者面向日常用户, 更简洁; 后者是更底层的传统工具, 适合脚本和精确控制.

  • 查看 manual:

    # -A 3: 显示匹配行之后的 3 行
    # -B 1: 显示匹配行之前的 1 行
    man cc | grep -A 3 -B 1 " -g "

Vim Cheatsheet

光标移动