Windows 下 Codex 的 Shell 使用优化
太阳作者太阳
原创内容采用 CC-4.0 协议发布,转载请注明出处

Windows 下 Codex 的 Shell 使用优化

Codex 在 Windows 上遇到的问题,很多不在代码本身,而在 shell。命令能不能执行、参数怎么解析、输出是不是 Codex 预期的格式,都会受到当前 PowerShell 版本和工具链的影响。

如果还在使用 Windows PowerShell 5.1,建议先升级到 PowerShell 7(命令名是 pwsh)。再根据需要补充 GNU 命令工具,并在提示词里明确告诉 Codex 当前 shell 的规则。

先升级到 PowerShell 7

PowerShell 7 对跨平台命令习惯更友好,也支持 &&|| 这样的条件链式执行。可以通过 WinGet 安装:

winget install --id Microsoft.Powershell --source winget

安装完成后,用 pwsh 打开 PowerShell 7:

pwsh

确认实际版本和命令路径:

$PSVersionTable.PSVersion
Get-Command pwsh

输出版本号为 7.x,且 Get-Command 能找到 pwsh.exe,说明新 shell 已经可以使用。旧的 powershell.exe 仍然可以保留,只有在需要兼容旧脚本时才使用它。

需要时安装 GNU 命令工具

Windows 原生 PowerShell 已经有不少对应命令,例如:

Linux 习惯 PowerShell 写法
ls Get-ChildItem
cat Get-Content
cd Set-Location
grep Select-String

但这些命令返回的对象和 Unix 文本流不完全一样。项目脚本、个人习惯或 Codex 生成的命令如果明显依赖 GNU 工具,可以安装 Windows 版本的 GNU Coreutils:

winget install Microsoft.Coreutils

安装后重新打开终端,再检查命令是否进入 PATH

Get-Command grep
Get-Command ls

如果命令找不到,先确认安装是否成功,再检查当前终端是否读取了新的环境变量。安装程序修改的是系统配置,已经打开的 shell 不一定会自动刷新。

给 Codex 一段明确的 shell 说明

Codex 执行命令时,需要知道自己面对的是 PowerShell,而不是 Bash。可以在任务开始时提供下面这段说明:

Run a simple PowerShell command. Prefer Python for complex or stateful tasks. Start a persistent session with interactive=True, then reuse the same tool with task_id=<id> to send input and read output in one step. Use wait_for_pattern to wait for a prompt. TaskOutput remains available as a fallback for listing/monitoring tasks. Send 'exit' to close the session.

PowerShell quick reference:
- Cmdlets use Verb-Noun names: Get-ChildItem (list files), Get-Content (read file), Set-Location (cd), Copy-Item, Move-Item, Remove-Item, New-Item, Select-String (grep), Get-Command, Get-Help.
- Splat cmdlet params with `@{}`: `$p = @{LiteralPath=$f; Destination=$d}; Copy-Item @p`. `$LASTEXITCODE` is for native commands only, not cmdlet success.
- The pipeline `|` passes .NET objects, not plain text; shape with Where-Object, Select-Object, ForEach-Object, Sort-Object, Measure-Object.
- `foreach (...) { }` is a statement, not an expression — cannot be piped directly. Assign first or use `ForEach-Object`.
- Comparison operators: -eq -ne -gt -ge -lt -le, -like (wildcard), -match (regex), -contains (collection membership), -replace (regex replace). Logical operators: -and -or -not (or `!`).
- Chain commands with `;` (always run next) or `&&` / `||` (PowerShell 7+: run next only on success / only on failure).
- Strings: 'single quotes' literal; "double quotes" expand $variables and $(subexpressions). Use `${name}_suffix` for variable boundaries, `$($obj.prop)` for properties. Avoid Bash-style `"\"q\""`; use `'"q"'` or backtick-escaping.
- Here-strings: `@'...'@` (literal) or `@"..."@` (expanded). Opening delimiter last on line; closing delimiter alone at line start. No Bash heredocs (`python - <<'PY'`). Prefer `ConvertTo-Json` over manual JSON escaping.
- Native arguments: `& $exe @argList`. Don't use `$args` (automatic variable). Omitted arg `''` `$null` are distinct. Capture `$LASTEXITCODE` immediately.
- Avoid backtick continuation `` ` ``; trailing space silently breaks. Use `@()` arrays or natural breaks after pipes/commas/operators.
- Avoid `--%` (Stop-Parsing); it disables parsing. Use only for fixed literal native commands.
- Environment variables: Use `$env:NAME` for session-scoped read/write. Use `[Environment]::SetEnvironmentVariable('NAME', 'value', 'Scope')` for persistence. Resolution priority: Process > User > Machine. List with `Get-ChildItem Env:`. Append PATH with `$env:PATH += ';new\path'` — never overwrite, check for duplicates first. Do not use `%NAME%` inside PowerShell. Child process changes do not propagate back to parent.
- $LASTEXITCODE holds the exit code of the last native command; $? is $true if the last command succeeded. Note: $LASTEXITCODE may not be set if native output is piped to a cmdlet; capture it before piping.
- Parameter value expressions must be parenthesized: `-Index (100..120)` not `-Index 100..120`.

这段提示词的作用不是让 Codex 背命令,而是减少它把 Bash 语法直接粘到 PowerShell 中的情况。尤其要提醒它:PowerShell 管道传递的是 .NET 对象,grep 的替代写法通常是 Select-String,而 $LASTEXITCODE 只适合判断原生程序的退出码。

几个容易踩到的坑

&& 只在 PowerShell 7 中可用。旧版 PowerShell 里应使用分号,或者改写成显式的 $? 判断。分号会无条件执行后面的命令,不能当作成功条件链使用。

PowerShell 的 foreach 是语句,不能像 ForEach-Object 那样直接接在管道中。需要先把结果保存到变量,或者改用管道版本。

处理 JSON 时,不要照搬 Bash 的 heredoc 和复杂转义。PowerShell 的 here-string 配合 ConvertTo-Json 通常更容易读,也更不容易因为引号层级出错。

复杂、需要多步状态的操作,可以让 Codex 使用 Python。Python 适合承接数据转换、批量文件处理和需要保持中间状态的脚本;简单的文件查看、移动和命令检查,直接使用 PowerShell 更合适。

结语

Windows 上使用 Codex,不需要强行把所有命令变成 Linux 风格。比较稳妥的做法是:用 PowerShell 7 处理 Windows 文件和系统任务,需要 Unix 命令时再补 GNU 工具,并在任务开头说明 shell 的语法规则。这样 Codex 生成的命令更接近当前环境,出错时也更容易定位到底是 shell、工具还是项目代码的问题。