[编程达人挑战赛] 用 PowerShell 写了一个“电脑一键初始化脚本”:从混乱到可复制的开发环境

文章目录

[编程达人挑战赛] 用 PowerShell 写了一个"电脑一键初始化脚本":从混乱到可复制的开发环境

每次装新系统,都要重复安装软件、改配置、调环境变量,浪费时间还容易漏。

这篇文章聊聊我是怎么用 PowerShell 写了一个"PC 一键初始化脚本",把这些琐事自动化,并且在实战中踩坑、调优,最后变成一个可以放心分享给同事使用的小工具。


一、为什么我要做一个"一键初始化脚本"?

先说场景:

我日常需要在多台机器上工作(办公室台式机、笔记本、测试机),而且经常重装系统 / 换设备。每次环境搭建都要重复:

  • 安装常用软件(浏览器、Git、IDE、压缩工具、终端工具等);
  • 配置环境变量(Python、Java、Node、Go......看项目吃什么);
  • 调整系统设置(显示"此电脑"图标、显示文件扩展名、关一些开机自启);
  • 配置 Git、SSH key、常用 alias 等。

之前完全靠"记忆 + 手点",问题是:

  • 步骤容易漏(比如忘记装某个 CLI 工具,写代码写到一半才发现);
  • 很难复现"上一台机器上那个刚刚好的状态";
  • 帮同事配环境时全靠嘴讲,对方边听边做,效率极低。

于是我决定做一件事:

用 PowerShell 写一个「一键初始化脚本」,把"我脑子里的那套环境模板"变成可执行代码。


二、整体设计思路:别急着写脚本,先把套路想清楚

一开始我就给自己定了几个约束:

  1. 脚本必须支持重复运行

    • 再跑一次不会把系统搞坏,而是幂等:有的就跳过,没有的就补上。
  2. 配置和逻辑尽量分离

    • 软件列表、下载地址、安装参数不要写死在代码逻辑里,方便以后统一改动。
  3. 需要有"可见反馈"

    • 不只是黑屏滚 log,要让使用者知道"现在在做什么、哪一步失败了"。
  4. 能适应几种常见场景

    • 新系统全新初始化;
    • 已有系统补充安装;
    • 给同事远程指导使用。

因此我最后把脚本拆成了几个模块(逻辑上):

  • 01_EnvCheck.ps1(环境检测与权限检查)
  • 02_SoftInstall.ps1(常用软件安装与配置)
  • 03_SystemTweaks.ps1(系统设置调整)
  • 04_DevConfig.ps1(开发环境与 Git / SSH 等)

主入口 Init-MyPC.ps1 只做调度:加载配置 → 按顺序调用模块 → 输出结果。

在这里插入图片描述(建议放一张"脚本目录结构 + PowerShell 运行界面"的截图)


三、关键实现一:用配置驱动的软件安装(不再写一堆 if)

很多人写"初始化脚本"的第一个版本,都长这样:

powershell 复制代码
# 典型"长成一坨 if"的版本
if (-not (Get-Command git.exe -ErrorAction SilentlyContinue)) {
    # 安装 Git
}

if (-not (Test-Path "C:\Program Files\7-Zip\7z.exe")) {
    # 安装 7zip
}

if (-not (Get-Command code.cmd -ErrorAction SilentlyContinue)) {
    # 安装 VS Code
}

这样写的问题是:
扩展性极差,软件一多,脚本又臭又长,而且换一个下载地址,就要改多处。

我后面改成了 配置 + 通用安装函数 的方式:

3.1 用 JSON/YAML 列表维护软件清单

例如 softwares.json

json 复制代码
[
  {
    "Name": "Git",
    "Check": "git.exe",
    "Type": "winget",
    "Id": "Git.Git"
  },
  {
    "Name": "7zip",
    "CheckPath": "C:\\Program Files\\7-Zip\\7z.exe",
    "Type": "winget",
    "Id": "7zip.7zip"
  },
  {
    "Name": "VSCode",
    "Check": "code.cmd",
    "Type": "winget",
    "Id": "Microsoft.VisualStudioCode"
  }
]

3.2 通用安装函数(以 winget 为例)

powershell 复制代码
function Test-SoftInstalled {
    param(
        [string]$CheckCommand,
        [string]$CheckPath
    )

    if ($CheckCommand) {
        return [bool](Get-Command $CheckCommand -ErrorAction SilentlyContinue)
    }

    if ($CheckPath) {
        return Test-Path $CheckPath
    }

    return $false
}

function Install-SoftFromConfig {
    param(
        [Parameter(Mandatory)]
        [string]$ConfigPath
    )

    $list = Get-Content $ConfigPath | ConvertFrom-Json

    foreach ($soft in $list) {
        Write-Host "==> 检查 $($soft.Name) ..." -ForegroundColor Cyan

        $installed = Test-SoftInstalled -CheckCommand $soft.Check -CheckPath $soft.CheckPath

        if ($installed) {
            Write-Host "    已安装,跳过。" -ForegroundColor Green
            continue
        }

        if ($soft.Type -eq 'winget') {
            Write-Host "    使用 winget 安装:$($soft.Id)" -ForegroundColor Yellow
            winget install --id $soft.Id --silent --accept-package-agreements --accept-source-agreements
        } else {
            Write-Host "    不支持的安装类型:$($soft.Type)" -ForegroundColor Red
        }
    }
}

主脚本调用:

powershell 复制代码
Install-SoftFromConfig -ConfigPath ".\config\softwares.json"

这样有几个好处:

  • 新增软件只需要改 JSON,不用再往脚本里堆 if
  • 支持重复执行:已安装就跳过;
  • 以后要换安装方式(比如某些机器不用 winget 而用本地安装包),只需在配置中增加类型和对应逻辑。

四、关键实现二:系统设置自动化(用脚本替代"狂点控制面板")

我经常会调整这些设置:

  • 桌面显示"此电脑"、"控制面板"等图标;
  • 资源管理器显示文件扩展名、显示隐藏文件;
  • 关闭一些烦人的开机提示。

这些本质上就是注册表 + 一些系统 API 调用,所以我把常用的设置封成函数:

4.1 显示桌面"此电脑"图标(经典需求)

powershell 复制代码
function Enable-DesktopThisPC {
    Write-Host "==> 开启桌面'此电脑'图标" -ForegroundColor Cyan

    $regPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel"
    New-Item -Path $regPath -Force | Out-Null
    # {20D04FE0-3AEA-1069-A2D8-08002B30309D} 是"此电脑"的 CLSID
    Set-ItemProperty -Path $regPath -Name "{20D04FE0-3AEA-1069-A2D8-08002B30309D}" -Value 0

    # 刷新桌面
    rundll32.exe user32.dll,UpdatePerUserSystemParameters
}

4.2 显示文件扩展名

powershell 复制代码
function Show-FileExtensions {
    Write-Host "==> 设置显示文件扩展名" -ForegroundColor Cyan
    $regPath = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"
    Set-ItemProperty -Path $regPath -Name "HideFileExt" -Value 0
}

然后在 03_SystemTweaks.ps1 里统一调用:

powershell 复制代码
Enable-DesktopThisPC
Show-FileExtensions
# 其他 tweaks ...

这一块的"成就感"非常高:点一次脚本,桌面和资源管理器瞬间变成自己熟悉的样子。


五、踩坑与调试:权限、编码、路径,这三件事最容易"阴沟翻船"

5.1 权限:不是所有 PowerShell 都等于"管理员"

一开始我常遇到这样的问题:

  • 在普通 PowerShell 窗口里跑脚本,各种 Set-ItemProperty/winget 调用失败;
  • 提示 Access Denied 或某些安装步骤静默失败。

后来脚本开头我统一加了一个检查:

powershell 复制代码
function Ensure-Admin {
    $currentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = New-Object Security.Principal.WindowsPrincipal($currentIdentity)
    $isAdmin = $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

    if (-not $isAdmin) {
        Write-Host "[ERROR] 请用管理员权限运行此脚本。" -ForegroundColor Red
        Read-Host "按回车键退出..."
        exit 1
    }
}

Ensure-Admin

少了这几行,后面所有报错都是"诈尸";加上之后,问题定位成本直线下降。

5.2 编码与中文路径

我有一台机器的用户名中带有中文,结果脚本里使用硬编码路径时各种炸:

  • C:\Users\某某某\AppData\...
  • 某些工具对非 ASCII 路径支持不好。

