0%

命令行的艺术

命令行的艺术 笔记

基础

  • man bash 学习 Bash 基础知识

  • man 阅读文档

  • apropos 查找文档

  • 有些命令并不对应可执行文件,而是在 Bash 内置好的,此时可以使用 helphelp -d 命令获取帮助信息

  • type 命令 来判断这个命令到底是可执行文件、shell 内置命令还是别名

  • >< 来重定向输出和输入

  • | 来重定向管道

  • > 会覆盖输出文件而 >> 是在文件末添加。了解标准输出 stdout 和标准错误 stderr

  • 通配符

    • * 代表任意数量的字符

    • ? 字符代表单个字符

    • [...] 匹配方括号之中的任意一个字符

    • [start-end] 表示一个连续的范围

    • [^...][!...] 表示匹配不在方括号里面的字符,也可以是连续范围 [!start-end]

    • {...} 表示匹配大括号里面的所有模式,模式之间使用逗号分隔

    • {start..end} 会匹配连续范围的字符

    • [] 需要匹配的字符集合。{} 代表一组表达式的与关系

    • {...}[...] 有一个很重要的区别。如果匹配的文件不存在,[...] 会失去模式的功能,变成一个单纯的字符串,而 {...} 依然可以展开

      $ ls [ab].txt
      ls: [ab].txt: No such file or directory
      
      $ ls {a,b}.txt
      ls: a.txt: No such file or directory
      ls: b.txt: No such file or directory
    • 通配符是先解释,再执行

    • 通配符不匹配,会原样输出,这条规则对 {...} 不适用

    • 上面所有通配符只匹配单层路径,不能跨目录匹配,如果要匹配子目录里面的文件 ls */*.txt

  • 引用意味着保护在引号中的字符串

    • "" 部分引用,允许解释变量名前缀 ($)、后引符 (`) 和 转义符 (\)

    • '' 全局引用,不允许解释变量引用,不会展开通配符

    • 变量引用会保留空白字符

      $ hello="A B  C   D"
      $ echo $hello
      A B C D
      $ echo "$hello"
      A B  C   D
    • '' 保护被引起字符串中的特殊字符

      $ ls
      first a.txt b.txt
      $ grep '[Ff]irst' *.txt
      a.txt:This is the first line of file1.txt.
      b.txt:This is the First line of file1.txt.
      
      # [Ff]irst 解释匹配到文件 first,因此等效于 grep first *.txt
      $ grep [Ff]irst *.txt
      a.txt:This is the first line of file1.txt.
      
      # 删除之后未匹配到,原样输出
      $ rm first
      $ grep [Ff]irst *.txt
      a.txt:This is the first line of file1.txt.
      b.txt:This is the First line of file1.txt.
  • Bash 中的任务管理工具:

    • & 后台执行
    • ctrl-z 挂起当前任务
    • ctrl-c 中断前台任务
    • jobs 列出所有后台任务
    • fg % 指定后台任务转到前台
    • bg 当前任务转到后台
    • kill % 杀掉指定后台任务
  • 基本网络管理工具:

    • ifconfignet-tools 套件提供的网络管理命令
    • ipiproute2 套件提供的网络管理命令,功能更强大,并旨在取代后
      $ sudo ip addr add 192.168.0.193/24 dev wlan0
      $ ip addr show wlan0
      $ ip route show
      $ ip neighbour # mac addr
      $ sudo ip link set ppp0 down
      $ sudo ip link set ppp0 up
    • dignslookup 用来解析域名,dig 可以得到更多的域名信息
  • grep / egrep 正则表达式

    • egrep = grep -E
    • -i –ignore-case
    • -o –only-matchin
    • -v –invert-match
    • -A –after-context
    • -B –before-context
    • -C –context
  • 学会基本的文件管理工具:lsls -l (了解 ls -l 中每一列代表的意义),lessheadtailtail -f (甚至 less +F),lnln -s (了解硬链接与软链接的区别),chownchmoddu (硬盘使用情况概述:du -hs \*)。 关于文件系统的管理,学习 dfmountfdiskmkfslsblk。知道 inode 是什么(与 ls -idf -i 等命令相关)。

日常使用

  • Bash 命令行

    • Tab 自动补全参数
    • ctrl-r 搜索命令行历史记录
      • 重复按下 ctrl-r 会向后查找匹配项
      • Enter 键会执行当前匹配的命令
      • 右方向键会将匹配项放入当前行中,不会直接执行,以便做出修改
  • Bash 命令行移动

    • ctrl-w 删除最后一个单词
    • ctrl-u 删除行内光标所在位置之前的内容
    • alt-balt-f 以单词为单位移动光标
    • ctrl-a 光标移至行首
    • ctrl-e 光标移至行尾
    • ctrl-k 删除光标至行尾的所有内容
    • ctrl-l 清屏
    • man readline 可以查看 Bash 中的默认快捷键。内容有很多,例如 alt-. 循环地移向前一个参数,而 alt-* 可以展开通配符
  • 按下 alt-# 在行首添加 # 把它当做注释再按下回车执行。之后借助命令行历史记录,你可以很方便恢复你刚才输入到一半的命令

  • xargs 将标准输入转为命令行参数

    • -L 指定多少行作为一个命令行参数
    • -P 最大并行数
    • -i-I 将命令行参数传给多个命令
      $ find . -name '*.py' | xargs grep some_function
      $ cat hosts | xargs -I{} ssh root@{} hostname
      $ find . -name "*.c" | xargs -i cp {} ~
      $ echo -e "a\nb\nc" | xargs -L 1 echo
      a
      b
      c
      $ echo -e "a\nb\nc" | xargs -L 2 echo
      a b
      c
  • parallel 在一台或多台计算机上并行的执行计算任务

  • pstree -p 以一种优雅的方式展示进程树

  • pgreppkill 根据名字查找进程或发送信号(-f 参数通常有用)

  • kill 发送信号给进程

    • kill -l 所有信号种类
    • man 7 signal 查看详细列表
    • kill -STOP [pid] 发送 STOP 信号到进程以停止进程
  • 使一个后台进程持续运行

    • disown 是bash的内置命令,将指定任务从 jobs 列表之中移除
      # 移出最近一个正在执行的后台任务
      $ disown
      # 移出所有正在执行的后台任务
      $ disown -r
      # 移出所有后台任务
      $ disown -a
      # 不移出后台任务,但是让它们不会收到SIGHUP信号
      $ disown -h
      # 根据jobId,移出指定的后台任务
      $ disown %2
      $ disown -h %2
    • nohup 命令实际上将子进程与它所在的 session 分离,不会自动把进程变为后台任务,所以必须加上 & 符号
      $ nohup node server.js &
  • 检查端口

    • netstat -lntpss -plat 检查哪些进程在监听端口(默认是检查 TCP 端口; 添加参数 -u 则检查 UDP 端口)
    • lsof -iTCP -sTCP:LISTEN -P -n
  • lsof 来查看开启的套接字和文件

  • uptimew 来查看系统已经运行多长时间

  • alias 来创建常用命令的快捷形式。例如:alias ll='ls -latr' 创建了一个新的命令别名 ll

  • 可以把别名、shell 选项和常用函数保存在 ~/.bashrc,具体看下这篇文章。这样做的话你就可以在所有 shell 会话中使用你的设定

  • 把环境变量的设定以及登陆时要执行的命令保存在 ~/.bash_profile。而对于从图形界面启动的 shell 和 cron 启动的 shell,则需要单独配置文件

  • 要想在几台电脑中同步你的配置文件(例如 .bashrc.bash_profile),可以借助 Git 建立 repository

  • 变量和文件名中包含空格处理时要格外小心

    • Bash 变量要用引号括起来,比如 "$FOO" 部分引用
    • 使用 -0-print0 选项以便用 NULL 代替 来分隔文件名
      $ locate -0 pattern | xargs -0 ls -al
      $ find / -print0 -type d | xargs -0 ls -al
    • for 循环中循环访问的文件名含有空字符(空格、tab 等字符),只需用 IFS=$'\n' 把内部字段分隔符设为换行符
  • Bash set 使用

    • set -x 输出执行的那行命令
    • set -v
    • set -e 发生错误时退出而不是继续运行
    • set -u 来检查是否使用了未赋值的变量
    • set -o pipefail,它可以监测管道中的错误
    • 当牵扯到很多脚本时,使用 trap 来检测 ERR 和 EXIT。一个好的习惯是在脚本文件开头这样写,这会使它能够检测一些错误,并在错误发生时中断程序并输出信息:
      set -euxo pipefail
      trap "echo 'error: Script failed: see failed command above'" ERR
  • Bash subshell

    • subshell 调用方式
      • 运行另一个 shell 脚本
      • 嵌在 () 里的一列命令在一个子 shell 里运行
      • 管道产生子shell
    • 子 shell 中的目录更改不会影响到父 shell
    • 子 shell 里的变量实际为局部变量
    • 子 shell 可用于为一组命令设定临时的环境变量
    • 子 shell(使用括号 (...))是一种组织参数的便捷方式。一个常见的例子是临时地移动工作路径
      # do something in current dir
      (cd /some/other/dir && other-command)
      # continue in original dir
  • Bash 变量扩展

    • $# 参数个数
    • $0 - $9 位置参数
    • ${name:?error message} 用于检查变量是否存在
    • input_file=${1:?usage: $0 input_file} 只需要一个参数
    • ${name:-default} 变量为空时使用默认值
    • output_file=${2:-logfile} 再加一个(可选的)参数,默认值为 logfile
    • 数学运算符 ((...)),例如 i=$(( (i + 1) % 5 ))
    • 序列 {1..10},例如 for i in {1..5}; do echo $i; done
    • 模式匹配截断字符串:${var%suffix}${var#prefix}。例如,假设 var=foo.pdf,那么 echo ${var%.pdf}.txt 将输出 foo.txt
    • ${PARAMETER/PATTERN/STRING} 替换字符串
  • 括号扩展({})来减少输入相似文本,并自动化文本组合

      # mv foo.txt foo.pdf ../
      $ mv foo.{txt.pdf} ../
      # cp foo foo.bak
      $ cp foo{,.bak}
      # mkdir -p test-a/subtest-{1,2,3} test-b/subtest-{1,2,3} test-c/subtest-{1,2,3}
      $ mkdir -p test-{a,b,c}/subtest-{1,2,3}
  • <(some command) 可以将输出视为文件。例如,对比本地文件 /etc/hosts 和一个远程文件:

      diff /etc/hosts <(ssh somehost cat /etc/hosts)
  • 编写脚本时,把代码都放在大括号里可以防止下载不完全代码被执行

      {
          # 在这里写代码
      }
  • Here Documents 是一种定义字符串的方法,可以保存文字里面的换行或是缩排等空白字元

    cat <<End-of-message
    -------------------------------------
    This is line 1 of the message.
    This is line 2 of the message.
    This is the last line of the message.
    -------------------------------------
    End-of-message
  • 同时重定向标准输出和标准错误

    • > 重定向标准输出
    • < 重定向标准输入
    • /dev/null 特殊空设备
      • > /dev/null 丢弃输出
      • < /dev/null 发送 EOF 给程序并立刻断开输入
      • 为了保证命令不会在标准输入里残留一个未关闭的文件句柄捆绑在你当前所在的终端上,在命令后添加 < /dev/null
    • There are two formats for redirecting standard output and standard error:
      • &>word 推荐使用
      • >&word
      • 等效于 >word 2>&1
        $ ls -l &>logfile </dev/null
        $ ls -l >logfile 2>&1 </dev/null
  • 查看编码信息

    • man ascii 查看具有十六进制和十进制值的ASCII表
    • man unicodeman utf-8,以及 man latin1 通用编码信息
  • 使用 screentmux 来使用多份屏幕,当你在使用 ssh 时(保存 session 信息)将尤为有用。而 byobu 可以为它们提供更多的信息和易用的管理工具。另一个轻量级的 session 持久化解决方案是 dtach

  • ssh 中,了解如何使用 -L-D(偶尔需要用 -R)开启隧道是非常有用的,比如当你需要从一台远程服务器上访问 web 页面。

  • 对 ssh 设置做一些小优化可能是很有用的,例如这个 ~/.ssh/config 文件包含了防止特定网络环境下连接断开、压缩数据、多通道等选项:

        TCPKeepAlive=yes
        ServerAliveInterval=15
        ServerAliveCountMax=6
        Compression=yes
        ControlMaster auto
        ControlPath /tmp/%r@%h:%p
        ControlPersist yes
  • 一些其他的关于 ssh 的选项是与安全相关的,应当小心翼翼的使用。例如你应当只能在可信任的网络中启用 StrictHostKeyChecking=noForwardAgent=yes

  • 考虑使用 mosh 作为 ssh 的替代品,它使用 UDP 协议。它可以避免连接被中断并且对带宽需求更小,但它需要在服务端做相应的配置。

  • 获取八进制形式的文件访问权限(修改系统设置时通常需要,但 ls 的功能不那么好用并且通常会搞砸),可以使用类似如下的代码:

        stat -c '%A %a %n' /etc/timezone
  • 使用 percol 或者 fzf 可以交互式地从另一个命令输出中选取值。

  • 使用 fppPathPicker)可以与基于另一个命令(例如 git)输出的文件交互。

  • 将 web 服务器上当前目录下所有的文件(以及子目录)暴露给你所处网络的所有用户,使用:
    python -m SimpleHTTPServer 7777 (使用端口 7777 和 Python 2)或python -m http.server 7777 (使用端口 7777 和 Python 3)。

  • 以其他用户的身份执行命令,使用 sudo。默认以 root 用户的身份执行;使用 -u 来指定其他用户。使用 -i 来以该用户登录(需要输入_你自己的_密码)。

  • 将 shell 切换为其他用户,使用 su username 或者 sudo - username。加入 - 会使得切换后的环境与使用该用户登录后的环境相同。省略用户名则默认为 root。切换到哪个用户,就需要输入_哪个用户的_密码。

  • 了解命令行的 128K 限制。使用通配符匹配大量文件名时,常会遇到“Argument list too long”的错误信息。(这种情况下换用 findxargs 通常可以解决。)

  • 当你需要一个基本的计算器时,可以使用 python 解释器(当然你要用 python 的时候也是这样)。例如:

    >>> 2+3
    5

文件及数据处理

  • 通过文件名查找文件

    • 当前目录 find . -iname '*something*'
    • 所有路径 locate something (但注意到 updatedb 可能没有对最近新建的文件建立索引,所以你可能无法定位到这些未被索引的文件)
  • 检索源码 ag rg

  • Markdown,HTML,以及所有文档格式之间的转换,pandoc

  • 使用 jq 处理 JSON

  • 将 HTML 转为文本:lynx -dump -stdin

  • 当你要处理棘手的 XML 时候,xmlstarlet 算是上古时代流传下来的神器。

  • 使用 shyaml 处理 YAML。

  • 要处理 Excel 或 CSV 文件的话,csvkit 提供了 in2csvcsvcutcsvjoincsvgrep 等方便易用的工具。

  • 当你要处理 Amazon S3 相关的工作的时候,s3cmd 是一个很方便的工具而 s4cmd 的效率更高。Amazon 官方提供的 aws 以及 saws 是其他 AWS 相关工作的基础,值得学习。

  • 了解如何使用 sortuniq,包括 uniq 的 -u 参数和 -d 参数,具体内容在后文单行脚本节中。另外可以了解一下 comm

  • 了解如何使用 cutpastejoin 来更改文件。很多人都会使用 cut,但遗忘了 join

  • 了解如何运用 wc 去计算新行数(-l),字符数(-m),单词数(-w)以及字节数(-c)。

  • 了解如何使用 tee 将标准输入复制到文件甚至标准输出,例如 ls -al | tee file.txt

  • 要进行一些复杂的计算,比如分组、逆序和一些其他的统计分析,可以考虑使用 datamash

  • 注意到语言设置(中文或英文等)对许多命令行工具有一些微妙的影响,比如排序的顺序和性能。大多数 Linux 的安装过程会将 LANG 或其他有关的变量设置为符合本地的设置。要意识到当你改变语言设置时,排序的结果可能会改变。明白国际化可能会使 sort 或其他命令运行效率下降许多倍。某些情况下(例如集合运算)你可以放心的使用 export LC_ALL=C 来忽略掉国际化并按照字节来判断顺序。

  • 你可以单独指定某一条命令的环境,只需在调用时把环境变量设定放在命令的前面,例如 TZ=Pacific/Fiji date 可以获取斐济的时间。

  • 了解如何使用 awksed 来进行简单的数据处理。 参阅 One-liners 获取示例。

  • 替换一个或多个文件中出现的字符串:

        perl -pi.bak -e 's/old-string/new-string/g' my-files-*.txt
  • 使用 repren 来批量重命名文件,或是在多个文件中搜索替换内容。(有些时候 rename 命令也可以批量重命名,但要注意,它在不同 Linux 发行版中的功能并不完全一样。)

        # 将文件、目录和内容全部重命名 foo -> bar:
        repren --full --preserve-case --from foo --to bar .
        # 还原所有备份文件 whatever.bak -> whatever:
        repren --renames --from '(.*)\.bak' --to '\1' *.bak
        # 用 rename 实现上述功能(若可用):
        rename 's/\.bak$//' *.bak
  • 根据 man 页面的描述,rsync 是一个快速且非常灵活的文件复制工具。它闻名于设备之间的文件同步,但其实它在本地情况下也同样有用。在安全设置允许下,用 rsync 代替 scp 可以实现文件续传,而不用重新从头开始。它同时也是删除大量文件的最快方法之一:

    mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir
  • 若要在复制文件时获取当前进度,可使用 pvpycpprogressrsync --progress。若所执行的复制为block块拷贝,可以使用 dd status=progress

  • 使用 shuf 可以以行为单位来打乱文件的内容或从一个文件中随机选取多行。

  • 了解 sort 的参数。显示数字时,使用 -n 或者 -h 来显示更易读的数(例如 du -h 的输出)。明白排序时关键字的工作原理(-t-k)。例如,注意到你需要 -k1,1 来仅按第一个域来排序,而 -k1 意味着按整行排序。稳定排序(sort -s)在某些情况下很有用。例如,以第二个域为主关键字,第一个域为次关键字进行排序,你可以使用 sort -k1,1 | sort -s -k2,2

  • 如果你想在 Bash 命令行中写 tab 制表符,按下 ctrl-v [Tab] 或键入 $'\t' (后者可能更好,因为你可以复制粘贴它)。

  • 标准的源代码对比及合并工具是 diffpatch。使用 diffstat 查看变更总览数据。注意到 diff -r 对整个文件夹有效。使用 diff -r tree1 tree2 | diffstat 查看变更的统计数据。vimdiff 用于比对并编辑文件。

  • 对于二进制文件,使用 hdhexdump 或者 xxd 使其以十六进制显示,使用 bvihexedit 或者 biew 来进行二进制编辑。

  • 同样对于二进制文件,strings(包括 grep 等工具)可以帮助在二进制文件中查找特定比特。

  • 制作二进制差分文件(Delta 压缩),使用 xdelta3

  • 使用 iconv 更改文本编码。需要更高级的功能,可以使用 uconv,它支持一些高级的 Unicode 功能。例如,这条命令移除了所有重音符号:

        uconv -f utf-8 -t utf-8 -x '::Any-Lower; ::Any-NFD; [:Nonspacing Mark:] >; ::Any-NFC; ' < input.txt > output.txt
  • 拆分文件可以使用 split(按大小拆分)和 csplit(按模式拆分)。

  • 操作日期和时间表达式,可以用 dateutils 中的 dateadddatediffstrptime 等工具。

  • 使用 zlesszmorezcatzgrep 对压缩过的文件进行操作。

  • 文件属性可以通过 chattr 进行设置,它比文件权限更加底层。例如,为了保护文件不被意外删除,可以使用不可修改标记:sudo chattr +i /critical/directory/or/file

  • 使用 getfaclsetfacl 以保存和恢复文件权限。例如:

     getfacl -R /some/path > permissions.txt
     setfacl --restore=permissions.txt
  • 为了高效地创建空文件,请使用 truncate(创建稀疏文件),fallocate(用于 ext4,xfs,btrf 和 ocfs2 文件系统),xfs_mkfile(适用于几乎所有的文件系统,包含在 xfsprogs 包中),mkfile(用于类 Unix 操作系统,比如 Solaris 和 Mac OS)。

## 系统调试

单行脚本

冷门但有用

更多资源

  • expr:计算表达式或正则匹配

  • m4:简单的宏处理器

  • yes:多次打印字符串

  • cal:漂亮的日历

  • env:执行一个命令(脚本文件中很有用)

  • printenv:打印环境变量(调试时或在写脚本文件时很有用)

  • look:查找以特定字符串开头的单词或行

  • cutpastejoin:数据修改

  • fmt:格式化文本段落

  • pr:将文本格式化成页/列形式

  • fold:包裹文本中的几行

  • column:将文本格式化成多个对齐、定宽的列或表格

  • expandunexpand:制表符与空格之间转换

  • nl:添加行号

  • seq:打印数字

  • bc:计算器

  • factor:分解因数

  • gpg:加密并签名文件

  • toe:terminfo 入口列表

  • nc:网络调试及数据传输

  • socat:套接字代理,与 netcat 类似

  • slurm:网络流量可视化

  • dd:文件或设备间传输数据

  • file:确定文件类型

  • tree:以树的形式显示路径和文件,类似于递归的 ls

  • stat:文件信息

  • time:执行命令,并计算执行时间

  • timeout:在指定时长范围内执行命令,并在规定时间结束后停止进程

  • lockfile:使文件只能通过 rm -f 移除

  • logrotate: 切换、压缩以及发送日志文件

  • watch:重复运行同一个命令,展示结果并/或高亮有更改的部分

  • when-changed:当检测到文件更改时执行指定命令。参阅 inotifywaitentr

  • tac:反向输出文件

  • shuf:文件中随机选取几行

  • comm:一行一行的比较排序过的文件

  • strings:从二进制文件中抽取文本

  • tr:转换字母

  • iconvuconv:文本编码转换

  • splitcsplit:分割文件

  • sponge:在写入前读取所有输入,在读取文件后再向同一文件写入时比较有用,例如 grep -v something some-file | sponge some-file

  • units:将一种计量单位转换为另一种等效的计量单位(参阅 /usr/share/units/definitions.units

  • apg:随机生成密码

  • xz:高比例的文件压缩

  • ldd:动态库信息

  • nm:提取 obj 文件中的符号

  • abwrk:web 服务器性能分析

  • strace:调试系统调用

  • mtr:更好的网络调试跟踪工具

  • cssh:可视化的并发 shell

  • rsync:通过 ssh 或本地文件系统同步文件和文件夹

  • wiresharktshark:抓包和网络调试工具

  • ngrep:网络层的 grep

  • hostdig:DNS 查找

  • lsof:列出当前系统打开文件的工具以及查看端口信息

  • dstat:系统状态查看

  • glances:高层次的多子系统总览

  • iostat:硬盘使用状态

  • mpstat: CPU 使用状态

  • vmstat: 内存使用状态

  • htop:top 的加强版

  • last:登入记录

  • w:查看处于登录状态的用户

  • id:用户/组 ID 信息

  • sar:系统历史数据

  • iftopnethogs:套接字及进程的网络利用情况

  • ss:套接字数据

  • dmesg:引导及系统错误信息

  • sysctl: 在内核运行时动态地查看和修改内核的运行参数

  • hdparm:SATA/ATA 磁盘更改及性能分析

  • lsblk:列出块设备信息:以树形展示你的磁盘以及磁盘分区信息

  • lshwlscpulspcilsusbdmidecode:查看硬件信息,包括 CPU、BIOS、RAID、显卡、USB设备等

  • lsmodmodinfo:列出内核模块,并显示其细节

  • fortuneddatesl:额,这主要取决于你是否认为蒸汽火车和莫名其妙的名人名言是否“有用”

Ref

  1. 命令行通配符教程
  2. 引用
  3. Linux 守护进程的启动方法
  4. xargs 命令教程
  5. Bash 脚本 set 命令教程
  6. Bash 参数和参数扩展
  7. the usage of < /dev/null & in the command line
  8. What does &> do in bash?