尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

PowerShell与CMD深度对比:从脚本迁移到编码乱码的避坑指南

PowerShell与CMD深度对比:从脚本迁移到编码乱码的避坑指南 1. 从一次脚本迁移说起为什么我要认真对比这两个命令行前阵子帮一个朋友把他那台用了五六年的老机器做系统迁移顺手整理了一下他平时用的自动化脚本。结果发现一个很典型的现象他电脑里躺着几十个.bat文件从清理临时目录到批量重命名照片全是当年从各种论坛抄来的 CMD 脚本。这些脚本在旧机器上跑得好好的换到新系统之后有的直接报错有的中文全变成乱码还有几个涉及网络请求的干脆卡死不动。我问他为什么不换成 PowerShell他说了一句特别有代表性的话“CMD 我熟啊PowerShell 那些命令看着就头大。”这句话其实点出了很多人对这两个工具的认知状态。CMD 是 Windows 从 DOS 时代一路带过来的老伙计命令短、上手快、资料多PowerShell 则是后来居上的“正规军”面向对象、功能强大但学习曲线明显更陡。问题在于现在已经是 Windows 11 的时代了很多新系统里的默认行为、编码规则、权限模型都变了继续用老思路写 CMD 脚本踩坑几乎是必然的。这篇内容我想做的事情很明确把 PowerShell 和 CMD 放在一起从实际使用的角度做一次彻底对比。不是那种“CMD 是命令提示符PowerShell 是脚本语言”的百科式介绍而是围绕真实场景——脚本迁移、编码处理、命令链写法、开机自启、权限管理、外部程序调用——把两者的差异、各自的适用边界、以及迁移过程中最容易翻车的地方讲清楚。如果你平时只是偶尔打开命令行敲两个命令那这篇内容能帮你搞清楚什么时候该用哪个如果你手上有大量历史 CMD 脚本需要维护或者迁移那这篇内容基本可以当作一份避坑手册来用。我会尽量把每个结论背后的原因讲透让你不只是知道“怎么做”而是明白“为什么这么做”。2. 本质差异一个传字符串一个传对象要理解 PowerShell 和 CMD 的所有行为差异得先抓住它们最根本的设计哲学区别。这个区别不理解透后面所有的坑你都会踩得莫名其妙。2.1 CMD 的世界里只有文本CMD 的运作模式非常朴素你输入一行字符串它解析这行字符串找到对应的可执行程序把参数传过去程序输出一堆文本CMD 把这堆文本原样显示在窗口里。整个过程中CMD 自己并不“理解”这些文本的含义它只是个搬运工。举个例子你在 CMD 里执行tasklist它会吐出一张进程列表。这张列表对人来说是可读的但对 CMD 来说它就是一堆字符。如果你想从里面筛选出某个进程只能用findstr这种文本过滤工具去匹配字符串tasklist | findstr chrome这行命令能工作但它本质上是在做“文本匹配”而不是“按属性筛选”。如果某个进程的名字里恰好包含 chrome 但并不是你要找的那个它也会被匹配出来。这就是文本流的局限。2.2 PowerShell 的世界里是结构化对象PowerShell 的设计思路完全不同。它执行Get-Process的时候返回的不是一堆文本而是一组对象。每个对象都有属性Name、Id、CPU、WorkingSet等等。你可以直接按属性筛选、排序、计算Get-Process | Where-Object { $_.CPU -gt 100 } | Sort-Object CPU -Descending这行命令的含义是“找出 CPU 时间超过 100 秒的进程按 CPU 降序排列”。注意这里没有任何文本匹配的动作全是基于对象属性的操作。这就是 PowerShell 强大的根源。这个差异带来的直接后果是CMD 里很多需要“解析文本”才能完成的任务在 PowerShell 里变成了“访问属性”。前者脆弱、依赖输出格式后者稳定、不依赖显示格式。微软官方文档里有一句话总结得很到位PowerShell 的管道传递的是对象CMD 的管道传递的是文本。2.3 这个差异如何影响你的日常操作理解了这个本质差异很多现象就说得通了。比如为什么 PowerShell 里cd到某个目录后ls出来的东西比 CMD 的dir信息更丰富因为Get-ChildItem返回的是文件系统对象每个对象自带几十个属性而dir只是把这些属性格式化成文本给你看。再比如为什么 PowerShell 里执行外部程序比如python、git时输出有时候会“卡住”或者显示不全因为外部程序输出的是纯文本PowerShell 需要把这些文本重新包装成对象才能继续处理这个转换过程在某些情况下会出问题。这是后面要专门讲的一个大坑。提示判断一个命令是 PowerShell 原生命令还是外部程序看它的名字。原生命令遵循“动词-名词”格式比如Get-Process、Set-Location、Remove-Item外部程序通常是单个单词或者带.exe后缀比如python、git、ipconfig。3. 命令语法对照那些看起来一样却完全不同的命令很多人从 CMD 转到 PowerShell 时最容易被“看起来一样”的命令坑到。cd、dir、copy、del这些命令在 PowerShell 里也能用但它们要么是别名要么行为有微妙差异。下面这张表是我整理的高频命令对照建议收藏。功能CMD 命令PowerShell 原生命令PowerShell 中的别名注意事项切换目录cdSet-Locationcd、sl别名可用但跨盘符切换行为不同列出目录dirGet-ChildItemdir、ls、gci别名输出格式与 CMD 不同复制文件copyCopy-Itemcopy、cp别名参数与 CMD 不兼容删除文件delRemove-Itemdel、rm别名不支持 CMD 的某些参数创建目录mdNew-Item -ItemType Directorymd、mkdir别名行为基本一致查看内容typeGet-Contenttype、cat别名可用但编码处理不同查找字符串findstrSelect-String无完全不同的工具不能混用设置变量set VARvalue$VAR value无语法完全不同环境变量%VAR%$env:VAR无引用方式完全不同命令链、;、7.0无版本差异大后面详述3.1 别名是个甜蜜的陷阱PowerShell 为了照顾从 CMD 过来的人给很多原生命令设置了别名。cd是Set-Location的别名dir是Get-ChildItem的别名copy是Copy-Item的别名。这看起来很方便但实际上是个陷阱。因为别名只映射了命令名没有映射参数。你在 CMD 里习惯的copy /y source dest在 PowerShell 里用copy别名执行会直接报错因为Copy-Item不认识/y这个参数。正确的写法是Copy-Item -Force source dest。我个人的建议是在写脚本的时候永远用完整的原生命令名不要用别名。别名是给交互式命令行用的脚本里用别名会让代码可读性变差而且容易在不同版本之间出问题。你可以用Get-Alias查看某个别名对应的真实命令Get-Alias cd Get-Alias dir3.2 变量和环境变量的引用方式这是新手最容易搞混的地方。CMD 里设置变量用set引用用%set MY_PATHC:\Users\test echo %MY_PATH%PowerShell 里设置变量用$引用也用$$MY_PATH C:\Users\test Write-Output $MY_PATH环境变量的差异更大。CMD 里环境变量和普通变量用同一套%语法PowerShell 里环境变量有专门的前缀$env:$env:PATH $env:USERPROFILE这个差异在写跨平台脚本时特别重要。如果你在 PowerShell 脚本里写了%USERPROFILE%它不会被解析只会被当成普通字符串输出。3.3 命令链运算符的版本坑CMD 里用表示“前一个命令成功才执行后一个”用表示“无条件依次执行”用||表示“前一个失败才执行后一个”。PowerShell 在 7.0 版本之前只支持;无条件依次执行不支持和||。如果你在 Windows PowerShell 5.1Windows 10/11 自带的版本里写会直接报语法错误。这是很多从 CMD 迁移过来的脚本翻车的重灾区。从 PowerShell 7.0 开始和||被正式支持了。所以如果你要用这两个运算符要么升级到 PowerShell 7要么用传统写法替代# PowerShell 5.1 中的替代写法 command1 if ($?) { command2 }$?是 PowerShell 的内置变量表示上一条命令是否成功。这个写法在 5.1 和 7.x 里都能用兼容性最好。4. 编码与乱码中文用户绕不开的一道坎中文 Windows 用户在使用命令行时遇到乱码的概率极高。这个问题的根源在于代码页Code Page和字符编码的历史遗留问题。CMD 和 PowerShell 在这方面的表现差异很大处理方式也完全不同。4.1 CMD 的代码页机制CMD 默认使用系统区域设置对应的代码页。简体中文 Windows 的默认代码页是 936GBK。当你执行chcp命令时可以看到当前代码页chcp输出会是活动代码页: 936。这意味着 CMD 默认用 GBK 编码来解读和显示文本。如果你用type命令查看一个 UTF-8 编码的文本文件中文就会变成乱码。解决办法是临时切换代码页到 65001UTF-8chcp 65001但这里有个坑切换代码页之后某些老程序的输出会变得不正常因为它们的输出是按 GBK 编码的现在被当成 UTF-8 来解读了。所以chcp 65001不是万能药它只是把乱码从一个地方转移到另一个地方。4.2 PowerShell 的编码处理PowerShell 的编码处理比 CMD 复杂因为它涉及三个层面控制台输入编码、控制台输出编码、以及文件读写编码。在 Windows PowerShell 5.1 里Get-Content默认按系统 ANSI 编码中文系统就是 GBK读取文件。如果你读一个 UTF-8 文件需要显式指定编码Get-Content -Path test.txt -Encoding UTF8在 PowerShell 7.x 里默认编码变成了 UTF-8无 BOM这个改动让跨平台脚本友好了很多但也带来了新的兼容性问题如果你用 7.x 写了一个脚本里面用Get-Content读 GBK 文件不指定编码就会乱码。输出方面PowerShell 5.1 的控制台输出编码默认跟随系统代码页。你可以通过设置$OutputEncoding和[Console]::OutputEncoding来调整[Console]::OutputEncoding [System.Text.Encoding]::UTF8 $OutputEncoding [System.Text.Encoding]::UTF8这两行是很多中文 PowerShell 脚本的标配建议直接放在脚本开头。4.3 一个真实的乱码排查案例我之前遇到过一个场景一个 CMD 脚本调用 Python 程序Python 输出中文CMD 显示乱码。排查过程是这样的第一步确认 Python 脚本本身的编码。用python -c import sys; print(sys.stdout.encoding)查看 Python 认为的标准输出编码。结果是cp936也就是 GBK。第二步确认 CMD 的代码页。chcp显示 936也是 GBK。理论上应该匹配为什么还乱码第三步检查 Python 脚本文件本身的编码。用十六进制工具打开发现文件是 UTF-8 编码但脚本里没有声明编码。Python 3 默认按 UTF-8 读取源文件但输出时用的是cp936如果脚本里的中文字符串在转换过程中出了问题就会乱码。最终的解决方案是在 Python 脚本开头加上编码声明并且在输出时显式指定编码# -*- coding: utf-8 -*- import sys import io sys.stdout io.TextIOWrapper(sys.stdout.buffer, encodingutf-8)然后在 CMD 里先执行chcp 65001再运行 Python 脚本。这样整条链路的编码就统一到 UTF-8 了。这个案例说明一个道理乱码问题从来不是单一环节的问题而是整条链路上编码不一致导致的。CMD 和 PowerShell 只是链路中的一环排查时要通盘考虑。注意chcp 65001在某些 Windows 版本上会导致命令行窗口的字体显示异常尤其是使用点阵字体的时候。建议把控制台字体改成 TrueType 字体比如 Consolas 或微软雅黑显示效果会正常很多。5. 脚本编写与执行策略从 bat 到 ps1 的迁移CMD 脚本以.bat或.cmd为扩展名PowerShell 脚本以.ps1为扩展名。两者在语法、执行方式、权限模型上都有本质区别。这一章讲迁移过程中最关键的几个问题。5.1 执行策略PowerShell 的第一道门槛很多第一次运行.ps1脚本的人都会遇到这个错误无法加载文件 xxx.ps1因为在此系统上禁止运行脚本。这是 PowerShell 的执行策略Execution Policy在起作用。默认情况下Windows 客户端系统的执行策略是Restricted禁止运行任何脚本。这个设计是为了防止恶意脚本自动执行。查看当前执行策略Get-ExecutionPolicy修改执行策略需要管理员权限Set-ExecutionPolicy RemoteSignedRemoteSigned的含义是本地编写的脚本可以直接运行从网络下载的脚本需要有数字签名才能运行。这是微软推荐的平衡安全性和便利性的设置。这里要特别提醒一点网上有些教程会让你执行Set-ExecutionPolicy Bypass这个设置会完全关闭脚本执行限制安全性极低。我强烈不建议在日常使用的机器上这么做。如果只是临时运行某个脚本可以用-ExecutionPolicy Bypass参数只对那一次执行生效powershell -ExecutionPolicy Bypass -File script.ps1这样既运行了脚本又不会永久改变系统设置。5.2 从 bat 到 ps1 的语法转换下面用一个实际例子来演示迁移过程。假设有一个 CMD 脚本功能是清理指定目录下超过 30 天的临时文件echo off set TEMP_DIRC:\Temp forfiles /p %TEMP_DIR% /s /m *.* /d -30 /c cmd /c del path echo 清理完成对应的 PowerShell 版本$TEMP_DIR C:\Temp $cutoff (Get-Date).AddDays(-30) Get-ChildItem -Path $TEMP_DIR -Recurse -File | Where-Object { $_.LastWriteTime -lt $cutoff } | Remove-Item -Force Write-Output 清理完成对比一下就能看出差异。CMD 版本依赖forfiles这个外部工具参数晦涩日期计算用/d -30这种简写。PowerShell 版本用Get-Date做日期计算用Where-Object做条件筛选逻辑清晰得多而且不依赖外部工具。更重要的是PowerShell 版本可以轻松扩展。比如你想加一个“只删除 .tmp 文件”的条件只需要在Where-Object里加一个判断Get-ChildItem -Path $TEMP_DIR -Recurse -File | Where-Object { $_.LastWriteTime -lt $cutoff -and $_.Extension -eq .tmp } | Remove-Item -ForceCMD 版本要加这个条件就得改forfiles的/m参数而且不支持多个扩展名。5.3 调用外部程序时的引号地狱CMD 和 PowerShell 在调用外部程序时引号的处理规则完全不同这是迁移过程中最容易出问题的地方。CMD 里如果路径包含空格用双引号包起来C:\Program Files\MyApp\app.exe C:\My Documents\file.txtPowerShell 里调用外部程序时如果路径包含空格需要用调用运算符 C:\Program Files\MyApp\app.exe C:\My Documents\file.txt如果不加PowerShell 会把带空格的路径当成字符串而不是命令。这个坑我踩过不止一次。更麻烦的是参数传递。PowerShell 在把参数传给外部程序时会做一层解析有时候会改变参数的原始形式。比如你想传一个包含引号的参数给外部程序可能需要用反引号转义 app.exe quoted argument这种写法可读性极差而且容易出错。我的经验是如果外部程序的参数特别复杂考虑用Start-Process配合参数数组$args (-input, C:\My Documents\file.txt, -output, result.txt) Start-Process -FilePath app.exe -ArgumentList $args -Wait -NoNewWindow-Wait表示等待程序执行完成-NoNewWindow表示在当前窗口运行而不是弹新窗口。这两个参数在脚本自动化里非常常用。6. 权限与提权管理员身份的正确打开方式Windows 的权限模型决定了有些操作必须以管理员身份执行。CMD 和 PowerShell 在提权方面的处理方式不同理解这些差异能帮你避免很多“为什么命令执行失败”的困惑。6.1 CMD 的提权方式CMD 本身没有内置的提权机制。要以管理员身份运行 CMD通常的做法是在开始菜单搜索“cmd”右键选择“以管理员身份运行”。或者在已经打开的 CMD 里用runas命令启动一个新的提权进程runas /user:Administrator cmd但runas需要输入管理员账户密码而且启动的新窗口和当前窗口是独立的环境变量不共享。这在脚本自动化里很不方便。6.2 PowerShell 的提权方式PowerShell 同样没有内置的“自我提权”命令但可以通过Start-Process配合-Verb RunAs参数来启动一个提权的 PowerShell 进程Start-Process powershell -Verb RunAs -ArgumentList -File C:\script.ps1这行命令会弹出一个 UAC 确认框用户确认后会以管理员身份启动一个新的 PowerShell 进程来执行脚本。这里有个重要的细节提权后的进程是一个全新的进程它不会继承当前进程的变量、函数、模块。所以如果你在脚本里定义了一些变量提权后是用不了的。解决办法是把所有需要的东西都写在脚本文件里通过-File参数传递。6.3 判断当前是否具有管理员权限在脚本里经常需要判断当前是否以管理员身份运行。PowerShell 里可以用这个写法$isAdmin ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-not $isAdmin) { Write-Output 需要管理员权限 exit 1 }CMD 里判断管理员权限比较麻烦通常用net session命令的返回值来间接判断net session nul 21 if %errorlevel% neq 0 ( echo 需要管理员权限 exit /b 1 )net session是一个需要管理员权限才能执行的命令如果执行失败errorlevel 非 0说明当前不是管理员。提示在 Windows 11 上UAC 的默认行为是“仅当应用尝试更改我的计算机时通知我”。这意味着即使你以管理员账户登录普通进程也不会自动获得管理员权限。所以提权判断在脚本里是必须的。7. 开机自启与后台运行两种不同的实现路径开机自启是很多自动化脚本的常见需求。CMD 和 PowerShell 脚本在实现自启时思路和工具都不一样。7.1 CMD 脚本的开机自启CMD 脚本实现开机自启最传统的方式是放到“启动”文件夹C:\Users\用户名\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup把.bat文件或者它的快捷方式放进去登录时就会自动执行。这种方式简单直接但有个缺点脚本执行时会弹出一个命令行窗口而且窗口不会自动关闭除非脚本最后加了exit。另一个方式是注册表HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run在这个键下新建一个字符串值数据指向脚本路径。这种方式和启动文件夹效果一样但更隐蔽适合不想让用户轻易发现的场景。7.2 PowerShell 脚本的开机自启PowerShell 脚本实现自启思路类似但因为执行策略的限制不能直接把.ps1文件放到启动文件夹会被执行策略拦截。通常的做法是创建一个.bat或.cmd的启动器在里面调用 PowerShellecho off powershell -ExecutionPolicy Bypass -WindowStyle Hidden -File C:\scripts\startup.ps1-WindowStyle Hidden参数让 PowerShell 窗口隐藏运行不会弹出来打扰用户。这是实现“静默自启”的关键。如果想让脚本在后台持续运行比如定时任务可以用计划任务Task Scheduler$action New-ScheduledTaskAction -Execute powershell.exe -Argument -ExecutionPolicy Bypass -WindowStyle Hidden -File C:\scripts\monitor.ps1 $trigger New-ScheduledTaskTrigger -AtLogOn Register-ScheduledTask -TaskName MyMonitor -Action $action -Trigger $trigger -RunLevel Highest这段代码创建了一个登录时触发的计划任务以最高权限运行 PowerShell 脚本。-RunLevel Highest表示以管理员权限运行适合需要提权的脚本。7.3 静默运行的一个实用技巧如果你想让 CMD 脚本静默运行不显示窗口可以用 VBScript 包装Set WshShell CreateObject(WScript.Shell) WshShell.Run cmd /c C:\scripts\mytask.bat, 0, False保存为.vbs文件双击执行时不会显示任何窗口。Run方法的第二个参数0表示隐藏窗口第三个参数False表示不等待脚本执行完成。PowerShell 脚本的静默运行更简单直接用-WindowStyle Hidden参数就行。但要注意隐藏窗口后脚本里的Write-Host输出就看不到了调试时会很不方便。建议在开发阶段先用可见窗口运行确认没问题后再改成隐藏。8. 外部程序调用Python、Git 等工具的集成现代开发工作流里命令行脚本经常需要调用外部程序比如 Python、Git、Node.js。CMD 和 PowerShell 在这方面的表现差异很大尤其是涉及输出捕获和错误处理的时候。8.1 CMD 调用外部程序CMD 调用外部程序很直接直接写程序名就行python script.py git status捕获输出用重定向python script.py output.txt 2121表示把标准错误也重定向到标准输出这样错误信息也会被写入文件。CMD 里判断外部程序是否执行成功用%errorlevel%python script.py if %errorlevel% neq 0 ( echo 执行失败 )8.2 PowerShell 调用外部程序PowerShell 调用外部程序同样直接写程序名但输出处理方式不同。默认情况下外部程序的输出会被 PowerShell 逐行捕获为字符串数组$output python script.py $output | ForEach-Object { Write-Output 行: $_ }这里有个坑如果外部程序输出大量数据PowerShell 会先把所有输出缓存在内存里然后再赋值给变量。如果输出量特别大比如几百 MB可能会导致内存占用飙升。解决办法是用管道直接处理不要先赋值给变量python script.py | ForEach-Object { ... }另一个坑是错误处理。PowerShell 默认不会把外部程序的非零退出码当成错误$?变量在外部程序返回非零时会是$false但不会抛出异常。要获取退出码用$LASTEXITCODEpython script.py if ($LASTEXITCODE -ne 0) { Write-Error Python 脚本执行失败退出码: $LASTEXITCODE }8.3 一个 Python 与 PowerShell 协作的实际案例我之前做过一个数据处理的自动化流程用 Python 做数据清洗用 PowerShell 做文件调度和结果汇总。核心逻辑是这样的$dataDir C:\Data\incoming $outputDir C:\Data\processed Get-ChildItem -Path $dataDir -Filter *.csv | ForEach-Object { $inputFile $_.FullName $outputFile Join-Path $outputDir $_.Name python C:\Scripts\clean_data.py --input $inputFile --output $outputFile if ($LASTEXITCODE -eq 0) { Write-Output 处理成功: $($_.Name) Move-Item -Path $inputFile -Destination C:\Data\archive\$($_.Name) } else { Write-Error 处理失败: $($_.Name) } }这个脚本展示了 PowerShell 在文件调度方面的优势Get-ChildItem获取文件列表ForEach-Object遍历Join-Path拼接路径Move-Item归档。这些操作如果用 CMD 写需要大量for循环和字符串拼接可读性和可维护性都差很多。注意在 PowerShell 里调用 Python 时如果 Python 脚本输出中文可能会遇到编码问题。建议在 Python 脚本里显式设置标准输出编码为 UTF-8并且在 PowerShell 里设置[Console]::OutputEncoding [System.Text.Encoding]::UTF8。9. 常见故障排查从报错信息反推问题根源这一章整理几个高频故障场景每个场景都给出完整的排查链路方便你遇到类似问题时参考。9.1 “无法将‘set-location’项识别为 cmdlet”这个报错通常出现在你试图用cd命令切换到一个不存在的路径时。PowerShell 的报错信息有时候会让人困惑因为它报的是Set-Locationcd的真实命令名而不是cd。排查步骤确认路径是否存在Test-Path 你的路径确认路径里有没有特殊字符需要转义如果路径包含空格用引号包起来cd C:\Program Files如果是跨盘符切换PowerShell 需要加-LiteralPath参数或者先切换到目标盘符实际上这个报错更常见的原因是路径拼写错误或者路径不存在。PowerShell 的报错信息不够直观容易让人以为是命令本身的问题。9.2 “安装了 Python 但 CMD 里找不到 python 命令”这是环境变量配置问题。Python 安装时有一个选项叫“Add Python to PATH”如果没勾选安装程序不会把 Python 的安装目录加到系统 PATH 环境变量里。排查步骤在 CMD 里执行where python看是否能找到如果找不到手动检查 PATHecho %PATH%找到 Python 安装目录通常在C:\Users\用户名\AppData\Local\Programs\Python\Python3xx把这个目录和它的Scripts子目录加到 PATH 里在 PowerShell 里临时添加 PATH$env:PATH ;C:\Users\用户名\AppData\Local\Programs\Python\Python311永久添加需要用系统设置或者setx命令setx PATH %PATH%;C:\Python311 /M/M表示修改系统级环境变量需要管理员权限。不加/M则修改用户级环境变量。9.3 “cmd 窗口闪退”CMD 脚本双击运行时窗口一闪而过通常是因为脚本执行完毕自动关闭了。解决办法是在脚本最后加pauseecho off echo 正在执行... rem 你的命令 pausepause会显示“请按任意键继续...”等待用户按键后才关闭窗口。这在调试阶段非常有用。如果是 PowerShell 脚本闪退可以在脚本最后加Read-HostRead-Host 按回车键退出9.4 “PowerShell 中的乱码如何处理”前面已经详细讲过编码问题这里补充一个快速排查流程现象可能原因排查命令解决方案中文显示为问号控制台编码不匹配chcpchcp 65001中文显示为方块字体不支持中文查看控制台属性换成 Consolas 或微软雅黑读取文件乱码文件编码与读取编码不一致Get-Content -Encoding显式指定编码输出到文件乱码输出编码设置问题$OutputEncoding设置为 UTF-8外部程序输出乱码外部程序编码与控制台不一致[Console]::OutputEncoding统一设置为 UTF-8这个表格基本覆盖了 90% 的乱码场景。遇到乱码时按表格逐项排查通常几分钟就能定位问题。10. 选型建议什么场景用 CMD什么场景用 PowerShell讲了这么多差异最后回到一个实际问题日常工作中到底该用哪个我的判断标准很简单一次性、简单的系统操作用 CMD需要逻辑判断、数据处理、复杂流程的用 PowerShell。具体来说以下场景适合 CMD快速查看 IP 配置ipconfig测试网络连通性ping、tracert简单的文件复制、移动、删除执行单个外部程序并查看输出在老旧的、只支持 CMD 的环境里工作以下场景适合 PowerShell需要按条件筛选文件或进程需要处理结构化数据CSV、JSON、XML需要编写带逻辑判断和循环的脚本需要调用 REST API 或处理网络请求需要做批量操作和自动化调度需要跨平台兼容PowerShell 7 支持 Linux 和 macOS还有一个现实因素微软已经把 PowerShell 作为 Windows 的主力命令行工具新功能和新 API 都优先在 PowerShell 里支持。CMD 虽然还会长期存在因为大量遗留脚本依赖它但它的功能基本已经冻结了。从长远看投入时间学习 PowerShell 的回报率明显更高。不过我也不建议完全抛弃 CMD。有些场景下 CMD 确实更直接比如你只是想快速看一下本机 IP打开 CMD 敲ipconfig比打开 PowerShell 敲Get-NetIPAddress快得多。工具是拿来用的不是拿来站队的。我个人的习惯是把 PowerShell 作为主力工作环境配置好 profile设置好编码日常的脚本和自动化都用 PowerShell 写。遇到需要快速执行单个命令的场景直接在 PowerShell 里敲因为大部分 CMD 命令在 PowerShell 里也能用通过别名或者直接调用外部程序。只有在维护历史遗留的.bat脚本时才会专门打开 CMD 去调试。如果你正在考虑从 CMD 迁移到 PowerShell我的建议是不要一次性全部迁移。先把最常用、最痛的那几个脚本迁移过来跑通之后再逐步扩展。迁移过程中遇到问题回头查一下这篇内容里的对照表和排查流程大部分坑都能避开。
返回列表