解决方案:

  • 一律使用 $env:USERPROFILE / $HOME 等环境变量组成路径;
  • 读取/写入文件时,养成显式声明编码的习惯,例如:
powershell 复制代码
Set-Content -Path $profilePath -Value $profileContent -Encoding UTF8

5.3 路径相对/绝对混用导致"找不到文件"

脚本有时会被放到 U 盘、网络盘或其他路径运行,

如果用相对路径 .\config\xxx 而不切换工作目录,就容易找不到文件。

我最后把入口脚本统一写成这样:

powershell 复制代码
# 取得当前脚本所在路径,并切换到该目录
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
Set-Location $ScriptDir

# 后续路径都基于 $ScriptDir
Install-SoftFromConfig -ConfigPath (Join-Path $ScriptDir "config\softwares.json")

这小小几行能救你无数次"同一脚本在不同机器表现不一样"的折磨。


六、进一步的代码优化:从"能用"到"敢分享"

在基本功能跑通后,我做了几件优化工作,让它更像一个"可以公开分享的作品":

  1. 增加日志输出与日志文件

    • 关键步骤打印到控制台,同时写入 logs\init-YYYYMMDD.log
    • 方便事后排查,比如某步 winget 安装失败、网络中断等。
  2. 参数化执行

    • 支持 -SkipSoft-SkipTweaks 之类参数:

      • 有时我只想跑系统设置,而不想重新检查所有软件;
    • 在入口脚本中解析 $args 或用 param() 定义参数,提升灵活性。

  3. 增加"预检查模式"(Dry Run)

    • 加一个 -WhatIf 模式,只打印"将要安装/修改什么",不做实际变更;
    • 同事在第一次跑时可以先预览,信任感会高很多。

做到这个阶段,我才觉得它从"我的个人脚本"升级成了"可以发到团队、写成教程"的项目。


七、总结:把"搭环境"变成一段可以复用的代码

最后用三句话总结这次项目给我的收获:

  1. 所有重复性的操作,迟早都应该被脚本接管。

    哪怕一开始只自动化 30% 的步骤,对长期效率提升也非常可观。

  2. 写脚本之前先想清楚:幂等性、配置分离、可见反馈。

    这三点想明白了,后面的重构和扩展都会轻松很多。

  3. 把自己的"小工具"做成可以分享的作品,本身就是一次"系统性编码思维训练"。

    你会被迫去考虑别人怎么用、怎么出错、怎么阅读你的代码和说明。


如果你也在为"每次换电脑就从零搭环境"头疼,可以从非常小的一步开始:

先写一个只安装 1~2 个软件 的 PowerShell 脚本,把这个流程跑通,再慢慢加模块、加配置。

等你回头看时,你会发现:

你不仅节省了大量机械劳动,还多了一篇可以参加【编程达人挑战赛】的"项目复盘 & 开发经验总结"博客。

相关推荐
x***13391 小时前
MySQL 篇 - Java 连接 MySQL 数据库并实现数据交互
java·数据库·mysql
v***91301 小时前
PostgreSQL 中进行数据导入和导出
大数据·数据库·postgresql
c***21291 小时前
oracle使用PLSQL导出表数据
数据库·oracle
q***33371 小时前
Spring boot启动原理及相关组件
数据库·spring boot·后端
RoboWizard1 小时前
PCIe 5.0 SSD有无独立缓存对性能影响大吗?Kingston FURY Renegade G5!
人工智能·缓存·电脑·金士顿
合作小小程序员小小店1 小时前
桌面开发,在线%医院管理%系统,基于vs2022,c#,winform,sql server数据
开发语言·数据库·sql·microsoft·c#
霍格沃兹测试开发学社-小明1 小时前
测试左移2.0:在开发周期前端筑起质量防线
前端·javascript·网络·人工智能·测试工具·easyui
懒麻蛇2 小时前
从矩阵相关到矩阵回归:曼特尔检验与 MRQAP
人工智能·线性代数·矩阵·数据挖掘·回归
xwill*2 小时前
RDT-1B: A DIFFUSION FOUNDATION MODEL FOR BIMANUAL MANIPULATION
人工智能·pytorch·python·深度学习