diff --git a/README-zh.md b/README-zh.md index 79ebf77..78a4c5d 100644 --- a/README-zh.md +++ b/README-zh.md @@ -35,63 +35,63 @@ 涵盖范围: -- 这篇文章对刚接触命令行的新手,以及具有命令行使用经验的人都有用处。本文致力于做到*覆盖面广*(尽量包括一切重要的内容),*具体*(给出最常见的具体的例子),以及*简洁*(避免不必要的,或是可以在其他地方轻松查到的细枝末节)。每个技巧在特定情境下或是基本的,或是能显著节约时间。 -- 本文为 Linux 所写,除了[仅限 OS X 系统](#仅限-os-x-系统)和[仅限 Windows 系统](#仅限-windows-系统)的部分。其它节中的大部分内容都适用于其它 Unix 系统或 OS X,甚至 Cygwin。 -- 本文关注于交互式 Bash,尽管很多技巧也适用于其他 shell 或 Bash 脚本。 -- 本文包括了“标准的”Unix 命令和需要安装特定包的命令,只要它们足够重要。 +- 这篇文章不仅能帮助刚接触命令行的新手,而且对具有经验的人也大有裨益。本文致力于做到*覆盖面广*(涉及所有重要的内容),*具体*(给出具体的最常用的例子),以及*简洁*(避免冗余的内容,或是可以在其他地方轻松查到的细枝末节)。在特定应用场景下,本文的内容属于基本功或者能帮助您节约大量的时间。 +- 本文主要为 Linux 所写,但在[仅限 OS X 系统](#仅限-os-x-系统)章节和[仅限 Windows 系统](#仅限-windows-系统)章节中也包含有对应操作系统的内容。除去这两个章节外,其它的内容大部分均可在其他类 Unix 系统或 OS X,甚至 Cygwin 中得到应用。 +- 本文主要关注于交互式 Bash,但也有很多技巧可以应用于其他 shell 和 Bash 脚本当中。 +- 除去“标准的”Unix 命令,本文还包括了一些依赖于特定软件包的命令(前提是它们具有足够的价值)。 注意事项: -- 为了能在一页内展示尽量多的东西,一些具体的信息会被间接地包含在引用页里。聪明机智的你,如果掌握了使用 Google 搜索引擎的基本思路与命令,那么你将可以查阅到更多的详细信息。使用 `apt-get`,`yum`,`dnf`,`pacman`, -`pip` 或 `brew`(以及其它合适的包管理器)来安装新程序。 -- 使用 [Explainshell](http://explainshell.com/) 去获取相关命令、参数、管道等内容的解释。 +- 为了能在一页内展示尽量多的东西,一些具体的信息可以在引用的页面中找到。我们相信机智的你知道如何使用 Google 或者其他搜索引擎来查阅到更多的详细信息。文中部分命令需要您使用 `apt-get`,`yum`,`dnf`,`pacman`, +`pip` 或 `brew`(以及其它合适的包管理器)来安装依赖的程序。 +- 遇到问题的话,请尝试使用 [Explainshell](http://explainshell.com/) 去获取相关命令、参数、管道等内容的解释。 ## 基础 -- 学习 Bash 的基础知识。具体来说,输入 `man bash` 并至少全文浏览一遍; 它很简单并且不长。其他的 shell 可能很好用,但 Bash 功能强大到几乎所有情况下都是可用的 ( *只*学习 zsh,fish 或其他的 shell 的话,在你自己的电脑上会显得很方便,但在很多情况下会限制你,比如当你需要在服务器上工作时)。 +- 学习 Bash 的基础知识。具体地,在命令行中输入 `man bash` 并至少全文浏览一遍; 它理解起来很简单并且不冗长。其他的 shell 可能很好用,但 Bash 的功能已经足够强大并且到几乎总是可用的( 如果你*只*学习 zsh,fish 或其他的 shell 的话,在你自己的设备上会显得很方便,但过度依赖这些功能会给您带来不便,例如当你需要在服务器上工作时)。 -- 学习并掌握至少一个基于文本的编辑器。通常 Vim (`vi`) 会是你最好的选择,因为在终端里进行随机编辑,Vim 真的毫无敌手,哪怕是 Emacs、某大型 IDE 甚至时下非常流行的编辑器。 +- 熟悉至少一个基于文本的编辑器。通常而言 Vim (`vi`) 会是你最好的选择,毕竟在终端中编辑文本时 Vim 是最好用的工具(甚至大部分情况下 Vim 要比 Emacs、大型 IDE 或是炫酷的编辑器更好用)。 -- 学会如何使用 `man` 命令去阅读文档。学会使用 `apropos` 去查找文档。了解有些命令并不对应可执行文件,而是Bash内置的,可以使用 `help` 和 `help -d` 命令获取帮助信息。你可以用 `type 命令` 来判断它到底是可执行文件、shell 内置命令、还是别名。 +- 学会如何使用 `man` 命令去阅读文档。学会使用 `apropos` 去查找文档。知道有些命令并不对应可执行文件,而是在 Bash 内置好的,此时可以使用 `help` 和 `help -d` 命令获取帮助信息。你可以用 `type 命令` 来判断这个命令到底是可执行文件、shell 内置命令还是别名。 - 学会使用 `>` 和 `<` 来重定向输出和输入,学会使用 `|` 来重定向管道。明白 `>` 会覆盖了输出文件而 `>>` 是在文件末添加。了解标准输出 stdout 和标准错误 stderr。 -- 学会使用通配符 `*` (或许再算上 `?` 和 `[`...`]`) 和引用以及引用中 `'` 和 `"` 的区别。 +- 学会使用通配符 `*` (或许再算上 `?` 和 `[`...`]`) 和引用以及引用中 `'` 和 `"` 的区别(后文中有一些具体的例子)。 -- 熟悉 Bash 任务管理工具:`&`,**ctrl-z**,**ctrl-c**,`jobs`,`fg`,`bg`,`kill` 等。 +- 熟悉 Bash 中的任务管理工具:`&`,**ctrl-z**,**ctrl-c**,`jobs`,`fg`,`bg`,`kill` 等。 -- 了解 `ssh`,以及学会通过使用 `ssh-agent`,`ssh-add` 等命令来实现基本的无密码认证。 +- 学会使用 `ssh` 进行远程命令行登录,最好知道如何使用 `ssh-agent`,`ssh-add` 等命令来实现基础的无密码认证登录。 -- 学会基本的文件管理:`ls` 和 `ls -l` (了解 `ls -l` 中每一列代表的意义),`less`,`head`,`tail` 和 `tail -f` (甚至 `less +F`),`ln` 和 `ln -s` (了解硬链接与软链接的区别),`chown`,`chmod`,`du` (硬盘使用情况概述:`du -hs *`)。 关于文件系统的管理,学习 `df`,`mount`,`fdisk`,`mkfs`,`lsblk`。知道 inode 是什么(与 `ls -i` 和 `df -i` 等命令相关)。 +- 学会基本的文件管理工具:`ls` 和 `ls -l` (了解 `ls -l` 中每一列代表的意义),`less`,`head`,`tail` 和 `tail -f` (甚至 `less +F`),`ln` 和 `ln -s` (了解硬链接与软链接的区别),`chown`,`chmod`,`du` (硬盘使用情况概述:`du -hs *`)。 关于文件系统的管理,学习 `df`,`mount`,`fdisk`,`mkfs`,`lsblk`。知道 inode 是什么(与 `ls -i` 和 `df -i` 等命令相关)。 -- 学习基本的网络管理:`ip` 或 `ifconfig`,`dig`。 +- 学习基本的网络管理工具:`ip` 或 `ifconfig`,`dig`。 - 学习并使用一种版本控制管理系统,例如 `git`。 -- 熟悉正则表达式,以及 `grep`/`egrep` 里不同参数的作用,例如 `-i`,`-o`,`-v`,`-A`,`-B` 和 `-C`,这些参数是值得学习并掌握的。 +- 熟悉正则表达式,学会使用 `grep`/`egrep`,它们的参数中 `-i`,`-o`,`-v`,`-A`,`-B` 和 `-C` 这些是很常用并值得认真学习的。 -- 学会使用 `apt-get`,`yum`,`dnf` 或 `pacman` (取决于你使用的 Linux 发行版)来查找或安装软件包。并确保你的环境中有 `pip` 来安装基于 Python 的命令行工具 (接下来提到的部分程序使用 `pip` 来安装会很方便)。 +- 学会使用 `apt-get`,`yum`,`dnf` 或 `pacman` (具体使用哪个取决于你使用的 Linux 发行版)来查找和安装软件包。并确保你的环境中有 `pip` 来安装基于 Python 的命令行工具 (接下来提到的部分程序使用 `pip` 来安装会很方便)。 ## 日常使用 -- 在 Bash 中,可以使用 **Tab** 自动补全参数,使用 **ctrl-r** 搜索命令行历史(在按下之后,键入便可以搜索,重复按下 **ctrl-r** 会在更多匹配中循环,按下 **Enter** 会执行找到的命令,按下右方向键会将结果放入当前行中,使你可以进行编辑)。 +- 在 Bash 中,可以通过按 **Tab** 键实现自动补全参数,使用 **ctrl-r** 搜索命令行历史记录(按下按键之后,输入关键字便可以搜索,重复按下 **ctrl-r** 会向后查找匹配项,按下 **Enter** 键会执行当前匹配的命令,而按下右方向键会将匹配项放入当前行中,不会直接执行,以便做出修改)。 -- 在 Bash 中,可以使用 **ctrl-w** 删除你键入的最后一个单词,使用 **ctrl-u** 删除当前光标所在位置之前的内容,使用 **alt-b** 和 **alt-f** 以单词为单位移动光标,使用 **ctrl-a** 将光标移至行首,使用 **ctrl-e** 将光标移至行尾,使用 **ctrl-k** 删除光标至行尾的所有内容,使用 **ctrl-l** 清屏。键入 `man readline` 查看 Bash 中的默认快捷键,内容很多。例如 **alt-.** 循环地移向前一个参数,以及 **alt-*** 展开通配符。 +- 在 Bash 中,可以按下 **ctrl-w** 删除你键入的最后一个单词,**ctrl-u** 可以删除行内光标所在位置之前的内容,**alt-b** 和 **alt-f** 可以以单词为单位移动光标,**ctrl-a** 可以将光标移至行首,**ctrl-e** 可以将光标移至行尾,**ctrl-k** 可以删除光标至行尾的所有内容,**ctrl-l** 可以清屏。键入 `man readline` 可以查看 Bash 中的默认快捷键。内容有很多,例如 **alt-.** 循环地移向前一个参数,而 **alt-*** 可以展开通配符。 -- 你喜欢的话,可以键入 `set -o vi` 来使用 vi 风格的快捷键,而 `set -o emacs` 可以把它改回来。 +- 你喜欢的话,可以执行 `set -o vi` 来使用 vi 风格的快捷键,而执行 `set -o emacs` 可以把它改回来。 -- 为了方便地键入长命令,在设置你的编辑器后(例如 `export EDITOR=vim`),键入 **ctrl-x** **ctrl-e** 会打开一个编辑器来编辑当前命令。在 vi 模式下则键入 **escape-v** 实现相同的功能。 +- 为了便于编辑长命令,在设置你的默认编辑器后(例如 `export EDITOR=vim`),**ctrl-x** **ctrl-e** 会打开一个编辑器来编辑当前输入的命令。在 vi 风格下快捷键则是 **escape-v**。 -- 键入 `history` 查看命令行历史记录,再用 `!n`(`n` 是命令编号)就可以再次执行。其中有许多缩写,最有用的大概就是用 `!$` 指代上次键入的参数,以及用 `!!` 指代上次键入的命令了(参考 man 页面中的“HISTORY EXPANSION”)。不过这些通常被 **ctrl-r** 和 **alt-.** 取代。 +- 键入 `history` 查看命令行历史记录,再用 `!n`(`n` 是命令编号)就可以再次执行。其中有许多缩写,最有用的大概就是 `!$`, 它用于指代上次键入的参数,而 `!!` 可以指代上次键入的命令了(参考 man 页面中的“HISTORY EXPANSION”)。不过这些功能,你也可以通过快捷键 **ctrl-r** 和 **alt-.** 来实现。 -- 要进入 home 目录可以用 `cd`。要访问你的 home 目录中的文件,可以使用前缀 `~`(例如 `~/.bashrc`)。在 `sh` 脚本里则用 `$HOME` 指代 home 目录。 +- `cd` 命令可以切换工作路径,输入 `cd ~` 可以进入 home 目录。要访问你的 home 目录中的文件,可以使用前缀 `~`(例如 `~/.bashrc`)。在 `sh` 脚本里则用环境变量 `$HOME` 指代 home 目录的路径。 -- 回到上一个工作路径:`cd -` +- 回到前一个工作路径:`cd -`。 -- 如果你输入命令的时候改变了主意,按下 **alt-#** 在行首添加 `#`,或者依次按下 **ctrl-a**, **#**, **enter**。这样做的话,之后你可以很方便的利用命令行历史回到你刚才输入到一半的命令。 +- 如果你输入命令的时候中途改了主意,按下 **alt-#** 在行首添加 `#` 把它当做注释再按下回车执行(或者依次按下 **ctrl-a**, **#**, **enter**)。这样做的话,之后借助命令行历史记录,你可以很方便恢复你刚才输入到一半的命令。 - 使用 `xargs` ( 或 `parallel`)。他们非常给力。注意到你可以控制每行参数个数(`-L`)和最大并行数(`-P`)。如果你不确定它们是否会按你想的那样工作,先使用 `xargs echo` 查看一下。此外,使用 `-I{}` 会很方便。例如: ```bash @@ -100,7 +100,7 @@ ``` -- `pstree -p` 有助于展示进程树。 +- `pstree -p` 以一种优雅的方式展示进程树。 - 使用 `pgrep` 和 `pkill` 根据名字查找进程或发送信号(`-f` 参数通常有用)。 @@ -108,23 +108,23 @@ - 使用 `nohup` 或 `disown` 使一个后台进程持续运行。 -- 使用 `netstat -lntp` 或 `ss -plat` 检查哪些进程在监听端口(默认是检查 TCP 端口; 使用参数 `-u` 检查 UDP 端口)。 +- 使用 `netstat -lntp` 或 `ss -plat` 检查哪些进程在监听端口(默认是检查 TCP 端口; 添加参数 `-u` 则检查 UDP 端口)。 -- 有关打开套接字和文件,请参阅 `lsof`。 +- `lsof` 来查看开启的套接字和文件。 - 使用 `uptime` 或 `w` 来查看系统已经运行多长时间。 - 使用 `alias` 来创建常用命令的快捷形式。例如:`alias ll='ls -latr'` 创建了一个新的命令别名 `ll`。 -- 把别名、shell 选项和常用函数保存在 `~/.bashrc`,然后[安排登陆 shell 来读取](http://superuser.com/a/183980/7106)。这样你就可以在所有 shell 会话中使用你的设定。 +- 可以把别名、shell 选项和常用函数保存在 `~/.bashrc`,具体看下这篇[文章](http://superuser.com/a/183980/7106)。这样做的话你就可以在所有 shell 会话中使用你的设定。 -- 把环境变量的设定以及登陆时要执行的命令保存在 `~/.bash_profile`。对于从图形界面启动的,以及 `cron` 工作的 shell,需要单独配置。 +- 把环境变量的设定以及登陆时要执行的命令保存在 `~/.bash_profile`。而对于从图形界面启动的 shell 和 `cron` 启动的 shell,则需要单独配置文件。 -- 要在几台电脑中同步你的配置文件(例如 `.bashrc` 和 `.bash_profile`),可以用 Git。 +- 要想在几台电脑中同步你的配置文件(例如 `.bashrc` 和 `.bash_profile`),可以借助 Git。 -- 当变量和文件名中包含空格的时候要格外小心。Bash 变量要用引号括起来,比如 `"FOO"`。尽量使用 `-0` 或 `-print0` 选项以便用空字符来分隔文件名,例如 `locate -0 pattern | xargs -0 ls -al` 或 `find / -print0 -type d | xargs -0 ls -al`。如果 for 循环中循环访问的文件名含有空格,只需用 `IFS=$'\n'` 把内部字段分隔符设为换行符。 +- 当变量和文件名中包含空格的时候要格外小心。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 -x` 去调试输出,尽可能地使用严格模式,使用 `set -e` 令脚本在发生错误时退出而不是继续运行,使用 `set -u` 来检查是否使用了未赋值的变量,使用 `set -o pipefail` 严谨地对待错误(尽管问题可能很微妙)。当牵扯到很多脚本时,使用 `trap`。一个好的习惯是在脚本文件开头这样写,这会使它检测一些错误,并在错误发生时中断程序并输出信息: +- 在 Bash 脚本中,使用 `set -x` 去调试输出(或者使用它的变体 `set -v`,它会记录原始输入,包括多余的参数和注释)。尽可能地使用严格模式:使用 `set -e` 令脚本在发生错误时退出而不是继续运行;使用 `set -u` 来检查是否使用了未赋值的变量;试试 `set -o pipefail`,它可以监测管道中的错误。当牵扯到很多脚本时,使用 `trap` 来检测 ERR 和 EXIT。一个好的习惯是在脚本文件开头这样写,这会使它能够检测一些错误,并在错误发生时中断程序并输出信息: ```bash set -euo pipefail trap "echo 'error: Script failed: see failed command above'" ERR @@ -137,7 +137,7 @@ # continue in original dir ``` -- 在 Bash 中,要注意变量展开的形式有很多。检查变量是否存在:`${name:?error message}`。例如,当 Bash 脚本只需要一个参数时,可以使用这样的代码 `input_file=${1:?usage: $0 input_file}`。在变量为空时使用默认值:`${name:-default}`。如果你要在之前的例子中再加一个(可选的)参数,可以使用类似这样的代码 `output_file=${2:-logfile}`,如果省略了 $2,它的值就为空,于是 `output_file` 就会被设为 `logfile`。数学表达式:`i=$(( (i + 1) % 5 ))`。序列:`{1..10}`。截断字符串:`${var%suffix}` 和 `${var#prefix}`。例如,假设 `var=foo.pdf`,那么 `echo ${var%.pdf}.txt` 将输出 `foo.txt`。 +- 在 Bash 中,变量有许多的扩展方式。`${name:?error message}` 用于检查变量是否存在。此外,当 Bash 脚本只需要一个参数时,可以使用这样的代码 `input_file=${1:?usage: $0 input_file}`。在变量为空时使用默认值:`${name:-default}`。如果你要在之前的例子中再加一个(可选的)参数,可以使用类似这样的代码 `output_file=${2:-logfile}`,如果省略了 $2,它的值就为空,于是 `output_file` 就会被设为 `logfile`。数学表达式:`i=$(( (i + 1) % 5 ))`。序列:`{1..10}`。截断字符串:`${var%suffix}` 和 `${var#prefix}`。例如,假设 `var=foo.pdf`,那么 `echo ${var%.pdf}.txt` 将输出 `foo.txt`。 - 使用括号扩展(`{`...`}`)来减少输入相似文本,并自动化文本组合。这在某些情况下会很有用,例如 `mv foo.{txt,pdf} some-dir`(同时移动两个文件),`cp somefile{,.bak}`(会被扩展成 `cp somefile somefile.bak`)或者 `mkdir -p test-{a,b,c}/subtest-{1,2,3}`(会被扩展成所有可能的组合,并创建一个目录树)。 @@ -146,7 +146,7 @@ diff /etc/hosts <(ssh somehost cat /etc/hosts) ``` -- 编写脚本时,你可能会想要把代码都放在大括号里。缺少右括号的话,代码就会因为语法错误而无法执行。如果你的脚本是要放在网上供人下载的,这样的写法就体现出它的好处了,因为没有完全下载的脚本无法执行: +- 编写脚本时,你可能会想要把代码都放在大括号里。缺少右括号的话,代码就会因为语法错误而无法执行。如果你的脚本是要放在网上分享供他人使用的,这样的写法就体现出它的好处了,因为这样可以防止下载不完全代码被执行。 ```bash { # 在这里写代码 @@ -155,15 +155,15 @@ - 了解 Bash 中的“here documents”,例如 `cat <logfile 2>&1`。通常,为了保证命令不会在标准输入里残留一个打开了的文件句柄导致你当前所在的终端无法操作,添加 `logfile 2>&1` 或者 `some-command &>logfile`。通常,为了保证命令不会在标准输入里残留一个未关闭的文件句柄捆绑在你当前所在的终端上,在命令后添加 `>> 2+3 5