前言
诸如React、Angular、Vue等现代前端框架严重依赖于终端。如果你还不习惯使用命令行界面,你将很难运行本地开发服务器或构建你的应用程序。
讽刺的是,我们的整个工作都建立在图形化用户界面上,但我们在开发中使用的工具却大多是基于命令行的。
除非你有计算机科学背景,或者在上世纪80年代使用计算机长大,否则你大概率不会拥有丰富的终端经验。然而,大部分在线资源都假定你已经对此十分熟练了。
要成为一个终端大佬需要多年的练习,但好消息是:我们可以走捷径。我们并不真的需要知道可以用终端做的绝大多数的事情。如果我们专注于最重要的关键基础知识,我们应该能够在很短的时间内适应命令行。✨
这也是这篇文章想要介绍的内容。它是使用现代JS框架(如React)所需的,终端基础知识的一门手册,有了它你就可以转到有趣的事情上:构建用户界面。
我还将分享我从终端中获得的技巧和窍门,这些东西我多希望在我刚开始工作时就有人能告诉我。
准备工作
好了,在开始之前,我们还需要做两件事情。
首先,我们需要一些终端软件。终端软件是运行命令行环境的应用程序。
几乎每个操作系统都会有一个内置的终端,比如MacOS的Terminal.app
,或者Windows的Command Prompt
,这些应用程序都可以工作,但都不太让人满意。大多数开发者会选择使用其他软件。
只要你使用的是现代技术,那么选择使用哪款终端应用就没那么重要。因此,这里我有两个主要建议:
- Hyper。Hyper是现代的,多平台的终端应用程序。它颜值很高,还带有一些时髦的功能,比如可以分为多个窗口。
- 如果你使用 VS Code 作为代码编辑器,那么 VS Code 已经内置了一个强大的、现代化的终端。这非常棒,意味着代码和终端可以在一个应用内并排运行。你可以在 VS Code 中通过选择 查看→终端 来打开终端。
在这篇文章中,我将使用Hyper来展示所有的例子。
到这里,选择好了终端应用才完成了一半。我们还需要确保运行正确的shell
语言。
当我们在终端输入命令并回车时,命令会通过shell
语言进行解释执行。它本质上是在终端应用程序中运行的环境。
最流行的shell
语言是Bash。当你在网上看到命令行说明时,这些说明很有可能是基于Bash的。这是大多数Linux发行版使用的默认shell
语言。
现代的MacOS版本自带的Zsh,而不是Bash。但Zsh与Bash十分类似:它们属于同一家族,几乎共享所有相同的命令。从目的角度出发,它们是可以互换使用的。
如果你使用的是Linux或者MacOS,那就可以正式开始了。你的计算机已经在使用一种"行业标准"的shell
语言。然而,如果你使用的是Windows,我们还有一点工作要做。
形象的比喻
你是否曾经在你的浏览器中打开开发者控制台,来运行一些任意的JavaScript代码?
在这种情况下,应用程序是Chrome,而语言是JavaScript。Chrome提供了命令行界面,但当我们运行命令时,这些命令是用JavaScript解释的。
当涉及到终端时,它也是同样的道理。像Hyper这样的终端应用可能正在运行Bash shell
语言。与浏览器不同的是,终端应用可以在多种shell
语言之间进行切换。
Windows设置
首先,我需要承认的是,当涉及到Windows开发时,我并不是专业人士。请对我接下来说的每一句话持保留态度。
Bash是基于Linux的shell
语言,它不会在Windows上原生运行。幸运的是,较新版本的Windows具有安装和运行Linux的能力,就像Linux是一种应用程序一样。这被称为Windows Subsystem for Linux,通常缩写为WSL。
这里有一个教程,介绍了所需的步骤:在Windows中,如何安装并使用Zsh。
我自己运行了这些步骤,虽然这有点乏味,但它确实起作用了!
一旦设置好之后,你就可以配置终端来使用Bash或者Zsh。这里有一些关于配置Hyper来使用Zsh的介绍。
如果你在这些步骤中遇到了麻烦,这里有些其他的解决方案你可以试一试。一个流行的方法是Git Bash,它允许你在Windows内使用模拟技术运行Bash。
归根结底,你如何做到并不重要。重要的是,你能够在Windows中使用Bash或Zsh。
Hello World
当你第一次打开终端应用时,你会遇到这个相当无助的界面:
你的终端看起来可能会有些不同,这取决于你使用的操作系统、终端应用、shell
语言。总的来说,你可能会看到一行文字,和一堆空白空间。
这一行文字被称为提示。它之所以被称为"提示",是因为它在等待你提供某种指令。
我们的第一个命令,输入文本echo "hello world"
并回车:
语法有一点不同,但你可以认为命令就是内置的JavaScript函数。echo
命令与JavaScript中的console.log
函数非常相似。
跟函数一样,命令也可以接收参数。在本例中,echo
接收了一个参数,并输出了字符串。
当我们按下回车时,命令就会立即执行,值也会被打印。下一行会呈现一个新的提示,让我们知道它已经准备好接收下一个指令。
就像这样,你已经成功运行了第一条终端命令。
跳过$
在阅读NPM包的安装说明时,你会经常看到这样的内容:
$ npm install some-package
如果你试图运行这一段文字,你会得到一个错误。这是因为美元符号($)不应该被包括在内。你应该在美元符号之后输入所有内容。
为什么安装说明会包含一个随机的符号,而这个符号实际上并不是命令的一部分?好吧,在Bash shell
语言中,$是提示符,显示在提示的末尾。
它本质上是一个符号,并说道:嘿,这里的东西要在终端运行!
尽管在许多现代shell
语言(如Zsh)中,$实际上并不作为提示字符使用,但其象征意义仍然存在,比如保存的图标是一个软盘形状,尽管我们已经几十年没有使用软盘了。
导航
终端的主要目的是能够让你在文件系统中移动文件,并打开/运行东西。它本质上是我们每天使用的GUI文件资源管理器(如Finder、Windows Explorer)的基于文本的版本。
为了帮助我们全方位导航,有很多终端命令可以使用。让我们来探索其中的一些。
pwd命令代表着 Print Working Directory ,它有点像购物中心地图上的“你在这里”箭头。它会告诉你当前所处的位置:
当你打开终端应用时,你通常会被扔进 ”home” 目录,该目录包含了 Documents 和 Desktop 目录。在我的机器上,这个目录位于/Users/joshu
。
使用ls
(List的简写)命令,你可以查看当前目录下的内容:
在我的终端上,目录是加粗的,并以浅水色展示。而单个文件是普通文字粗细,以白色展示。
我们可以使用cd
(Change Directory)命令来移动文件系统:
这相当于在GUI文件资源管理器中双击 "stuff" 目录。
需要注意的是,提示从波浪字符(~)变成了 "stuff" 。在Zshshell
语言中,默认的提示由一个箭头和当前目录的名称组成,比如说"→ Documents"。 等一下,为什么之前是一个波浪字符,而不是父目录的名称?在MacOS和Linux上,波浪字符是用户home
目录的缩写。在我的机器上,"~"相当于"/Users/joshu
"。 很容易误以为"~"是一个提示字符,就像Bash中的"$"一样。
如果我想返回上一级,返回到home
目录该怎么办?我也可以使用cd命令来达到目的,但是要带有两个点(..
) 。
在大多数shell
语言中,点字符(.
)具有着特殊的含义:
- 一个点(
.
)表示当前目录。 - 两个点(
..
)表示父级目录。
如果你在JavaScript中使用过模块系统,你可能已经熟悉了这个惯例。它使用同样的符号,用两个点来表示父级目录:
import { COLORS } from '../../constants';
import Button from '../Button';
关于cd
命令,有件很重要的事情需要知道。那就是cd
可以接收复杂的路径。终端的初学者往往会像在GUI文件资源管理器中那样,一步一步地走:
这样做是没问题的,但是需要花费大量额外的工作。我们可以像这样一步到位地完成同样的路径跳转:
Tab自动补全
终端最令人生畏的事情之一是,它不会给你任何线索或提示。使用GUI文件资源管理器,你可以看到文件和文件夹的完整列表,以刷新你的记忆并帮助你找到你要找的东西。
如果你想按照我的建议使用cd
,从一个地方一下子跳到另一个地方,看起来你可能需要一个照相式的记忆。除非你记住路径链中每个目录的确切名称,否则你无法做到这一点,对不对?
幸运的是,一个非常方便的技巧使这一切变得更加容易:tab自动补全。
在有效使用终端时,Tab键是至关重要的。除了这里展示的导航技巧外,我们还可以用Tab键来自动完成Git分支,或者补全命令的剩余部分。
试试在不同情况下按下Tab键,看看会发生什么吧。
Visual自动补全
如果你发现很难掌握Tab的自动补全,你可能会对Fig感兴趣。Fig是一个终端插件,它增加了编辑器风格的自动补全。
我也刚刚开始尝试使用Warp,一个为速度和用户体验而建立的现代终端。在写这篇文章的时候,它是MacOS独有的,但他们确实计划在测试版之后将其移植到Windows和Linux。
我们正生活在一个终端复兴的时代,有很多工具的目的是使它不那么令人生畏。
标志
早些时候,我提到过Bash/Zsh里的命令,就像JavaScript里的函数。当涉及到标志时,这个类比就不太适用了。
标志是修饰符,以预先定义的方式调整命令的行为。
举例来说,让我们来看一看rm
命令。该命令允许我们删除单个文件:
我们没有得到任何形式的确认,但如果我们再次查看,会发现theme-song.mp3
文件确实已被删除。
再继续之前,我应该警告你:终端可能相当不宽容。rm
命令没有 "你确定吗?"的确认提示。也没有任何撤销操作。当你使用rm
删除文件,它不会进入回收站/垃圾桶。它被永久地、不可逆转地删除。 这是终端的一个共同主题。没有太多的安全机制。因此,在使用rm
这样的命令时,请务必小心。
如果你尝试在目录上使用rm
命令,你会得到一个错误:
默认情况下,rm
只可以删除单个文件,但我们可以使用r
标志来改变规则:
r
标志代表着递归(recursive)。它将删除stuff
目录在内的任何东西,stuff
目录内的目录内的任何东西,stuff
目录内的目录内的任何东西,以此类推。
你也可能遇到一些文件权限问题。由于这个原因,f
标志(Force)也是很常用的。我们可以用一个破折号将多个标志分组,就像这样:
标志有很多形状和大小。按照惯例,标志通常有一个简短的形式(例如:-f)和一个完整的形式(--force)。完整形式通常使用两个破折号,并使用整个单词而不是单个字母。
让我们看个其他的例子。早前我们看到过的ls
命令,通常使用两个标志调用:
l
标志,也就是long
。它将目录内容打印成一个带有元数据的详细列表。a
标志,也就是all
。它将包含隐藏文件和目录。
这很大程度上改变了输出格式:
这里有很多烦人的数据,包括令人琢磨不透的权限字符。但是一些元数据,比如显示文件最后一次更新日期,可是很有用的。
手册
为了了解更多有关命令的内容,你可以使用man
命令(manual的缩写)调取它的内置文档。
我要告诉你的是,man
文档密密麻麻,而且经常难以解析。但它对于了解某些命令有哪些标志还是很有用的。
在某些情况下,文件会在你的默认文本编辑器中打开,但通常会在终端中打开,如图所示。这里使用了一个被称为less
的程序。
要在less
中滚动文件,请使用上/下方向键。在现代版本的MacOS上,你也可以使用鼠标滚轮来滚动,尽管这在其他平台上可能会导致错误的行为。
当你完成手册查看后,按q
退出。它应该会恢复到典型的终端视图中。
中断命令
有些进程是长时间不间断运行,如果要停止运行,就需要进行中断。
举例来说,打开终端并尝试运行接下来的命令:ping 8.8.8.8
。
ping
命令将检查给定IP地址的延迟情况。它对于检查一个给定的服务器是否在线很有用。8.8.8.8
是谷歌DNS服务器的IP地址。
与我们到目前为止看到过的命令不同,ping
是一个长期运行的进程,它永不停歇。默认情况下,它将一直ping
Google的DNS服务器,直到时间结束。
当我们对结果感到满意时,我们可以通过按下ctrl
和c
来中断该命令。即使在MacOS上,大多数的快捷键都使用的⌘修饰符,这里我们也使用ctrl
。
另一个很有用的命令是ctrl
+ d
。这会终止当前的会话。如果ctrl
+ c
在某些情况下不起作用,ctrl
+ d
可能会起作用。
最后,如果上面的方法都失败了,可以直接关闭当前的tab页或者窗口。该方式的快捷键取决于你使用的操作系统以及终端程序。如果是在MacOS上使用的Hyper,关闭当前窗口的快捷键组合是⌘
+ w
。
退出Vi / Vim
有的时候,会使用Vi
或者Vim
来编辑文件。这些编辑器是出了名的难以退出;ctrl
+ c
在这里可帮不了你。
要在不保存的情况下退出,请遵循以下步骤:
- 按下
Escape
。 - 按下
:
,这应该会在终端的底部添加一个提示。 - 输入
q!
,并按下回车。
通用开发任务
到目前为止,我们已经看到了很多关于如何使用终端做事情的例子。接下来让我们来看看,如何通过终端完成一些典型的开发任务。
下面的示例假设你已经在本地安装了Node.js。如果你还没有安装,你可以从Node主页下载安装包进行安装。
管理依赖
假设今天是你上班的第一天。你的团队已经允许你访问项目的源代码,你已经把源码下载到了你的电脑上。然后呢?
第一步就是下载项目的第三方依赖。
以下是需要遵循的步骤:
cd path/to/project
npm install
npm
代表着Node Package Manager。当你安装Node.js时,npm
会自动被安装。
运行该命令会从NPM仓库下载项目所依赖的所有第三方代码。这些代码将存在于本地的node_modules
目录中。
运行NPM脚本
此时,你已经下载好了第三方的代码,然后呢?
如果你查看项目的package.json
,你可能会看到这样的一部分配置:
{
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
}
}
这些scripts
是可以用NPM工具运行的任务。它们可以通过运行npm run [name]
来执行。举个例子,为了启动一个本地开发服务器,我们会运行:
cd path/to/project
npm run start
运行该命令会启动一个长期运行的进程。它启动了一个Node服务器,允许我们在应用程序上进行开发,监听文件的变动,当我们编辑文件时,重新进行打包。
当我们结束开发时,我们可以使用ctrl
+ c
来关闭该服务。
NPM脚本的美妙之处在于它们将事情标准化。start
,build
,和 test
是这些标准任务的常见名称。因此,我们不必记住每个项目定制的命令,即使这些项目使用完全不同的工具。
我们也可以创建属于自己的NPM脚本。关于此,我会在后面的文章进行详细说明。
在IDE中打开项目
当我想在一个项目上开始工作时,首先我会在终端导航到项目的根目录。然后运行以下命令:
cd path/to/project
code .
我们在前面提到过,.
指的是当前工作目录。code
是我的代码编辑器VS Code添加的一个命令。运行这个命令可以在我的代码编辑器中打开整个项目,让我可以随心所欲地在不同的文件之间轻松跳转。
需要注意的是,该命令的运行取决于你的编辑器。而且,对于在MacOS上使用VS Code的人,你需要做一些工作来启用code
命令。
重新安装依赖
你知道对任何电脑问题的标准建议是重启吗?
该问题的JavaScript版本是重新安装NPM依赖。有时候,只需要彻底删除并重新安装就可以解决问题。尤其是你会编辑node_modules
文件并进行调试时。
我们可以这么做:
cd path/to/project
rm -rf node_modules
npm install
我们进入到正确的目录后,使用rm
命令删除所有的第三方代码,然后使用npm install
重新安装依赖。
使用Git
虽然有GUI应用程序可以使用Git,但许多开发者更愿意使用命令行来完成与Git相关的任务。
完整的 Git 命令行教程远远超出了本文的范围,但这里有一份我常用的命令的快速小抄:
// 下载Git仓库到本地
git clone [URL]
// 检查哪些文件被修改
git status -s
// 查看更改
git diff
// 添加所有文件到暂存区
git add .
// 提交暂存的文件
git commit -m "Short descriptive message"
// 创建新的本地分支
git switch -c [new branch name]
// 切换分支
git switch [branch name]
// 推送代码
git push origin [branch name]
// 开启可交互的变基
git rebase -i [branch name or commit hash]
小技巧
多年来,我掌握了一些终端小技巧。它们并不重要,但它们有助于改善开发者使用终端的体验。
循环和切换命令
许多终端程序会记录下你在一个特定会话中所运行的每一条命令。你可以使用上键循环查看先前的命令。
如果我明确知道最近运行过某条命令,通常按几次上键会比从头开始输入更快。
这里还有一个不久前学到的神奇小技巧: -
字符。
假设我们想用cd
在两个目录之间来回跳动。我们可以通过输入整个路径来做到这一点。亦或者使用cd -
来快速切换到上一个cd
的目录下。
清除终端
就像清空桌面那样,清空终端可以让人头脑清晰。
有好几种办法可以做到这一点。先来看看clear
命令,它可以清除所有先前输入的命令,并使它看起来像你刚刚开启一个新的终端会话。
还有一个通用的快捷键,ctrl
+ L
。它与clear
命令的效果相同。它应该能在MacOS、Windows和Linux中工作。
这个命令/快捷键是在Bash/Zsh中实现的。它是shell
环境的一部分。这意味着它只在shell
空闲时起作用。
某些终端程序也实现了它们自己的快捷键,这些快捷键甚至可以在shell
繁忙时工作。下面是我所知道的快捷键的清单:
- 在MacOS中,几乎所有的
shell
(Terminal.app、iTerm2、Hyper),快捷键是⌘ + k。 - 如果在非MacOS平台使用Hyper,那么快捷键是
ctrl
+shift
+k
。
这些应用程序级的快捷键要好用得多。即使在shell
繁忙的时候也可以使用它们。
比如说,假设你正在运行一个开发服务器,这是一个长期运行的进程,所以ctrl
+ L
的快捷键是不起作用的。当你开发项目时,大量的信息会被记录在终端窗口中。应用程序的快捷键允许你清除旧的日志,就像归档旧的电子邮件一样。这真的非常有用,也是现代终端程序如何使我们更加轻松的绝佳示例。
别名
每隔一段时间,我就会发现自己会重复敲一些命令。如果这个命令又长又复杂,每次都要完整的敲出来,而且要逐字逐句地记住,这就非常烦人。
Bash和Zsh都支持别名,这是一种创建自定义快捷键的方法。比如说,我可以把它设置成每当我输入hi
时,它就自动运行echo "Hello World!"
。
设置别名有点超出了本教程的范围,而且根据你的shell
语言,说明也有点不同。这里有一些更深入的有用教程:
切换到GUI文件资源管理器
除非你已经达到了使用终端的黑带段位,否则有时你会想在GUI文件资源管理器中打开工作目录。
在MacOS中,open .
命令可以做到这一点。
open
命令一般用于打开一个文件,就像在GUI文件资源管理器中双击一个文件打开它一样。
然而,当我们试图打开一个目录时,它会选择弹出一个新的Finder窗口,同时显示该目录的内容。
由于点字符(.
)代表的是当前目录,所以打开.
允许我们从终端切换到Finder,以继续我们在终端之外的工作。
在Windows上,你可以使用explorer .
来达到同样的目的。
在Linux上,只要Linux发行版实现了FreeDesktop标准,xdg-open
就可以用来打开文件,或当前目录。
链式命令
每当我从Github上克隆一个新项目时,我一般要做两件事:
npm install
,来拉取第三方依赖。npm run start
,来启动本地开发服务器。
npm install
命令通常需要花费几分钟时间。我没有足够的注意力坐在那里盯着依赖是否下载完成,所以我经常会用Twitter来分散自己的注意力。接下来我知道的是,20分钟过去了,我完全忘记了我要启动一个开发服务器。
我们可以使用链式命令来解决该问题。以下是它的工作原理:
&&
操作符允许我们将多个命令链接在一起。第一条命令将被执行,即npm install
。当它完成的同时,第二个命令将自动运行。
这是一个特别巧妙的技巧,因为npm run start
通常会打开一个浏览器窗口,吸引我的注意力,让我知道一切都准备好了。相比之下,npm install
是静默完成的。
一旦我掌握了链式命令的窍门,我就开始到处使用它。我经常会把一堆Git命令排列在一起:
git add . && git commit -m "Stuff" && git push origin main
总结
我们在这篇文章中涵盖了很多内容。希望你不会感觉到太大的压力。
终端有一个很好的名声,对初学者来说是令人生畏和棘手的。如果你对它感到费劲,那也是完全正常的。
希望这篇文章至少能缩小你需要学习的范围。可以使用终端做很多事情,但我们可以专注某一部分功能。
如果你觉得这篇文章对你有所帮助,欢迎收藏关注转发~
原文链接:https://www.joshwcomeau.com/javascript/terminal-for-js-devs/#cycling-and-toggling-commands
作者:Joshua Comeau