Skip to content

Instantly share code, notes, and snippets.

@xianghongai
Last active February 12, 2026 11:23
Show Gist options
  • Select an option

  • Save xianghongai/6e7a0c166fdecba87b1705cd0d250c03 to your computer and use it in GitHub Desktop.

Select an option

Save xianghongai/6e7a0c166fdecba87b1705cd0d250c03 to your computer and use it in GitHub Desktop.
Windows 11 开发环境

Windows 11 开发环境

安装 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

安装常用包

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,说是能自动接受软件包本身的许可协议、自动接受软件源的协议条款。

WinGet 注册

低版本 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 RegistSave 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 中准备好物料,然后通过资源管理器拷贝,跨文件系统,不推荐拷贝大文件、多文件。

配置 WSL

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,这样占用资源少,且稳定。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment