# ============================================================ # Z · 工具台 — Windows 新电脑预装脚本 win-setup.ps1 # 网页:https://z.cohenjikan.com/setup/ # # 用法(先看清楚再运行): # irm https://z.cohenjikan.com/setup/win-setup.ps1 | iex # 或下载后: # powershell -ExecutionPolicy Bypass -File .\win-setup.ps1 # # 做的事:winget 装常用软件 → Git 用户信息 → SSH 密钥 → pip 包 → pnpm # 幂等:已装的跳过,已有的配置不覆盖,可重复运行。 # 不做的事:不改系统执行策略,不改注册表。个别安装器会弹 UAC,点是即可。 # ============================================================ # 只对当前进程放开执行策略,不动系统设置 try { Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force -ErrorAction Stop } catch { } $ErrorActionPreference = 'Continue' function Step([string]$m) { Write-Host ''; Write-Host ('== ' + $m) -ForegroundColor Cyan } function Skip([string]$m) { Write-Host (' 跳过 ' + $m) -ForegroundColor DarkGray } function Done([string]$m) { Write-Host (' 完成 ' + $m) -ForegroundColor Green } function Fail([string]$m) { Write-Host (' 失败 ' + $m) -ForegroundColor Red } function Has([string]$cmd) { return [bool](Get-Command $cmd -ErrorAction SilentlyContinue) } # winget 改的是注册表里的 PATH,当前窗口不会自动刷新;装完后手动合并一次 function Update-SessionPath { $m = [Environment]::GetEnvironmentVariable('Path', 'Machine') $u = [Environment]::GetEnvironmentVariable('Path', 'User') $env:Path = (@($m, $u) | Where-Object { $_ }) -join ';' } # Windows 自带一个假 python.exe(打开商店用的),要绕开它 function Get-PythonCommand { if (Has 'py') { return 'py' } $c = Get-Command python -ErrorAction SilentlyContinue if ($c -and $c.Source -notlike '*\WindowsApps\*') { return 'python' } return $null } Write-Host '' Write-Host 'Z · 工具台 — Windows 预装' -ForegroundColor Cyan Write-Host '将要:winget 装 8 个软件 → Git 用户名与邮箱 → SSH 密钥 → pip 包 → pnpm' Write-Host '已装的跳过,已有的配置不覆盖。' $null = Read-Host '按回车开始,Ctrl+C 取消' # ---------- 1. winget 装软件 ---------- Step 'winget 安装常用软件' if (-not (Has 'winget')) { Fail 'winget 不可用。先在 Microsoft Store 里更新「应用安装程序」,再重新运行。' return } $packages = @( @{ Id = 'Python.Python.3.12'; Name = 'Python 3.12' } @{ Id = 'OpenJS.NodeJS.LTS'; Name = 'Node.js LTS' } @{ Id = 'Git.Git'; Name = 'Git' } @{ Id = 'Gyan.FFmpeg'; Name = 'FFmpeg' } @{ Id = 'Microsoft.VisualStudioCode'; Name = 'VS Code' } @{ Id = '7zip.7zip'; Name = '7-Zip' } @{ Id = 'Microsoft.PowerToys'; Name = 'PowerToys' } @{ Id = 'Microsoft.WindowsTerminal'; Name = 'Windows Terminal' } ) foreach ($p in $packages) { # 已装的跳过:winget list 找不到时退出码非 0 winget list -e --id $p.Id --accept-source-agreements *> $null if ($LASTEXITCODE -eq 0) { Skip ($p.Name + ' 已安装'); continue } Write-Host (' 安装 ' + $p.Name + ' ...') winget install -e --id $p.Id --accept-package-agreements --accept-source-agreements if ($LASTEXITCODE -eq 0) { Done $p.Name } else { Fail ($p.Name + ',退出码 ' + $LASTEXITCODE) } } Update-SessionPath # ---------- 2. Git 用户信息 ---------- Step 'Git 用户信息' $gitEmail = '' if (Has 'git') { $gitName = git config --global user.name if ([string]::IsNullOrWhiteSpace($gitName)) { $gitName = Read-Host ' Git 用户名 user.name(留空跳过)' if ([string]::IsNullOrWhiteSpace($gitName)) { Skip 'user.name 未设置' } else { git config --global user.name $gitName; Done ('user.name = ' + $gitName) } } else { Skip ('user.name 已是 ' + $gitName) } $gitEmail = git config --global user.email if ([string]::IsNullOrWhiteSpace($gitEmail)) { $gitEmail = Read-Host ' Git 邮箱 user.email(留空跳过)' if ([string]::IsNullOrWhiteSpace($gitEmail)) { Skip 'user.email 未设置' } else { git config --global user.email $gitEmail; Done ('user.email = ' + $gitEmail) } } else { Skip ('user.email 已是 ' + $gitEmail) } } else { Fail 'git 不在 PATH 里。重开一个终端再运行一次。' } # ---------- 3. SSH 密钥 ---------- Step 'SSH 密钥 ed25519' $sshDir = Join-Path $HOME '.ssh' $keyFile = Join-Path $sshDir 'id_ed25519' if (Test-Path $keyFile) { Skip ($keyFile + ' 已存在') } elseif (-not (Has 'ssh-keygen')) { Fail '没有 ssh-keygen。Windows 10/11 自带 OpenSSH 客户端,可在「可选功能」里开启。' } else { if (-not (Test-Path $sshDir)) { New-Item -ItemType Directory -Path $sshDir | Out-Null } $comment = if ([string]::IsNullOrWhiteSpace($gitEmail)) { $env:USERNAME + '@' + $env:COMPUTERNAME } else { $gitEmail } # 空口令。PowerShell 5 会把空字符串参数整个丢掉,要写成 '""';PowerShell 7 直接传 '' if ($PSVersionTable.PSVersion.Major -ge 7) { ssh-keygen -q -t ed25519 -C $comment -f $keyFile -N '' } else { ssh-keygen -q -t ed25519 -C $comment -f $keyFile -N '""' } if (Test-Path ($keyFile + '.pub')) { Done '已生成。公钥如下,可加到 GitHub 或服务器:' Write-Host (' ' + (Get-Content ($keyFile + '.pub'))) } else { Fail '生成失败' } } # ---------- 4. Python 包 ---------- Step 'Python 包' $pipPkgs = @('pip', 'yt-dlp', 'pymupdf', 'pillow', 'python-docx', 'fastapi', 'uvicorn') $py = Get-PythonCommand if ($py -eq 'py') { py -3 -m pip install -U $pipPkgs } elseif ($py -eq 'python') { python -m pip install -U $pipPkgs } if ($py) { if ($LASTEXITCODE -eq 0) { Done ('pip 包已就绪:' + ($pipPkgs -join ' ')) } else { Fail ('pip 退出码 ' + $LASTEXITCODE) } } else { Fail 'python 不在 PATH 里。重开一个终端再运行一次。' } # ---------- 5. pnpm ---------- Step 'pnpm' if (Has 'pnpm') { Skip 'pnpm 已安装' } elseif (Has 'npm') { npm i -g pnpm if ($LASTEXITCODE -eq 0) { Done 'pnpm' } else { Fail ('npm 退出码 ' + $LASTEXITCODE) } } else { Fail 'npm 不在 PATH 里。重开一个终端再运行一次。' } # ---------- 完成 ---------- Step '完成' Write-Host ' 重新打开终端让 PATH 生效。VS Code 与 Windows Terminal 在开始菜单里。' Write-Host ''