给前端工程师的 Terminal 指南


现代前端框架如 React、Angular 和 Vue 都非常依赖终端。如果你对命令行界面不熟悉,你将很难启动本地开发服务器或构建应用程序!

这其中有一种深刻的讽刺。我们的工作是构建图形用户界面,而我们使用的开发工具却主要是基于命令行的!

这就是这篇博客的全部内容。它是现代 JS 框架(如 React)所需的终端基础知识指南,帮助你快速进入构建用户界面的有趣部分!

准备工作

好了,在开始之前我们需要做两件事。

首先,我们需要一些终端软件。这是运行命令行环境的应用程序。

几乎每个操作系统都有内置的终端,比如 MacOS Terminal.app Windows 的命令提示符。这些应用程序虽然可以使用,但体验一般。大多数开发者会选择其他工具。

选择终端应用程序并不是非常重要,只要你使用现代的工具即可。话虽如此,我有两个主要推荐:

    1. Hyper。Hyper 是一个现代的跨平台终端应用程序。它外观美观,带有一些实用的现代功能,比如可以将窗口分割为多个面板。
    1. 如果你使用 VS Code 作为代码编辑器,VS Code 自带了一个强大的现代终端。这很方便,因为你的代码和终端可以在同一个应用程序中并排运行。你可以通过选择“View → Terminal”打开 VS Code 的终端。

现在,终端应用程序只是开始。我们还需要确保我们正在运行正确的 shell 语言。

当我们在终端中输入命令并按下“回车”时,命令将由 shell 语言解释。它本质上是运行在终端应用程序中的环境。

最流行的 shell 语言是 Bash。当你看到在线的命令行教程时,通常都是基于 Bash 的。大多数 Linux 发行版默认使用 Bash 作为 shell 语言。

现代的 MacOS 版本使用 Zsh 代替 Bash,但 Zsh 与 Bash 非常相似:它们属于同一个“家族”,并且共享几乎所有相同的命令。对于我们的目的,它们可以互换使用。

如果你使用的是 Linux 或 MacOS,那你已经可以直接使用“行业标准”的 shell 语言。如果你使用的是 Windows,那么我们有一些额外的工作要做。

一个有用的类比

你有没有打开过浏览器中的开发者控制台来运行一些任意的 JavaScript 代码?

在这种情况下,应用程序是 Chrome,语言是 JavaScript。Chrome 提供命令行界面,但当我们运行命令时,这些命令由 JavaScript 解释。

在终端环境下,情况类似。一个终端应用程序(如 Hyper)可能运行 Bash shell 语言。与浏览器不同,终端应用程序可以在多种 shell 语言之间切换!

Hello World

当你第一次打开终端应用程序时,你会看到这样一个不太友好的界面:

根据你的操作系统、终端应用程序或 shell 语言,终端界面可能略有不同。然而,最终你可能会看到一行文本和大量的空白区域。

这一行文本被称为提示符(prompt)。它之所以叫“提示符”,是因为它在等待你输入某种指令。

我们来输入第一个命令,键入 echo "hello world" 并按回车键:

语法略有不同,但你可以将命令视为内置的 JavaScript 函数。echo 命令类似于 JavaScript 中的 console.log 函数。

像函数一样,命令也接受参数。在这种情况下,echo 接受一个参数,即要输出的字符串。

当我们按下“回车”时,命令立即执行,并显示了我们输入的值。一个新的提示符会出现在下方,告诉我们它已准备好接受下一个指令。

就这样,你成功运行了你的第一个终端命令!

忽略 “$” 符号!

当你阅读 NPM 包的安装说明时,经常会看到如下内容:

1
$ npm install some-package

如果你尝试运行整个这一行文本,你会遇到错误。这是因为 $ 符号并不需要输入。你只需输入 $ 符号后面的内容。

为什么安装说明中会包含一个不属于命令的随机符号呢?在 Bash shell 语言中,$ 是“提示符字符”,它出现在提示符的末尾。

它本质上是一个符号,表示“嘿,这里的东西是需要在终端中运行的命令!”

即使在许多现代 shell 语言(如 Zsh)中,$ 已经不再用作提示符字符,这种象征意义依然存在,就像“保存”图标是一个软盘 (),即使我们已经几十年没使用软盘了。

导航

