$PSScriptRoot 是什么
$PSScriptRoot 是 PowerShell 的自动变量,值是「当前脚本文件所在目录」,用来把配置、日志、数据文件定位到脚本旁边,而不是依赖调用者当时的工作目录。它解决的是脚本最容易出问题的一类场景:双击运行、被计划任务拉起、被别的脚本调用时,当前目录各不相同,写相对路径就会「在我机器上能跑,在服务器上找不到文件」。
基本信息
| 适用平台 | PowerShell 3.0 及以后(Windows 8/Server 2012 起自带) |
|---|---|
| 取值 | 脚本文件所在目录的绝对路径,不带文件名 |
| 配套变量 | $PSCommandPath 含文件名的完整路径、$MyInvocation.MyCommand.Path 等价写法 |
| 可用范围 | 只在脚本文件(.ps1)、模块脚本、点源脚本内有效 |
| 不可用范围 | 交互式控制台、-Command 一行式命令中为空 |
| 典型用法 | Join-Path $PSScriptRoot 'config.ini' 组合同目录文件的绝对路径 |
典型使用场景
- 读取同目录配置:脚本与 config.ini 一起发布,走到哪都读同一份
- 写日志到脚本目录:便于直接查看与打包,不依赖启动目录
- 定位同目录子模块:加载 .\lib\Helpers.ps1 这类随脚本分发的文件
- 计划任务运行:任务的工作目录默认是系统目录,路径必须从脚本自身派生
- 注册表式落地:脚本内统一切换到自身目录,避免后续相对路径写错位置
常用示例
常用示例
$cfg = Join-Path $PSScriptRoot 'config.ini' # 同目录下的配置文件
Get-Content -Path $cfg -Encoding UTF8
$log = Join-Path $PSScriptRoot 'run.log' # 同目录写日志
"$(Get-Date -Format s) start" | Add-Content -Path $log -Encoding UTF8
$lib = Join-Path $PSScriptRoot 'lib\Helpers.ps1'
. $lib # 点源加载同目录的辅助脚本
Split-Path -Parent $PSCommandPath # 等价写法:从完整路径取目录
Set-Location -LiteralPath $PSScriptRoot # 明确切换工作目录(谨慎使用)相关路径变量对照
| 变量/表达式 | 含义 | 什么时候不对 |
|---|---|---|
| $PSScriptRoot | 脚本文件所在目录 | 交互式会话与 -Command 中为空 |
| $PSCommandPath | 脚本文件完整路径 | 同上;管道线上下文中可能为空 |
| $PWD | 当前工作目录(PowerShell 位置) | 随 Set-Location 与调用方式变化 |
| [Environment]::CurrentDirectory | .NET 视角的当前目录 | 与 PowerShell 当前位置可能不一致 |
| $HOME / $env:USERPROFILE | 当前用户配置目录 | 服务账户下指向系统配置目录,易误写 |
在交互式命令行或 -Command 传参的一行式命令里 $PSScriptRoot 是空值,直接拼接会得到相对路径或报错;这类场景用 $PWD 或显式传入路径。
PowerShell 的当前位置($PWD)与 .NET 的当前目录([Environment]::CurrentDirectory)是两个东西,某些 .NET 方法读写文件时按后者解析相对路径,出现「PowerShell 里能找到、代码里找不到」时优先怀疑这里。
计划任务里的稳妥写法
计划任务里的稳妥写法
# 计划任务把脚本放在 D:\ops\cleanup.ps1,工作目录默认是 C:\Windows\System32
$root = $PSScriptRoot
$data = Join-Path $root 'data' # 一律用绝对路径
if (-not (Test-Path $data)) { New-Item -ItemType Directory -Path $data | Out-Null }
Start-Transcript -Path (Join-Path $root 'run.log') -Append # 留痕便于排错用 Set-Location $PSScriptRoot 把工作目录改成脚本目录会连带影响脚本内其它相对路径与外部程序调用的上下文,属于全局状态改动;更稳的做法是保持工作目录不变,所有路径都用 Join-Path 拼绝对路径。