powershell与CMD的区别
本文最后更新于38 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

在清理 C 盘时,我复制了一条 Sort-Object 的命令到 CMD,结果提示“命令语法不正确”。后来才发现这条命令其实是 PowerShell 的语法。

这不禁引发了我一个疑问,CMD 和PowerShell 到底有什么区别?为什么Windows要设计出两个命令行工具?

首先,从我遇到的问题,也是从两者最本质的区别说起,所有针对CMD返回的命令输出都是String(纯文本),它 将所有输入输出视为无结构的字符串。

想要了解这点,我们必须了解CMD的传输机制:

CMD的传输机制:

在命令行世界里,管道符(Pipe)是一个低调却极其有用的的符号(“|”)

它的作用是“将一个命令的标准输出,重定向为命令的标准输入”,我们可以举个例子:

假设CommandA是产生数据的源头,比如“dir”“type”“netsata” CommandB是数据处理中心,比如“findstr”“sort”“more”

在计算机眼里,CommandA与CommandB是两个独立的进程,但一般在操作系统中,每一个命令(如dir/findstr)运行起来后,都是一个独立的进程,他们之间是存在内存隔离的。

那么如何讲这两个独立的进程联系起来? 就需要管道来作用了。

进程虽然不能互相访问,但它们都有一个共同的“顶头上司”——操作系统内核

管道 | 的实现,本质上是内核利用其特权地位,在两个平行宇宙之间强行开凿了一条隧道

管道的逻辑本质上就是一个文件,前面的进程以写方式打开文件,后面的进程以读方式打开。这样前面写完后面读,于是就实现了通信。即把一个进程的输出直接连接在另外一个进程的输入

但需要注意的是,管道在逻辑上表现得像个文件,但它不落盘,而是由内核在内存中维护的一块环形缓冲区(Circular Buffer)

这是理解 | 运作逻辑的高级视角。在计算机科学中,这叫 Producer-Consumer Pattern

同步进行: A 和 B 是同时运行的。

流量控制(Backpressure):如果进程 A 产生数据太快,管子塞满了,内核会叫 A 歇一会儿(阻塞)。如果进程 B 处理太慢,管子空了,内核会叫 B 等一会儿(挂起)。

只有当进程 A 彻底说完话(发送 EOF 信号)且管道空了,进程 B 才会完成任务并退出。

而“|”是匿名管道,相比于有名管道性能消耗和存在时间更为贴切,这里不进行展开说明。

回到我们的例子,当CommandA被执行时,系统其实知道很多信息:比如dir命令在读取硬盘时,他知道哪个是“文件名”、哪个是“文件大小”;那个是“修改日期”等等。这些都是结构化数据

但为了让数据都可以经过管道,cmd就把这些信息全都变化为了纯文本字符串

而管道另一端的CommandB(比如findstr)拿到的只会有一串字符,也就造成了信息的语义丢失

正因为管道传输的是无结构的字符串,后一个命令如果想做稍微复杂一点的操作(比如:只删除大于 1GB 的文件),就必须进行文本解析

这就好比:

  1. CommandA 把一辆汽车拆成了一堆零件(字符串)扔进管子。
  2. CommandB 必须从这堆零件里,通过数数、匹配形状(文本解析)来猜哪块是发动机。

在 CMD 中,这通常表现为极其恶心的 for /f 语法:

DOS

:: 这是一个典型的“管道 + 文本解析”案例
for /f "tokens=3" %i in ('dir ^| findstr "target.log"') do @echo 文件大小是 %i

所以如果我们不使用管道符,往往在cmd进行的操作都十分有限。

当你发现没有任何一个现成的命令能直接给你要的结果时,管道符就登场了。

想象以下三个“进化”阶段:

第一阶段: 你想看 C 盘有哪些文件。 dir C:\\

第二阶段: 文件太多了,屏幕刷得飞快,你想停下来慢慢看。 dir C:\\ | more此时,你必须用 |dir 的输出“接”给 more 进程,让它帮你分页。

第三阶段: 你想找出 C 盘中所有包含“password”字样的 .txt 文件,并按日期排序。 没有一个 Windows 命令能直接完成这件事。你必须把三个进程焊在一起: dir /s /b *.txt | findstr "password" | sort

进程 A (dir):负责翻箱倒柜找所有后缀是 .txt 的文件。

进程 B (findstr):负责从 A 扔过来的成千上万行文字里,像筛沙子一样筛出带 “password” 的那几行。

进程 C (sort):负责把 B 筛出来的结果重新排个序。

所以如果我们想在cmd里取得文件名,就必须从一堆文字里像裁缝一样用各种复杂的字符串截取命令。这无疑非常麻烦和不方便。

了解了CMD的机制后,想必大家依旧存在很多疑问,比如

CMD为什么要设计的这么麻烦?

这就要说起cmd的历史了:

cmd的前身——MS-DOS诞生于内存只有 640KB 的时代。