终端的主要目的是让你在文件系统中移动并打开或运行东西。它本质上是我们每天使用的图形界面文件管理器(如 Finder 或 Windows Explorer)的文本版。

为了帮助我们导航,有很多终端命令可用。让我们来探索一些。

pwd 命令代表“打印工作目录”(Print Working Directory),它有点像商场地图上的“你在这里”箭头。它告诉你你目前所处的位置:

当你打开终端应用程序时,你通常会进入“主目录”,也就是包含 Documents 和 Desktop 目录的文件夹。在我的机器上,这个目录位于 /Users/joshu

你可以使用 ls 命令(即“列出”)查看当前目录的内容:

在终端中,目录用粗体显示,颜色为浅蓝色,而单个文件则是常规字体,颜色为白色。

我们可以使用 cd(“切换目录”)命令在文件系统中移动:

运行 ‘cd’ 命令,进入前面 ‘ls’ 显示的某个目录。随后运行 ‘pwd’ 确认新位置
这相当于在图形界面文件管理器中双击“stuff”目录。

注意提示符从波浪号字符(~)变为“stuff”。在 Zsh shell 语言中,默认的提示符由一个箭头和当前目录的名称组成,如“→ Documents”。

但是,为什么之前显示的是波浪号,而不是上一级目录的名称呢?在 MacOS 和 Linux 系统中,波浪号是用户主目录的简写。在我的机器上,“~” 等同于 “/Users/joshu”。

很容易错误地认为“~” 是一个提示符字符,就像 Bash 中的“$” 一样。

如果我想返回上一级目录,即主目录怎么办?我可以使用 cd 命令,后面加上两个点(..):

运行 ‘cd ..’,返回上一级目录

点字符(.)在大多数 shell 语言中有特殊含义:

  • 一个点(.)表示当前目录。
  • 两个点(..)表示父目录。

如果你曾使用过 JavaScript 的模块系统,你可能已经熟悉这种表示法。它使用相同的符号,用两个点来表示父目录:

1
2
import { COLORS } from '../../constants';
import Button from '../Button';

关于 cd,有一点很重要,那就是它可以接受复杂的路径。终端新手通常会像在图形界面文件管理器中一样,一步一步进入目录:

使用 ‘cd’ 命令逐步进入 4 个不同的目录

这样做是可行的,但这多花了很多步骤。我们可以一步完成相同的跳转,如下所示:

运行 ‘cd’ 命令,快速完成与之前相同的跳转,只需一步:’cd things/some-project/src/components’

Tab 自动补全

使用终端最令人生畏的事情之一就是它不会给你任何提示或线索。而在图形界面的文件浏览器中,你可以看到所有文件和文件夹的完整列表,帮助你刷新记忆,找到你要找的东西。

如果你像我建议的那样使用 cd 命令,从一个地方跃迁到另一个地方,似乎需要具备超强的记忆力。你必须记住路径中每个目录的确切名称,对吗?

幸运的是,有一个非常实用的技巧可以让这一切变得更简单:Tab 自动补全。

Tab 键在高效使用终端时至关重要。除了用来导航,你还可以利用 Tab 来自动补全 Git 分支名称,或补齐命令的剩余部分。

标志位 (Flags)

前面我提到,Bash/Zsh 中的命令类似于 JavaScript 函数。然而,在标志(flags)方面,这个类比就有点不适用了。

标志是修改命令行为的参数,可以以预定义的方式调整命令的执行。

例如,看看 rm 命令。这个命令可以删除单个文件:

运行 ‘rm theme-song.mp3’,然后运行 ‘ls’ 显示文件已被删除

我们没有得到任何确认提示,但检查后,theme-song.mp3 文件确实被删除了。*

操作时请小心!

在继续之前,我需要提醒你:终端的操作可能非常苛刻。
rm 命令没有“你确定吗?”的确认提示,也没有撤销功能。当你使用 rm 删除文件时,它不会进入回收站/垃圾桶,而是永久删除,无法恢复。*
这是终端的常见特性:几乎没有任何安全机制。因此,请在使用像 “rm” 这样的命令时非常小心!

如果你尝试使用 rm 命令删除一个目录,会出现错误:

运行 ‘rm’ 删除目录时出现错误

默认情况下,rm 只能删除单个文件,但我们可以使用 r 标志改变这个规则:

