基于 Windows 来认识下命令行界面与文件路径
命令行界面 (command line interface, CLI) 也勉强能当一门“语言”——直接跟操作系统对话的语言。它并不如大部分的正经编程语言那样值得专门性的系统性的学习,却不可避免地出现在学习正经编程语言的路上,因而一些常识性的认知还是必需的。以下知识碎片试图起到这个“扫盲”作用,更深入的命令行知识属于“Shell”这个话题。
对“命令”最简单而不失偏颇的理解大概是,“执行一个功能,并给该功能传入必要的参数”。通常命令的第一个单词即决定了执行哪个功能,而后面的单词则简单地作为参数传递给该功能,由这个功能对参数进行解读并产生实际的执行效果。
命令行概述
命令行界面原本是用户跟 (有屏幕的) 电脑系统进行交互的最原始界面。你每次输进去的 (命令) 是一串字符,系统回给你的输出结果 (画面) 也是一堆字符。这种简单的交互易于设计和实现,只要人为地规定好一套合适的字符集以及每个字符的二进制编码,字符的传入传出就可以轻松愉快地进行。
后来的发展大家也都知道了,形象直观的图形用户界面 (graphical user interface, GUI) 终成为现代操作系统提供给普通用户的第一选择。通过鼠标点击可交互的按钮,系统回馈出信息更丰富更具表达力的图形画面。但即使是在这样的 GUI 系统上,原始的命令行交互功能仍被保留在一个内置的纯字符应用程序中,这就是我们常说的 终端 或 shell 。
终端和 Shell 严格来讲是有区别的,但面向初学者的本文不适合纠缠这个问题。
Windows 10/11 系统预装了两个终端,即 cmd (命令提示符) 和 Windows PowerShell ,在开始菜单的“Windows PowerShell”和“Windows 系统”目录下可以分别找到它们。
cmd 和 powershell 的区别并不算小,但本文着重关注它们作为命令行界面的基础功能,这基本上是共通的。
命令如何被输入并执行
无论是 Windows 上的 cmd 和 powershell,还是 Linux 或 Mac 上的终端程序,这个命令行界面的用法其实大同小异。
各种终端在启动后一般都留下一个 (大多情况是闪烁的) 光标来提示用户输入,光标的前面则是终端环境给出的提示性文字 (这串文字会包含一些有用的即时信息,后面会讲)。
在光标处输入你的命令,回车后命令即被执行
。
有些命令执行时会输出一些内容,这些内容会在下方依序显示出来
。这里以一个简单的
echo abcde
命令来做演示 (该命令的执行效果是把 "abcde" 这串文字再原样输出出来)。
输入的命令如何被解析
一条命令在形式上是一条内部不换行的字符串。终端环境通常按如下步骤解析这条字符串:
-
整条命令
先按空格进行分解
,分成一段或多段独立的子串。例如上面的
echo abcde
,会被分解成echo
和abcde
两段。注意, 连续的多个空格跟一个空格在分词时效果相同 ,也就是说echo abcde
跟echo abcde
是等价的。除空格外也还存在一些特殊作用的字符,但分词时一般先不处理它们; -
分词产生的第一段子串 (即命令开头到第一个空格为止的内容),一般按两种情况进行处理
① 如果包含\
(对于 Windows) 或/
(对于 Linux、Mac),则视为某个 程序文件 的 路径 ,按该路径寻找并准备执行它。错误的路径会导致终端报出类似
系统找不到指定的路径
或
-bash: xxx/yyy: No such file or directory
的错误信息,执行失败
② 如果不包含\
或/
,则判断是否是 内置命令 ,如果是就准备执行
③ 如果既不包含\
或/
,又不是内置命令,则在 系统指定的一组目录里 搜索 同名 的程序并准备执行它,如果搜索遍了也没找到,通常报出类似
'xxxxx' 不是内部或外部命令,也不是可运行的程序或批处理文件
或
xxxxx: command not found
的错误信息,也执行失败 -
如果找到了符合要求的程序或命令,
第二段及往后的每一段子串
(可能是零个或多个),会作为一组参数传递给该程序。
程序在执行时能读取到这组参数,可以让程序接收到额外的信息
。以上面的
echo abcde
为例,其中abcde
就是传递给echo
命令的一条信息,echo
命令处理这条信息的方式是,把它原样又输出出来。其他程序通常基于传递给它的信息实现更多更复杂的功能,这就是具体程序的具体用法了。
文末会给出一些简单命令的具体用例。在那之前,我们还需要补充介绍一下什么叫“路径”,从而完整理解 2.① 所描述的内容。
文件路径
路径其实就是一个
专用于在电脑系统中指定文件的字符串形式
。一条路径通常至少包含一个
路径分隔符
,在 Windows 系统上,规范的分隔符是
\
;在 Linux 和 Mac 上是
/
。由路径分隔符分隔开的各个部分,大体对应于我们已经熟知的 "C盘" "D盘" "文件夹" "文件" 这些概念。以
两条示例路径
D:\c_learning\my-plugin
和
D:\c_learning\ABC.exe
为例,在文件资源管理器中,我们来规范地讲解下它们。
在 Windows 桌面上双击打开 "此电脑",在左侧切换到 "此电脑" 选项,即可看到 "设备和驱动器" 下一堆的 "盘"。
Windows 文件系统的顶级结构,即示例路径的第一部分,固定为一个英文字母 (
A
~
Z
,不分大小写) 加英文冒号
:
的形式,叫做
盘符
。我这里有
C:
D:
E:
F:
G:
五个盘符。示例路径中的
D:\
即是指其中的
D:
盘。盘符应具有唯一标识性,也就是
同一系统下的盘符是不能重复的
。
打开这个
D:
盘,我们会看到一些文件夹。
不难想到示例路径的
D:\c_learning
即是指上图中
c_learning
文件夹。类似盘符,
c_learning
这个文件夹名也要具有唯一标识性,即
同一盘符下的各个文件夹名字也应互不相同
。但不同盘符之间的文件夹不存在这个限制,
C:
盘也可以存在一个
C:\c_learning
文件夹,路径开头的
C:
或
D:
会区分出它们。
继续打开这个
D:\c_learning
文件夹,可以看到里面又有一堆文件夹和文件。
文件夹下相当于又提供了新一级空间
,可以在路径上继续追加新一级文件夹或文件。比如上图中的
my-plugin
文件夹,即是
D:\c_learning\my-plugin
;上途中的
ABC.exe
文件,即是
D:\c_learning\ABC.exe
。这也就是两条示例路径真实所指的东西。
同一文件夹下的各个文件夹和文件仍不应重名
,从而保证路径所指的唯一性。当然,不同文件夹之间互不影响,比如
D:
盘中的另一文件夹
D:\f_learning
下也可以存在
D:\f_learning\my-plugin
和
D:\f_learning\ABC.exe
,完整的路径总是能区分出它们。
你可以在自己电脑的文件管理器中探索文件系统的嵌套结构,从而理解这套基于
\
分隔的路径表示法。值得注意的是,当路径到达了一个文件而非文件夹,比如上面的
D:\c_learning\ABC.exe
,就不能再进一步延伸了,即无法再创建出
D:\c_learning\ABC.exe\xxxx
这样的东西,除非你把已有的
ABC.exe
文件删除并重新创建一个
ABC.exe
文件夹。
文件和文件夹并不存在命名原则上的区别。虽然绝大多数情况下,文件名都是以一个小数点.
加所谓扩展名或后缀名的形式结尾的,比如AAA.exe
BBB.c
CCC.cpp
等,从而暗示出它是一个文件并进一步暗示了它的文件类型。但注意这也仅仅是暗示,在文件路径里这样aaaa.xxx
的名字并不会被特殊对待,并且整个aaaa.xxx
的名字也是视作整体来遵守在同一文件夹下的唯一性,而不是只看.
前面的部分。文件夹也同样可以使用这样的名字,如果你觉得好看。
Windows 文件管理器有个不太好的默认设定,就是隐藏了一些常见类型的文件扩展名,让某些人误以为同一文件夹下可以存在同名文件。建议初学者按下图操作把文件名显示完整从而避免误解。
打开这个选项后你会发现 Windows 系统上没有扩展名的文件是很罕见的,除非你刻意去创建。
文件夹和文件在起名上并无太多限制,除了在同一文件夹下不重名以外 (考虑包含后缀
.xxx
在内的整个名字),还有就是是不能包含
:
\
/
*
?
"
<
>
|
等特殊字符。
:
和
\
是显而易见的,因为它们已经用在盘符和路径分隔符上了。其他几个的特殊性跟终端环境有关,先记住即可。
说到不重名性,Windows 路径中的 26 个英文字母是 不区分大小写 的,下面会提到这是它与 Linux 和 Mac 的一个显著区别。
你可能已经注意到,抛开可能存在的
.xxx
后缀形式不谈,文件夹路径和文件路径并无形式上的区别,通常只有系统真正按路径去访问了才能判断出它到底是文件夹还是文件。在某些特殊场合,为了显式表示一个路径是文件夹而非文件,可以刻意地在结尾追加一个
\
,比如上面
D:\c_learning\my-plugin
就又可以表示为
D:\c_learning\my-plugin\
。而文件是一条路径必然的终点,结尾不会再出现
\
。
Linux 和 Mac 的文件系统与此有些不同,主要区别包括:
1、它们使用/
作为路径分隔符,而不是\
,将以上规则中的\
替换为/
即可理解;
2、它们没有C:
D:
E:
这些盘符,文件系统的顶级结构只有一个根/
,可以理解为只存在一个无名的盘;
3、它们的文件路径区分大小写,也就是说/folder/abc
和/folder/Abc
是两条不同的路径,可以共存。而 Windows 上D:\folder\abc
和D:\folder\Abc
只能存其一,另一个同样有效,但会 跟前一个指向相同 的文件(夹)。
工作目录
以上讲到的所有路径都是
绝对路径
,即总是从顶级结构盘符 (或者 Linux 和 Mac 的根
/
) 开始,按文件夹嵌套一层层地表示到所需的级别。
但很多时候,我们都是固定地在一个特定文件夹里工作,可能已经嵌套了很多层,如果每个涉及到文件的路径都要这样完整表示,显然痛苦而不优雅。于是一个 工作目录 的概念就自然地引出了,并允许我们基于此使用一个更为简明可靠的路径表示法—— 相对路径 。
前面提到“终端环境给出的提示性文字”时,没有详解它的含义。现在重新审视一下它,
显然,它是一条路径,尾随了一个
>
符号。结合前面提到的工作目录的需求,答案呼之欲出,这就是终端启动时默认为我们提供的工作目录。
这串提示符其实可以自定义,cmd 的默认格式是工作目录的路径加一个>
,powershell 的提示符就与此不同,前面多了个PS
,后面还多个空格。这些只是视觉区别,暂不细纠。
规范的相对路径以
.\
开头。
.
即直接代替了当前的工作目录
。比如在上图这个环境下,要访问
C:\Users\Zeng\AAA.exe
这个文件 (如果存在的话),则可以直接使用
.\AAA.exe
而非完整冗长的绝对路径 (还可以避免把工作目录路径敲错导致找不到或者找错文件)。如果是工作目录下的嵌套文件夹,也可以让相对路径进一步加长来表示。比如
C:\Users\Zeng\folder\BBB.c
可以简化成
.\folder\BBB.c
。
很多情境下,开头这个
.\
可以省略,直接从工作目录下的文件(夹)名写起。如果你偏爱这么做,请
在遇到问题时回退到规范的做法
。
显然,当我们只处理工作目录下的文件时,相对路径能带来很大的方便。
你可能已经想到了 切换工作目录 的需求,和能否 以相对路径访问工作目录以外的文件(夹) 的疑问,下文会回答你。
命令行用法示例
cd
命令
基于工作目录下使用相对路径时,会很自然地产生如何更改工作目录的疑问,显然我们不能永远只在默认启动的那个目录下工作。
更改工作目录的方法是一个内置命令
cd
(
c
hange
d
irectory),既作为这个问题的回答,也作为一个重要的基础示例来实际演示下命令行的操作。
cd
命令可以接受一个参数 (回忆下命令的格式,我们应用空格来分隔参数与要执行的功能),这个参数即是你希望切换到的新工作目录的路径,既可以是绝对路径也可以是相对路径。当然,要是在连续使用
cd
命令时使用相对路径,要注意工作目录是随之变化的。另一个值得注意的点是,该路径指示的目录必须已存在且你有访问权限,如下图所示 (这里用 powershell 而非 cmd 做演示,因为 cmd 的
cd
命令存在一些怪癖)
cd
命令成功执行后通常并无输出,但从提示符的变化,你可以清楚地看到我们实现了工作目录的更改。
上图还展示了一个
cd ..
的用法,效果上它将工作目录切换到了当前目录的上一级目录。这并不是
cd
命令的特殊用法,而是暗示了以
..\
开头的另一种相对路径。类似于使用
.
代表当前工作目录,
..
则代表了当前目录的父级目录
。所以,当使用
cd .\my-plugin\
进入了
my-plugin
目录下后,使用
cd ..
就又让我们退了出来。
只要理解了工作目录与文件路径的概念,主流终端的
cd
都如上面那样简单。
mkdir
命令
前面提到
cd
要切换到的目录必须事先存在,否则命令将执行失败。我们固然可以提前在文件管理器里先建好需要的文件夹再开始在命令行工作,但显然,如果能直接在命令行里创建新目录 (文件夹),那将更有效率。
在命令行里新建文件夹的方法是内置命令
mkdir
(
m
a
k
e
dir
ectory)。它的用法也很简单,传给它一个尚不存在的文件夹路径作为参数即可,该路径可以是绝对的或相对的。
但注意,有些环境下,
mkdir
不支持一次性创建连续嵌套的多层文件夹,只能在已经存在的文件夹下新建一层文件夹。也就是描述待创建目录的路径,除了表示新文件夹名字的最后一级以外,其余各级必须已经存在了。
gcc 编译器
除了内置命令,还有些第三方的程序也可以安装在系统中并在命令行使用。这里粗糙地介绍一个并不算简单的程序,
gcc
。
一个验证
gcc
是否装好的方法就是尝试性地调用一下它。许多命令行程序都会支持一个特殊的参数
--version
,这可以让该程序输出自己当前的版本号。我们就用这个参数来在命令行调用一下
gcc
,即
gcc --version
如果是类似下面的一坨输出,甭管看不看得懂,至少说明
gcc
被成功调用了,即已正确安装
gcc (x86_64-win32-seh-rev0, Built by MinGW-W64 project) 12.2.0
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
如果gcc没有被正确安装,系统应该找不到它,报出类似下面的错误
'gcc' 不是内部或外部命令,也不是可运行的程序
或批处理文件。
或
gcc: command not found
即“输入的命令如何被解析”②.3 提到的执行错误的情况。
假设
gcc
已经正确安装。
gcc
是一个编译器,即一个
读取源代码文件,输出编译好的程序文件
的程序。所以最简单的用法是,传给它一个代码文件的路径,即
gcc 要编译的代码的路径
更多用法与在实际编程环境中的演示 (包括
gcc
的安装方法),可以参见下文