我们都知道,微软有一个极其偏执的坚持——向后兼容。为了让1980年代写的.bot批处理文件在2026年的Windows11上依然可以跑,cmd的底层逻辑几乎不敢大动。

这种坚持让cmd变成了一个”拒绝进化“的工具。它保留了过去所有过时的语法(比如晦涩难懂的“%i”和奇怪的“if errorlevel”)

而在CMD设计的年代,开发者认为命令行只是一个启动器的集合:当时的观点是,dir只管列文件,copy只管拷贝文件,命令之间不需要深度交流,只需要返回几行字让用户看明白就好了。

而这种“各扫门前雪”的设计,导致了我们之前讨论过的语义丢失。因为它在设计的时候压根就没有考虑过多个命令串联的情况

关于为什么CMD只传输String,而不传输Object,在当时,内存开销也是一个极大的因素,在当时,传输一个“对象”所需要保存的大量元数据(类型、属性、方法),这些在十几年前是极其奢侈的。

而纯文本字节流对内存的要求几乎为0,虽然解析很麻烦,但它可以在极其简陋的硬件上运行。

不过刚才也说过了,那时还只是内存只有 640KB 的时代,如今电脑内存动辄32GB,面对对象的传输开销已经算不上什么了——

Powershell的诞生

正因为 CMD 这种‘拆车成零件’的传输方式让开发者深陷文本处理的泥潭,微软才决定另起炉灶。如果说 CMD 是在搬运零件(字符串),那么 PowerShell 则是在搬运整车(对象)。它保留了数据的‘活体’属性,让管道符后的命令可以直接通过‘发动机’(属性访问)来操作,彻底终结了繁琐的文本解析时代

PowerShell 不再是一个简单的命令解释器,它本质上是一个基于 .NET 的自动化框架

.NET是什么?

要理解 .NET 为什么是 PowerShell 的核心,必须先看它在 Windows 操作系统中所处的层级

.NET 是微软开发的一个软件开发平台。它不仅仅是一堆代码,而是一个能让程序在上面运行、交流、甚至“跨语言对话”的巨大架构。

它主要由两部分组成:

1.CLR(Common Language Runtime/公共语言进行时)这是一个虚拟机环境,负责管理程序的内存分配、线性执行和安全检查

2.BCL(Base Class Library/基类库)这是一个机器庞大的标准函数库,涵盖了从文件系统操作、网络通信到加密算法的所有底层功能。

PowerShell 并不是独立于系统之外的工具,它本身就是使用 .NET 语言(C#)编写的一个应用程序

举个例子:

当你在 PowerShell 中运行一个命令(Cmdlet)时

在 CMD 中,dir调用的是原始的 Win32 API,返回的是字节流。

在 PowerShell 中,执行Get-ChildItem时,它会调用 .NET 基类库System.IO.Directory.GetFileSystemEntries这个函数,返回的是一个内存指针,指向一组已经格式化好的结构化数据块(即对象实例)

.NET 提供了一种名为反射(Reflection)的技术。它允许一个程序在运行时“查看”另一个数据结构的内部元数据(Metadata)。

当数据流过管道 | 进入 Sort-Object 时,Sort-Object 会利用反射 API 扫描这个数据块。

它能识别出这个数据块里包含 Name (String类型)、Length (Int64类型) 等明确定义的字段。因此,它不需要解析字符串,而是直接对比内存中对应的偏移量数值。

我们可以通过下表对比 CMD 的“文本交换”与 PowerShell 的“.NET 对象交换”在底层逻辑上的区别

特性CMD (基于标准流)PowerShell (基于 .NET 运行时)
数据载体字节序列 (Byte Stream)类型化实例 (Object Instance)
内存表现连续的 ASCII/Unicode 编码堆内存中的结构化对象,带元数据句柄
管道传递将 Stdout 的字节拷贝到 Stdin传递对象引用(内存地址)
验证方式无验证,后续进程需自行猜解类型检查,不符合属性要求的对象会被拦截

通过对底层架构的拆解,我们可以得出结论:CMD 和 PowerShell 的并存,本质上是微软对“兼容过去”与“面向未来”的折衷。

CMD 的定位是“遗留工具”:它的设计初衷是运行简单的可执行文件。由于它只传输无意义的字符串,任何复杂的逻辑处理(如排序、过滤、统计)都必须依赖极高成本的文本解析。这导致了脚本编写极其繁琐,且极易出错。

PowerShell 的定位是“管理框架”:依托于 .NET 运行时的对象模型,它消除了进程间通信的“语义丢失”。管道中传递的是带有元数据的内存指针,这让你可以直接操作数据的属性,而不需要关心数据在屏幕上是怎么显示的。

如果你只是想简单地切换目录(cd)、查看文件(dir)或者运行一个特定的 .exe 程序,CMD 依然能胜任。但如果你需要进行系统清理、自动化运维、或者像我开头提到的“按大小排序文件”这种涉及数据逻辑的操作,PowerShell(甚至是更现代的 PowerShell 7)是唯一的工业级选择。

至此,感谢观看。

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