运行带有 ‘r’ 标志的 ‘rm’ 命令,成功删除目录
r 标志表示“递归删除”(recursive)。它会删除 stuff 目录中的所有内容,删除目录中的目录中的所有内容,依次类推。*

你可能还会遇到文件权限问题,因此通常会使用 f 标志(强制删除)。我们可以将多个标志组合在一起,用一个短横线表示,例如:

运行带有 ‘r’ 和 ‘f’ 标志的 ‘rm’ 命令
标志有很多形式和大小。按惯例,标志通常有简写形式(例如 -f)和全写形式(例如 --force)。全写形式通常使用两个短横线,并使用完整的单词而不是单个字母。

我们再来看一个例子,前面我们提到的 ls 命令通常与两个标志一起使用:

  • l 标志,“长格式”(long),它会以详细列表形式打印目录内容,包含元数据。
  • a 标志,“全部”(all),它会包含隐藏文件和目录。

这极大地改变了输出内容:

运行 ‘ls -la’。显示了一个详细列表,包括隐藏文件和文件夹

这里有很多不必要的信息,包括非常晦涩的权限符号。但一些元数据,比如文件的最后更新日期,还是很有用的!

手册

为了了解更多关于命令的信息,你可以使用 man 命令(manual 的缩写)来查看内置的文档:

运行 ‘man’ 命令查看 ‘ls’ 的文档

提前警告你,man 文档非常密集且难以阅读。但它仍然有助于了解某些命令可用的标志。

有时,文件会在默认文本编辑器中打开,但通常它会像图中一样“在终端中”打开。这使用的是一个名为 less 的程序。

less 中滚动文档,使用上下箭头键。在现代版本的 MacOS 上,你还可以使用鼠标滚轮进行滚动,但这可能会在其他平台上导致一些 Bug。

完成后,按 q 退出。退出后,终端会恢复到通常的显示状态。

中断命令

有些进程是长期运行的,需要手动中断。

例如,打开终端应用程序并尝试运行以下命令:ping 8.8.8.8

ping 命令用于检查与给定 IP 地址的延迟情况,通常用来检查服务器是否在线。8.8.8.8 是 Google DNS 服务器的 IP 地址。

运行 ping 8.8.8.8,终端会开始显示结果,显示每次 ping 返回的延迟时间为 30-45 毫秒。

与我们之前看到的命令不同,ping 是一个长期运行的进程,它不会自动停止,默认情况下会一直 ping Google’s DNS 服务器,直到手动中断。

当我们对结果感到满意时,可以按住 Ctrl 并按 C 来中断它。即使在 MacOS 上,大多数快捷键使用 ⌘ 键作为修饰符,这里我们依然使用 Ctrl 键。

另一个有用的命令是 Ctrl + D。这会结束当前会话。如果 Ctrl + C 无效,Ctrl + D 可能会生效。

最后,如果所有方法都不起作用,可以关闭当前的标签页/窗口。快捷键取决于操作系统和终端应用程序。在 MacOS 上使用 Hyper,这可以通过 ⌘ + W 完成。

退出 Vi / Vim

偶尔,你可能会发现自己在使用 Vi 或 Vim 编辑文件。这些编辑器以难以退出而著称,Ctrl + C 在这里无济于事!

要在不保存的情况下退出,请按以下步骤操作:

  1. Escape
  2. :,此时终端底部应出现一个提示符。
  3. 输入 q! 并按回车。

常见开发任务

到目前为止,我们已经看到了很多关于如何使用终端进行一般计算的示例。现在让我们看看如何完成一些典型的开发任务!

这些示例假设你已经安装了 Node.js。如果还没有安装,你可以从 Node 主页 下载副本。

管理依赖

假设这是你工作的第一天,团队已经给你访问源代码的权限,并且你已经将它下载到你的机器上。接下来做什么呢?

第一步是下载项目的第三方依赖!

按照以下步骤操作:

1
2
cd path/to/project
npm install

npm 是 Node 包管理器,安装 Node.js 时会自动安装它。

运行此命令会从 NPM 仓库下载项目依赖的所有第三方代码,这些代码会存放在本地的 node_modules 目录中。

运行 NPM 脚本

好吧,你已经下载了第三方代码,接下来呢?

如果你查看项目的 package.json 文件,你可能会看到类似这样的部分:

1
2
3
4
5
6
7
8
{
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}
}

