Windows 11 开发环境
-
-
Save xianghongai/6e7a0c166fdecba87b1705cd0d250c03 to your computer and use it in GitHub Desktop.
安装 PowerShell 7:winget install --id Microsoft.PowerShell --source winget ,或者在 https://learn.microsoft.com/en-us/powershell/scripting/install/install-powershell-on-windows?view=powershell-7.5 下载 MSI 文件安装。
将下面内容保存到 update_gh_hosts.ps1,管理员权限运行 PowerShell 7,进入到目录执行 .\update_gh_hosts.ps1。
# 必须以管理员身份运行
$currentPrincipal = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)) {
Write-Host "错误:请右键点击脚本并选择 '以管理员身份运行' (Run as Administrator)" -ForegroundColor Red
Start-Sleep -Seconds 3
exit
}
function Update-GitHubHosts {
# 无法访问则临时在 Terminal 中设置代理,例如:
# $env:HTTP_PROXY="http://<proxy>:<port>"
# $env:HTTPS_PROXY="http://<proxy>:<port>"
# 可选的 Hosts 源列表:
# https://raw.githubusercontent.com/ittuann/GitHub-IP-hosts/main/hosts
# https://raw.githubusercontent.com/maxiaof/github-hosts/refs/heads/master/hosts
$hostsUrl = "https://github-hosts.tinsfox.com/hosts"
$hostsPath = "$env:windir\System32\drivers\etc\hosts"
$startMark = "# GITHUB_HOSTS_START"
$endMark = "# GITHUB_HOSTS_END"
Write-Host "正在从网络获取最新的 GitHub Hosts..." -ForegroundColor Cyan
# 1. 下载并检查
try {
# 相当于 curl
$response = Invoke-WebRequest -Uri $hostsUrl -UseBasicParsing -TimeoutSec 10 -ErrorAction Stop
$rawContent = $response.Content
}
catch {
Write-Host "获取失败,请检查网络连接: $($_.Exception.Message)" -ForegroundColor Red
return
}
# 2. 清理格式:去除首尾空行,并确保内容不为空
# 相当于 awk 'NF' 和 [ ! -s ... ]
if ([string]::IsNullOrWhiteSpace($rawContent)) {
Write-Host "获取的内容为空,取消更新" -ForegroundColor Red
return
}
$cleanContent = $rawContent.Trim()
# 3. 更新 hosts 文件
Write-Host "在写入 $hostsPath ..." -ForegroundColor Yellow
if (-not (Test-Path $hostsPath)) {
New-Item -Path $hostsPath -ItemType File -Force | Out-Null
}
# 读取现有内容 (作为单一字符串处理,方便正则替换)
$currentHosts = Get-Content -Path $hostsPath -Raw -ErrorAction SilentlyContinue
if ($null -eq $currentHosts) { $currentHosts = "" }
# 删除旧的标记块 (相当于 sed -i "/start/,/end/d")
# 使用正则表达式匹配 Start 到 End 之间的所有内容(包括换行符)
$pattern = "(?s)\s*$startMark.*$endMark"
$hostsWithoutBlock = $currentHosts -replace $pattern, ""
# 构建新内容:保留旧内容 + 新标记块
# `r`n 是 Windows 的换行符
$newBlock = "`r`n`r`n$startMark`r`n$cleanContent`r`n$endMark"
$finalContent = $hostsWithoutBlock.TrimEnd() + $newBlock
try {
# 写入文件,使用 UTF8 编码防止乱码
Set-Content -Path $hostsPath -Value $finalContent -Encoding UTF8 -Force
}
catch {
Write-Host "写入失败,请检查文件权限或杀毒软件拦截" -ForegroundColor Red
return
}
# 4. 刷新 DNS 缓存
Write-Host "正在刷新 DNS 缓存..." -ForegroundColor Cyan
ipconfig /flushdns | Out-Null
Write-Host "GitHub Hosts 更新成功!" -ForegroundColor Green
# 尝试显示最后几行确认更新 (类似于 tail)
$lastLines = Get-Content -Path $hostsPath -Tail 5
$updateLine = $lastLines | Select-String "数据更新时间"
if ($updateLine) {
Write-Host $updateLine -ForegroundColor Gray
} else {
$date = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Host "更新于: $date" -ForegroundColor Gray
}
}
# 执行函数
Update-GitHubHosts
# 暂停一下以便用户看到结果
Write-Host "`n按任意键退出..."
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")winget install --source winget --exact --id Google.Chrome
# winget install --source winget --exact --id Google.Chrome.Canary
# winget install --source winget --exact --id Mozilla.Firefox
winget install --source winget --exact --id Microsoft.VisualStudioCode
winget install --source winget --exact --id Microsoft.VisualStudioCode.Insiders
# winget install --source winget --exact --id Anysphere.Cursor
# winget install --source winget --exact --id Codeium.Windsurf
# winget install --source winget --exact --id ByteDance.Trae
# winget install --source winget --exact --id appmakes.Typora
winget install --source winget --exact --id Bruno.Bruno # 替代 Postman,不联网,资产可自行用 VCS 托管
winget install --source winget --exact --id Git.Git
winget install --source winget --exact --id Yarn.Yarn
winget install --source winget --exact --id Schniz.fnm
winget install --source winget --exact --id pnpm.pnpm
winget install --source winget --exact --id astral-sh.uv
winget install --source winget --exact --id Gyan.FFmpeg
winget install --source winget --exact --id 7zip.7zip
winget install --source winget --exact --id NickeManarin.ScreenToGif
winget install --source winget --exact --id liule.Snipaste
winget install --source winget --exact --id gurnec.HashCheckShellExtension
# winget install --source winget --exact --id GnuPG.Gpg4win
# winget install --source winget --exact --id CPUID.CPU-Z
winget install --source winget --exact --id agalwood.Motrix
winget install --source winget --exact --id RandyRants.SharpKeys
winget install --source winget --exact --id AutoHotkey.AutoHotkey
winget install --source winget --exact --id hluk.CopyQ
# winget install --source winget --exact --id Docker.DockerDesktop
# winget install --source winget --exact --id Obsidian.Obsidian
# winget install --source winget --exact --id DBeaver.DBeaver.Community
winget install --source winget --exact --id Microsoft.PowerShell
# winget install --source winget --exact --id JanDeDobbeleer.OhMyPosh # 极耗资源
# winget install --source winget --exact --id kangfenmao.CherryStudio
# winget install --source winget --exact --id Rime.Weasel
# winget install --source winget --exact --id OlegShparber.Zeal
winget install --source winget --exact --id SumatraPDF.SumatraPDF
# winget install --source winget --exact --id OBSProject.OBSStudio
# winget install --source winget --exact --id Microsoft.PowerToys
# winget install --source winget --exact --id WiresharkFoundation.Wireshark
# winget install --source winget --exact --id XK72.Charles
# winget install --source winget --exact --id JGraph.Draw
# winget install --source winget --exact --id Xmind.Xmind
# winget install --source winget --exact --id Axure.AxureRP.11
# winget install --source winget --exact --id Figma.Figma
# winget install --source winget --exact --id Figma.FigmaAgent
# winget install --source winget --exact --id Tencent.TencentMeeting
# winget install --source winget --exact --id Tencent.WeChat
# winget install --source winget --exact --id Tencent.TIM
# winget install --source winget --exact --id Alibaba.DingTalk
# winget install --source winget --exact --id ByteDance.Feishu
# winget install --source winget --exact --id Kingsoft.WPSOffice
# winget install --source winget --exact --id ClashVergeRev.ClashVergeRev
# winget install --source winget --exact --id v2rayA.v2rayA
# winget install --source winget --exact --id Telegram.TelegramDesktop
# winget install --source winget --exact --id Oracle.JDK.21
# winget install --source winget --exact --id Oracle.JDK.17
# winget install --source winget --exact --id EclipseAdoptium.Temurin.21.JDK
# winget install --source winget --exact --id EclipseAdoptium.Temurin.17.JDK
# winget install --source winget --exact --id JetBrains.IntelliJIDEA.Ultimate
# winget install --source winget --exact --id JetBrains.DataGrip
# winget install --source winget --exact --id JetBrains.PyCharm
# winget install --source winget --exact --id JetBrains.WebStorm
# winget install --source winget --exact --id Youqu.ToDesk
# winget install --source winget --exact --id TeamViewer.TeamViewer
# winget install --source winget --exact --id NetEase.CloudMusic可以列模式加一下 --accept-package-agreements --accept-source-agreements,说是能自动接受软件包本身的许可协议、自动接受软件源的协议条款。
低版本 Windows 首次 WinGet 不可用,需要在 PowerShell 中执行 Add-AppxPackage -RegisterByFamilyName -MainPackage Microsoft.DesktopAppInstaller_8wekyb3d8bbwe 触发 Microsoft Store 在异步过程中注册 Windows 程序包管理器。
然后同步 VS Code、Chrome、Firefox 配置;
下面着重说一下 Git 配置,尤其是 Windows 上的换行符配置:
~/.gitconfig
[user]
name = 你的名字
email = 你的邮箱@qianxin.com
[init]
defaultBranch = main
[core]
quotePath = false
# 多平台换行符
## 提交混合换行符的文件
# 1. 拒绝 Commit
safecrlf = false
# 2. 允许 Commit
# safecrlf = false
# 3. 警告 Commit
# safecrlf = warn
## 转换换行符
# 1. Commit 时转换为 LF,Checkout 时转换为 CRLF
# autocrlf = true
# 2. Commit 时转换为 LF,Checkout 时不转换
autocrlf = input
# 3. Commit Checkout 均不转换
# autocrlf = false
## 统一换行符为 LF (默认,同操作系统)
eol = lf
# 使用 Vim 交互式编辑
editor = vim
# 分页显示模式
# less:退出后不留下内容,默认
# more:退出后留下内容
# 为空,不分页
pager = more
[alias]
# 所有 log,提交作者,按 Name 形式展现
lg = log --color --date=format:'%Y-%m-%d %H:%M:%S' --abbrev-commit --pretty=format:'%Cred%h%Creset %Cgreen%cd %C(yellow)%d%Creset %C(bold blue)%an%Creset %s'
[gui]
encoding = utf-8
[i18n "commit"]
encoding = utf-8
[i18n]
logoutputencoding = utf-8试过 PowerToys,也试过单独用 AutoHotkey 改 Ctrl/Alt/Win,但是,主观感觉下面方案较稳定。
1、采用 SharpKeys 使得在 Windows 环境按键与 macOS 一致~
| Map this key: | To this key: |
|---|---|
| Special: Left Alt (00_38) | Special: Left Ctrl (00_1D) |
| Special: Left Ctrl (00_1D) | Special: Left Windows (E0_5B) |
| Special: Left Windows (E0_5B) | Special: Left Alt (00_38) |
| Special: Right Alt (E0_38) | Special: Left Windows (E0_5C) |
然后 Write to Regist、Save Keys,
或者直接导入配置文件:macOS_Like_SharpKeys.skl;
配置之后,重启生效。
2、采用 AutoHotkey 改一些输入字符~
Windows 10 下载 Windows10AutoHotkey.ahk 保存到 C:\Users\Administrator\Documents\AutoHotkey 目录后,直接双击使用,目前测试转成 .exe 不能运行。
Windows 11 下载 Windows11AutoHotkey.ahk 和可执行文件 Windows11AutoHotkey.exe 到 C:\Users\Administrator\Documents\AutoHotkey 目录,然后,将可执行文件右键创建一个快捷方式并剪切,Windows + R 输入 shell:Startup 进入 C:\Users\Administrator\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup 开机启动目录,粘贴。
具体改了哪些,看源码注释,这里粘贴 Windows 11 中的源码:
#Requires AutoHotkey v2.0
#SingleInstance Force
#NoTrayIcon ; 隐藏托盘图标(可选)
; (1) 采用 CapsLock 切换输入法
; 完全禁用 CapsLock 的大小写功能
SetCapsLockState "AlwaysOff"
; 将 Caps Lock 映射为 Win+Space
CapsLock::
{
Send "{LWin down}{Space down}"
Sleep 50
Send "{Space up}{LWin up}"
}
; (2) 输出英文状态下的字符
; 反引号 `
`:: Send "{ASC 96}" ; 映射为 ASCII 码 (防止输入法干扰)
; `::
; {
; SendText "``"
; return
; }
; 左方括号
[:: Send "{ASC 91}" ; 映射为 ASCII 码 (防止输入法干扰)
; [::
; {
; SendText "["
; return
; }
; 右方括号
]:: Send "{ASC 93}" ; 映射为 ASCII 码 (防止输入法干扰)
; ]::
; {
; SendText "]"
; return
; }
; 左圆括号 (Shift+9)
+9:: Send "{ASC 40}" ; 映射为 ASCII 码 (防止输入法干扰)
; $+9::
; {
; SendText "("
; return
; }
; 右圆括号 (Shift+0)
+0:: Send "{ASC 41}" ; 映射为 ASCII 码 (防止输入法干扰)
; $+0::
; {
; SendText ")"
; return
; }
; 竖线 | (Shift+\)
$+\::
{
SendText "|"
return
}
; (3) 与 Mac 下的按键行为保持一致
; Ctrl+Left -> Home (跳转到行首)
^Left:: Send "{Home}"
; Ctrl+Right -> End (跳转到行尾)
^Right:: Send "{End}"
; Ctrl+Up -> Ctrl+Home (跳转到文档开头)
^Up:: Send "^{Home}"
; Ctrl+Down -> Ctrl+End (跳转到文档结尾)
^Down:: Send "^{End}"
; Ctrl+Tab -> Alt+Tab (窗口切换)
^Tab::
{
Send "{Alt down}{Tab}"
KeyWait "Ctrl" ; 等待 Ctrl 键释放
Send "{Alt up}"
}
; Ctrl+Shift+Tab -> Alt+Shift+Tab (反向窗口切换)
^+Tab::
{
Send "{Alt down}{Shift down}{Tab}{Shift up}"
KeyWait "Ctrl" ; 等待 Ctrl 键释放
Send "{Alt up}"
}
; Ctrl+Tab 和 Alt+Tab 不能双向映射,下面的映射会冲突,不要尝试
; Alt+Tab -> Ctrl+Tab (应用内标签页切换)
; Alt+Shift+Tab -> Ctrl+Shift+Tab (反向标签页切换)不要在跨文件系统路径上进行开发工具链「写密集型」操作:
| 操作 | 路径 | 后果 |
|---|---|---|
| Git 操作 | Windows → /mnt/wsl |
极慢 |
yarn install |
Windows → Linux FS | 极慢 |
| Node Modules 写入 | NTFS → ext4 | 文件系统语义转换 |
这不是慢一点,而是数量级慢。
例如这些场景尤其要注意:在 Windows 中准备好物料,然后通过资源管理器拷贝,跨文件系统,不推荐拷贝大文件、多文件。
Windows WSL Settings C:\Users\Administrator.wslconfig
[wsl2]
networkingMode=nat
dnsTunneling=true
autoProxy=true
firewall=false
localhostForwarding=true
swap=8589934592
memory=8589934592(1)、正向代理中间件
不绕过公司安全控制的工具,采用本地中间件 (Middleware),用于让 WSL 中的开发工具复用 Windows 已部署的零信任网络能力。
所有外部网络访问仍然由 Windows 网络栈发起,并被 TrustAgent 统一拦截、审计和控制,不具备加密、转发、隐藏流量或绕行策略的能力。
WSL Tool ──HTTP CONNECT──> Middleware ──TCP──> Windows Network Stack
↓
TrustAgent (WFP)
也就是说:
仅作为本机协议适配组件,本身不具备访问企业内网的能力,将 WSL 中的出站请求转交给 Windows 网络栈发起,所有流量仍由 TrustAgent 统一拦截、加密和审计。
中间组件不具备任何绕行、混淆或跨网络转发能力,也不引入新的网络出口,仅用于保证开发工具在受控网络环境下的可用性。
(2)、中间件的合规关注点
所有流量:不加密不混淆、不支持远程访问、不支持认证绕过,仍由 Windows 发起、仍经 TrustAgent 拦截、仍进入统一审计体系 (让原本“不可审计的 WSL 流量”,重新回到“可审计路径”)。
| 合规关注点 | Middleware 的事实 |
|---|---|
| 合规关注点 | Middleware 的事实 |
| 绕过 TrustAgent | 不可能 |
| 隐藏真实目标 | 不支持 |
| 自定义加密 | 不具备 |
| 流量混淆 | 不涉及 |
| 策略绕行 | 无策略能力 |
(3)、中间件与第三方代理工具的区分
只解决“进程归属”问题,不解决“网络通行”问题。
| 项目 | gost / ccproxy | Middleware |
|---|---|---|
| 项目 | gost / ccproxy | Middleware |
| 是否跨网络边界 | Y | N |
| 是否绕策略 | Y(能力上) | N |
| 是否具备转发能力 | Y | N |
| 是否可配置多跳 | Y | N |
| 定位 | 通信工具 | 本地适配器 |
底线判断:是“合规增强”,而不是“合规风险”。
微软一直主打面向对象,PowerShell 中,一切都是.NET 对象,命令(Cmdlet)输出的不是文本,而是一个或一系列结构化的对象,这些对象拥有属性和方法。
有别于 Unix/Linux 一切皆文本。
- 传统管道:
文本流 A | 工具B(处理文本) | 工具C(处理文本) - PowerShell 管道:
对象A | CmdletB(处理对象) | CmdletC(处理对象)
例如 Get-Process | Where-Object { $_.WorkingSet -gt 100MB } | Sort-Object -Property Name | Select-Object 获取所有正在运行的、内存使用超过 100MB 的进程名,并排序,记不住,好长。
做之前在 PowerShell 7 中以管理员身份执行以下命令:
# 允许本地未签名脚本
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser如果你有特殊需求,可以参考下表选择其他级别:
| 策略级别 | 说明 |
|---|---|
| Restricted | 默认设置。禁止运行任何脚本,仅允许运行单个命令。 |
| RemoteSigned | (推荐) 本地脚本无需签名;下载的脚本必须签名。 |
| AllSigned | 所有脚本(包括本地脚本)都必须由受信任的发布者签名。 |
| Unrestricted | 运行所有脚本。如果是下载的脚本,会询问你是否运行。 |
| Bypass | 没有任何限制,不显示警告或提示。通常用于临时自动化任务。 |
可以把一些常用的功能,搞成函数然后调用,例如 code $PROFILE 配置 C:\Users\Administrator\Documents\PowerShell\Microsoft.PowerShell_profile.ps1 :
$stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
# FNM
fnm env --use-on-cd --shell powershell | Out-String | Invoke-Expression
Write-Host "FNM 初始化: $($stopwatch.ElapsedMilliseconds)ms"
# 加载日常使用功能函数
. $HOME\Documents\PowerShell\profile.d\Functions.ps1
Write-Host "加载自定义函数: $($stopwatch.ElapsedMilliseconds)ms"上面从 profile.d\ 加载 Functions.ps1 文件,然后执行 . $PROFILE 生效和测试。
在 profile.d\ 下我们可以划分更细的层,如文件、网络、进程,然后用版本控制工具托管这些配置。
这里 Functions.ps1 文件仅示例:
# 快速查看端口占用
function port {
param(
[Parameter(Mandatory = $true)]
[int]$PortNumber
)
$connections = Get-NetTCPConnection -LocalPort $PortNumber -ErrorAction SilentlyContinue
if ($connections) {
Write-Host "端口 $PortNumber 正在被使用:" -ForegroundColor Green
$connections | ForEach-Object {
$process = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
[PSCustomObject]@{
PID = $_.OwningProcess
ProcessName = $process.ProcessName
LocalAddr = $_.LocalAddress
LocalPort = $_.LocalPort
RemoteAddr = $_.RemoteAddress
RemotePort = $_.RemotePort
State = $_.State
}
} | Format-Table -AutoSize
}
else {
Write-Host "端口 $PortNumber 未被占用" -ForegroundColor Yellow
}
}
# 查看本机所有网络接口的 IP 地址
function ips {
Write-Host "本机网络接口 IP 地址:" -ForegroundColor Cyan
Get-NetIPAddress -AddressFamily IPv4 |
Where-Object { $_.IPAddress -ne '127.0.0.1' } |
Select-Object InterfaceAlias, IPAddress, PrefixLength |
Format-Table -AutoSize
# 如果只想要 IP 地址列表 (类似 bash 版本)
# Get-NetIPAddress -AddressFamily IPv4 |
# Where-Object { $_.IPAddress -ne '127.0.0.1' } |
# Select-Object -ExpandProperty IPAddress
}
# 根据端口号结束进程
function killport {
param(
[Parameter(Mandatory = $true)]
[int]$PortNumber
)
# 检查端口号是否有效
if ($PortNumber -lt 1 -or $PortNumber -gt 65535) {
Write-Host "无效的端口号: $PortNumber" -ForegroundColor Red
return
}
# 获取占用端口的进程
$connections = Get-NetTCPConnection -LocalPort $PortNumber -ErrorAction SilentlyContinue
if (-not $connections) {
Write-Host "端口 $PortNumber 没有被任何进程占用" -ForegroundColor Red
return
}
# 获取唯一的进程 ID
$pids = $connections | Select-Object -ExpandProperty OwningProcess -Unique
Write-Host "找到占用端口 $PortNumber 的进程:" -ForegroundColor Cyan
# 显示进程详情
$connections | ForEach-Object {
$process = Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue
[PSCustomObject]@{
PID = $_.OwningProcess
ProcessName = $process.ProcessName
Path = $process.Path
LocalAddr = $_.LocalAddress
LocalPort = $_.LocalPort
State = $_.State
}
} | Format-Table -AutoSize
Write-Host "`n正在结束以下进程: $($pids -join ', ')" -ForegroundColor Yellow
# 尝试结束进程
$success = $true
foreach ($processId in $pids) {
try {
Stop-Process -Id $processId -Force -ErrorAction Stop
Write-Host "成功结束进程 $processId" -ForegroundColor Green
}
catch {
Write-Host "无法结束进程 $processId : $_" -ForegroundColor Red
$success = $false
}
}
# 再次检查
Start-Sleep -Milliseconds 500
$stillRunning = Get-NetTCPConnection -LocalPort $PortNumber -ErrorAction SilentlyContinue
if (-not $stillRunning) {
Write-Host "`n端口 $PortNumber 已释放" -ForegroundColor Green
}
else {
Write-Host "`n无法完全释放端口 $PortNumber" -ForegroundColor Red
Write-Host " 可能需要管理员权限运行 PowerShell" -ForegroundColor Yellow
}
}
# 根据 PID 终结进程
function killpid {
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[int[]]$ProcessId
)
process {
foreach ($id in $ProcessId) {
# 检查进程是否存在
$process = Get-Process -Id $id -ErrorAction SilentlyContinue
if (-not $process) {
Write-Host "进程 PID $id 不存在" -ForegroundColor Red
continue
}
# 显示进程信息
Write-Host "找到进程:" -ForegroundColor Cyan
$process | Select-Object Id, ProcessName, Path, StartTime, CPU | Format-Table -AutoSize
# 尝试结束进程
try {
Stop-Process -Id $id -Force -ErrorAction Stop
Write-Host "成功结束进程 $id ($($process.ProcessName))" -ForegroundColor Green
}
catch {
Write-Host "无法结束进程 $id : $_" -ForegroundColor Red
Write-Host " 可能需要管理员权限" -ForegroundColor Yellow
}
# 验证进程是否已结束
Start-Sleep -Milliseconds 300
$stillRunning = Get-Process -Id $id -ErrorAction SilentlyContinue
if ($stillRunning) {
Write-Host "进程 $id 仍在运行" -ForegroundColor Yellow
}
}
}
}
Write-Host "可用函数: port, ips, killport, killpid" -ForegroundColor Cyan然后在 PowerShell 7 环境可以愉快的快速日常使用了。
~/.bashrc / ~/.zshrc
if [ -f ~/.bash_common.sh ]; then
source ~/.bash_common.sh
fi~/.bash_common.sh
# 添加路径到 PATH 的辅助函数(预防重复)
add_to_path() {
if [[ -d "$1" ]] && [[ ":$PATH:" != *":$1:"* ]]; then
export PATH="$1:$PATH"
fi
}
# 添加路径到 PATH 末尾的辅助函数 (预防重复)
append_to_path() {
if [[ -d "$1" ]] && [[ ":$PATH:" != *":$1:"* ]]; then
export PATH="$PATH:$1"
fi
}
# 显示 PATH 统计信息
path_stats() {
local total=$(echo "$PATH" | tr ':' '\n' | wc -l)
local unique=$(echo "$PATH" | tr ':' '\n' | sort -u | wc -l)
local duplicates=$((total - unique))
echo "PATH 统计:"
echo "---"
echo "总路径数: $total"
echo "唯一路径: $unique"
echo "重复路径: $duplicates"
# 显示所有路径(带有效性标记)
echo ""
echo "所有路径列表:"
echo "---"
local index=1
local invalid_count=0
while IFS= read -r dir; do
if [ -n "$dir" ]; then
if [ -d "$dir" ]; then
printf "%2d. %s\n" $index "$dir"
else
printf "%2d. %s\n" $index "$dir"
invalid_count=$((invalid_count + 1))
fi
index=$((index + 1))
fi
done < <(echo "$PATH" | tr ':' '\n')
# 总结
echo ""
echo "总结:"
echo "---"
echo "有效路径: $((total - invalid_count))"
echo "无效路径: $invalid_count"
# 越靠前的路径优先级越高,从第 1 条路径开始查找,如果找到了,就执行,不再继续查找;如果没找到,才去第 2 条路径查找
}
# 清理重复的 PATH 条目
# 预防重复,而不是事后清理,函数在 PATH 还没有完全设置好之前就被调用,或者在设置 PATH 的过程中出了问题,会导致找不到 `awk` 这些基础命令
# 因此,这个函数没有用,仅作反模式说明
clean_path() {
export PATH=$(echo "$PATH" | awk -v RS=: '!a[$0]++' | paste -sd: -)
}
# 用户级可执行文件路径
add_to_path "$HOME/.local/bin"
# pnpm
add_to_path "$HOME/.local/share/pnpm"
# fnm
add_to_path "$HOME/.local/share/fnm"
if [ -n "$ZSH_VERSION" ]; then
eval "$(fnm env --use-on-cd --shell zsh)"
elif [ -n "$BASH_VERSION" ]; then
eval "$(fnm env --use-on-cd --shell bash)"
fi
# yarn
if command -v yarn &> /dev/null; then
yarn_global_bin="$(yarn global bin)"
add_to_path "$yarn_global_bin"
fi
# 实用函数,创建目录并进入
mkcd() {
mkdir -p "$1" && cd "$1"
}
# 快速查找文件 (按文件名模糊匹配)
ff() {
find . -type f -name "*$1*" 2>/dev/null
}
# 返回上级目录(支持多层)
# 用法: up (上一层)
# 用法: up 3 (上三层)
up() {
local d=""
local limit=${1:-1}
for ((i=1; i<=limit; i++)); do
d="../$d"
done
cd "$d" || return
}
# 解压任何格式
# 用法: extract ./file.zip
extract() {
if [ -f "$1" ]; then
case "$1" in
*.tar.bz2) tar xjf "$1" ;;
*.tar.gz) tar xzf "$1" ;;
*.bz2) bunzip2 "$1" ;;
*.rar) unrar x "$1" ;;
*.gz) gunzip "$1" ;;
*.tar) tar xf "$1" ;;
*.tbz2) tar xjf "$1" ;;
*.tgz) tar xzf "$1" ;;
*.zip) unzip "$1" ;;
*.Z) uncompress "$1" ;;
*.7z) 7z x "$1" ;;
*) echo "无法解压 '$1'" ;;
esac
else
echo "'$1' 不是有效文件"
fi
}
# 快速备份文件
# 用法: backup config.json → 生成 config.json.bak
backup() { cp "$1"{,.bak}; }
# 查找大文件
# 用法: big (大于 100MB)
# 用法: big 50 (大于 50MB)
big() {
find . -type f -size +${1:-100}M -exec ls -lh {} \; 2>/dev/null | awk '{print $5, $9}'
}
# 查找并高亮显示进程
psg() {
ps aux | grep -v grep | grep -i -e VSZ -e "$@"
}
# 用法: psg node
# 查看文件夹大小(排序)
duh() {
du -sh * | sort -h
}
# 查看内存使用
mem() { free -h; }
# 快速查看系统信息
sysinfo() {
echo "系统: $(uname -s)"
echo "架构: $(uname -m)"
echo "内核: $(uname -r)"
[ -f /etc/os-release ] && . /etc/os-release && echo "发行版: $PRETTY_NAME"
free -m | awk 'NR==2{printf "内存: %s/%sMB (%.2f%%)\n", $3,$2,$3*100/$2}'
echo "磁盘: $(df -h / | awk 'NR==2 {print $3 "/" $2 " (" $5 ")"}')"
}
# 快速查看网络信息
netinfo() {
# 自动获取默认路由网卡的 IP
local main_ip=$(ip route get 1.1.1.1 2>/dev/null | grep -oP 'src \K\S+')
echo "内网 IP: ${main_ip:-"未找到"}"
# Windows WSL 环境下的宿主机 IP
local win_ip=$(ip route show 2>/dev/null | grep default | awk '{print $3}')
[ -n "$win_ip" ] && echo "Windows IP: $win_ip"
echo "公网 IP: $(curl -s --max-time 2 ifconfig.me 2>/dev/null || echo '获取失败')"
echo "DNS: $(grep "^nameserver" /etc/resolv.conf 2>/dev/null | awk '{print $2}' | paste -sd ', ')"
}
# 快速查看资源使用
usage() {
echo "内存: $(free -h | awk '/^Mem:/ {print $3 "/" $2 " (" int($3/$2*100) "%)"}')"
echo "磁盘: $(df -h / | awk 'NR==2 {print $3 "/" $2 " (" $5 ")"}')"
echo "负载: $(cat /proc/loadavg | awk '{print $1, $2, $3}')"
}
# 快速查看开发工具版本
devtools() {
echo "Node: $(node -v 2>/dev/null || echo '未安装')"
echo "npm: v$(npm -v 2>/dev/null || echo '未安装')"
echo "pnpm: v$(pnpm -v 2>/dev/null || echo '未安装')"
echo "Python: $(python3 --version 2>/dev/null | awk '{print $2}' || echo '未安装')"
echo "Git: $(git --version 2>/dev/null | awk '{print $3}' || echo '未安装')"
echo "Docker: $(docker --version 2>/dev/null | awk '{print $3}' | tr -d ',' || echo '未安装')"
}
# 查看所有监听端口
ports() {
netstat -tuln 2>/dev/null || ss -tuln
}
# 快速查看端口占用
port() {
lsof -i ":$1" 2>/dev/null || echo "端口 $1 未被占用"
}
# 查看本机所有网络接口
ips() {
ip addr | grep -oP '(?<=inet\s)\d+(\.\d+){3}'
}
# 根据端口号结束进程
killport() {
if [ -z "$1" ]; then
echo "用法: killport <端口号>"
return 1
fi
local port=$1
# 检查端口号是否有效
if ! [[ "$port" =~ ^[0-9]+$ ]] || [ "$port" -lt 1 ] || [ "$port" -gt 65535 ]; then
echo "无效的端口号: $port"
return 1
fi
# 获取占用端口的进程 PID
# local pids=$(lsof -ti:$port)
# 使用更简洁的方式获取 PID
local pids=$(lsof -ti:$port 2>/dev/null)
if [ -z "$pids" ]; then
echo "端口 $port 没有被任何进程占用"
return 0
fi
echo "找到占用端口 $port 的进程:"
# 显示进程详情
lsof -i:$port
echo -e "\n正在结束以下进程: $pids"
# 结束进程
kill -9 $pids 2>/dev/null
if [ $? -eq 0 ]; then
echo "成功结束端口 $port 的进程"
else
echo "尝试强制结束进程..."
sudo kill -9 $pids 2>/dev/null
fi
# 再次检查
if [ -z "$(lsof -ti:$port)" ]; then
echo "端口 $port 已释放"
else
echo "无法完全释放端口 $port"
echo " 可能需要 root 权限: sudo killport $port"
fi
}
#
# 取消代理
unsetproxy() {
unset HTTP_PROXY HTTPS_PROXY http_proxy https_proxy NO_PROXY no_proxy
echo "代理已取消"
}
# 显示代理状态
showproxy() {
echo "HTTP_PROXY: ${HTTP_PROXY:-未设置}"
echo "HTTPS_PROXY: ${HTTPS_PROXY:-未设置}"
echo "NO_PROXY: ${NO_PROXY:-未设置}"
}
# 获取本机 IP
myip() {
# 1. 自动获取当前活跃的内网 IP (不硬编码 eth0)
# 逻辑:查找有默认路由的网卡所关联的 IP
local internal_ip=$(ip -4 addr show $(ip route | grep default | awk '{print $5}' | head -n1) | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -n1)
echo "内网 IP: ${internal_ip:-"未找到"}"
# 2. 识别是否为 WSL 环境并获取宿主机 IP
if grep -qE "(Microsoft|WSL)" /proc/version 2>/dev/null; then
local win_ip=$(ip route | grep default | awk '{print $3}')
echo "Windows IP: $win_ip"
else
local gateway_ip=$(ip route | grep default | awk '{print $3}')
echo "默认网关: $gateway_ip"
fi
}
update_win_host() {
local win_ip=$(ip route show | grep default | awk '{print $3}')
if [ -n "$win_ip" ]; then
# 检查 /etc/hosts 中是否存在 win.host
if grep -q "win.host" /etc/hosts; then
# 如果存在且 IP 不同,则更新
if ! grep -q "$win_ip win.host" /etc/hosts; then
sudo sed -i "s/.*win.host/$win_ip win.host/" /etc/hosts
fi
else
# 如果不存在,则追加
echo "$win_ip win.host" | sudo tee -a /etc/hosts > /dev/null
fi
fi
}
# 测试网站响应时间
# 用法: ping_web https://google.com
ping_web() {
curl -o /dev/null -s -w "响应时间: %{time_total}s\n" "$1"
}
# 快速下载文件
# 用法: dl https://example.com/file.zip
# 用法: dl https://example.com/file.zip myfile.zip
dl() {
curl -L -o "${2:-$(basename $1)}" "$1"
}
# 查看大文件的前/后几行
peek() {
head -n 20 "$1"
echo "..."
tail -n 20 "$1"
}
# 在历史命令中搜索
# 用法: h git
h() {
if [ -z "$1" ]; then
history 20
else
history | grep "$1"
fi
}
# 获取 Windows 主机 IP (WSL2 网关地址)
# HOST_IP=$(ip route show | grep default | awk '{print $3}')
HOST_IP=$(ip route show | grep -oP '(?<=via )\S+' | head -n1)
HOST_PORT="${1:-7890}"
PROXY_PROTOCOL="${2:-http}" # 支持 http/socks5
PROXY_URL="${PROXY_PROTOCOL}://$HOST_IP:$HOST_PORT"
export WIN_LOCATION="$HOST_IP:$HOST_PORT"
# 代理设置:获取当前真实的 Windows 宿主机 IP (NAT 模式下的网关,非 DNS 隧道 IP)
set_wsl_proxy() {
if [ -n "$HOST_IP" ]; then
# 设置环境变量代理 (适用于大多数命令行工具)
export http_proxy="$PROXY_URL"
export https_proxy="$PROXY_URL"
export HTTP_PROXY="$PROXY_URL"
export HTTPS_PROXY="$PROXY_URL"
export ALL_PROXY="$PROXY_URL"
# 设置 Global Agent 代理 (Node.js 全局代理)
export GLOBAL_AGENT_HTTP_PROXY="$PROXY_URL"
export GLOBAL_AGENT_HTTPS_PROXY="$PROXY_URL"
# 设置不需要代理的地址
export no_proxy="localhost,127.0.0.1,::1,${HOST_IP}"
export NO_PROXY="$no_proxy"
echo "WSL 代理已设置: HTTP_PROXY/HTTPS_PROXY=$PROXY_URL"
else
echo "无法获取主机IP,请检查网络连接"
return 1
fi
}
# 启动时自动设置 WSL 代理
set_wsl_proxy
# 在 Windows 资源管理器中打开当前目录
open() {
explorer.exe "${1:-.}"
}
# AI
export OPENAI_API_KEY=
export OPENAI_API_KEY__PACKYCODE=
export ANTHROPIC_BASE_URL=https://www.packyapi.com
export ANTHROPIC_AUTH_TOKEN=
不要只安装一个 VS Code 然后安装一堆扩展 (包含 FE IDE) 去跑 FE 项目。
只安装 FE IDE + FE Sitemap + Vue 三个扩展启动项目后的内存占用极少,尤其是 Windows 用户,这极大的节约了硬件资源。(其它平台也应该将“开发、环境启动”分开)
下载免安装版的 Visual Studio Code Insiders,解压到 C:\Tools 后,在 C:\Tools\VSCode-win32-x64-insider 目录下**创建 data 目录,**这一步非常重要,然后打开 Code - Insiders.exe
- 安装上面三个扩展,并配置
- 配置编辑器 "update.mode": "none" 不要自动更新
以后,就只是用这个 Code Insiders 编辑器来启动 QP 项目、处理 Sitemap,不做开发用;
主应用或微应用的开发,可以另用 Code/Cursor/AI,这样占用资源少,且稳定。