这些“脚本”是可以使用 NPM 工具运行的任务。它们可以通过运行 npm run [name] 来执行。例如,要启动一个本地开发服务器,我们可以运行:

1
2
cd path/to/project
npm run start

运行此命令会启动一个长期运行的进程。它启动了一个 Node 服务器,让我们可以开发应用程序,监视文件更改,并在编辑时重新打包。

当我们完成后,可以通过 Ctrl + C 来关闭服务器。

NPM 脚本的美妙之处在于它们的标准化。startbuildtest 是这些标准任务的惯用名称。因此,即使项目使用完全不同的工具,我们也不需要记住每个项目的特殊命令。*

(我们也可以创建自己的 NPM 脚本!这是我在项目中广泛使用的一种方法。稍后我会发布一篇关于这个主题的博客文章,记得订阅以免错过!)

在你的 IDE 中打开项目

当我想开始一个项目时,我会先通过终端导航到项目的根目录。然后运行以下命令:

1
2
cd path/to/project
code .

正如我们之前讨论的,. 代表当前工作目录。code 是由我的代码编辑器 VS Code 添加的命令。运行此命令将整个项目在代码编辑器中打开,让我可以轻松地在文件之间切换。

请注意,命令会根据你的编辑器而有所不同。而对于使用 VS Code 的 MacOS 用户,你需要做一些工作来启用 code 命令。

重新安装依赖

你知道解决计算机问题的标准建议是重启它吗?

JavaScript 版的解决方案就是重新安装 NPM 依赖。有时,它们只需要删除并重新下载。这尤其适用于当你偶尔会进入 node_modules 目录并编辑文件以帮助调试的情况。*

以下是步骤:

1
2
3
cd path/to/project
rm -rf node_modules
npm install

进入正确的目录后,我们可以使用 rm 命令删除所有第三方代码,然后使用 npm install 重新安装。

使用 Git

虽然有图形用户界面(GUI)应用程序可以用来操作 Git,但许多开发者更喜欢使用命令行进行 Git 相关的任务。

这篇博客文章不可能涵盖完整的命令行 Git 教程,但以下是我常用的一些命令的快速备忘单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 将 Git 仓库下载到本地机器上
git clone [URL]
// 检查哪些文件已被修改
git status -s
// 查看更改
git diff
// 暂存所有文件
git add .
// 提交已暂存的文件
git commit -m "简短的描述性信息"
// 创建一个新的本地分支
git switch -c [新分支名称]
// 切换分支
git switch [分支名称]
// 将代码推送到 Github(或项目所在的其他地方)
git push origin [分支名称]
// 启动交互式变基
git rebase -i [分支名称或提交哈希]

小技巧

多年来,我学到了一些实用的小技巧。虽然它们不是至关重要,但它们有助于改善使用终端的开发者体验。

循环和切换命令

许多终端应用程序会保留当前会话中所有运行的命令的日志。你可以使用“上”箭头键循环浏览之前的命令。

如果我知道我最近运行过一个命令,通常按几次“上”箭头比从头输入更快!

还有一个我之前学到的绝妙小技巧:- 符号。

假设我们想在两个目录之间来回切换,可以使用 cd 命令。我们可以通过反复输入完整路径来实现:

1
2
3
4
5
cd /path/to/first-directory
cd /path/to/second-directory
cd /path/to/first-directory
cd /path/to/second-directory
...

但我们可以用一个简单的技巧来节省时间:

1
cd -

每次运行 cd - 命令都会在上一个目录和当前目录之间切换,非常方便!

清除终端

像清理桌面一样,清理终端也可以让思路更清晰。

有几种方法可以实现这一点。可以使用 clear 命令,这会擦除之前输入的所有命令,让终端看起来像刚启动的新会话一样。

还有一个通用的快捷键是 Ctrl + L。这与 clear 命令效果相同。它应该在 MacOS、Windows 和 Linux 上都有效。

这个命令/快捷键是 Bash/Zsh 的一部分。它只在 Shell 空闲时有效,即当你有一个等待接收指令的提示符时。

某些终端应用程序也实现了自己的快捷键,这些快捷键即使在 Shell 正在忙碌时也能工作。我知道的快捷键包括:

  • 在 MacOS 上,几乎所有的 Shell(如 Terminal.app、iTerm2、Hyper)都可以使用 ⌘ + K
  • 如果你在非 MacOS 平台上使用 Hyper,快捷键是 Ctrl + Shift + K

这些应用程序级别的快捷键更为实用,即使在 Shell 忙碌时也可以使用。

例如,假设你正在运行一个开发服务器。这是一个长期运行的进程,因此 Ctrl + L 快捷键可能无效。当你在项目中工作时,终端窗口中会记录很多消息。应用程序快捷键允许你清除旧的日志,就像归档旧邮件一样。这非常有用,也是现代终端应用程序如何让我们的生活变得更轻松的一个很好的例子。

感谢 Aleksandr 和 Joseph Cagle 帮助我了解了这些在非 MacOS 平台上如何工作的!

别名

有时,我发现自己反复输入相同的命令。如果这个命令很长或很复杂,每次都输入和记住它会很烦人。

Bash 和 Zsh 支持别名,这是创建自定义快捷方式的一种方法。例如,我可以设置这样,当我输入 hi 时,它会自动运行 echo "Hello World!"

1
alias hi="echo 'Hello World!'"

设置别名的步骤略超出了本教程的范围,不同的 Shell 语言设置略有不同。这里有一些有用的教程可以深入了解:

切换到 GUI 文件浏览器

除非你已经达到了终端的黑带水平,否则会有时需要在 GUI 文件浏览器中打开工作目录。

  • 在 MacOS 上,open . 命令可以做到这一点:

    1
    open .

    open 命令通常用于打开文件,就像双击文件在 GUI 文件浏览器中打开它一样。
    当我们尝试打开目录时,它会弹出一个新的 Finder 窗口,显示该目录的内容。
    由于点字符(.)表示当前目录,open . 允许我们从终端切换到 Finder,以便继续在终端外工作。

  • 在 Windows 上,你可以使用 explorer . 来实现相同的目标!

  • 在 Linux 上,可以使用 xdg-open 打开文件或当前目录,只要 Linux 发行版实现了 FreeDesktop 标准。

    链接命令

每当我从 Github 克隆一个新项目时,通常会有两个连续的操作:

  1. npm install,以获取第三方依赖项
  2. npm run start,以启动本地开发服务器

npm install 命令通常需要几分钟。我没有耐心坐着看依赖项下载,因此我经常会去刷 Twitter。结果,20 分钟过去了,我完全忘了自己还要启动开发服务器。😬

我们可以通过链式操作来解决这个问题。它的工作原理如下:

1
npm install && npm run start

&& 操作符允许我们将多个命令链接在一起。第一个命令会被执行,即 npm install。它完成后,第二个命令会自动运行。

这是一个特别有用的小技巧,因为 npm run start 通常会打开一个浏览器窗口,吸引我的注意力,并让我知道一切都准备好了。而 npm install 则默默地完成。

一旦我掌握了链式操作,我开始在所有地方使用它。我经常排队执行一系列 Git 命令:

1
git add . && git commit -m "一些更改" && git push origin main
终端分屏和标签页

好了,我们来谈谈如何保持工作区的组织性。

运行开发服务器 npm run start 是一个长期运行的过程。我常常让开发服务器连续运行好几个星期!

当终端会话忙于执行任务时,它无法接受额外的命令。记住,提示符用于显示终端在等待命令;如果我们看不到提示符,就无法在该会话中运行任何东西!

幸运的是,现代终端应用程序使得在同一个应用程序中运行多个终端会话变得很容易。

在 Hyper 中,我们可以通过选择 Shell -> Split down 将窗口分成多个垂直窗格。在 MacOS 上,快捷键是 Shift + ⌘ + d。这会创建两个独立的会话:

两个终端会话,一个在另一个上面

通过将窗口分成多个会话,顶部会话可以专注于运行开发服务器,突出显示错误和其他重要信息。底部会话可以用来运行较短的任务。

有时,项目需要多个长期运行的任务;例如,可能我们有一个开发服务器和一个测试监视器。在这种情况下,我们可以将窗口分成 3 个会话。

在 Hyper 中,我们也可以创建多个标签页。可以通过 Shell -> New Tab 创建新标签。在 MacOS 上,快捷键与在网页浏览器中创建新标签相同:⌘ + t

什么时候使用标签页,什么时候使用分屏?我喜欢每个项目一个标签页。每个标签页可以分成多个会话,以满足特定项目的需求。