当在PowerShell中运行时,批处理文件总是在(cmd.exe
)子进程[1]中运行,因为PowerShell本身不理解批处理语言。
更改子进程中的工作目录仅限于该子进程(及其自己的子级),对调用进程没有影响;子进程无法更改调用进程的工作目录。
您唯一的选择是:
- 使您的批处理文件回显(打印)所需的工作目录
- 在PowerShell中捕获该路径并将其传递给
Set-Location
如果您不想更改批处理文件,请使用以下解决方法:
Set-Location -LiteralPath (cmd /c 'dev.bat >NUL && cd')
# Or if you want to use the 'cd' alias for Set-Location and
# are confident that path never has "[" characters in it (so that
# it can't be mistaken for a wildcard expression):
cd (cmd /c 'dev.bat >NUL && cd')
如果完全不需要批处理文件,并且您只想要一种方便的方法来创建自定义函数以更改到预定义位置(工作目录),请将以下函数放置在您的
$PROFILE
文件中:
function New-QuickCD ($Name, $LiteralPath) {
$funcDef = @"
function global:$Name { Push-Location -LiteralPath "$LiteralPath" } # quick-CD function
"@
Invoke-Expression $funcDef
$funcDef >> $PROFILE
}
注意:
生成的函数使用Push-Location
而不是Set-Location
,以便轻松返回到以前的位置并使用Pop-Location
(popd
)。
为了方便起见,在创建时生成的函数也通过Invoke-Expression
[2]在当前会话中定义,因此您无需重新加载(点源)$PROFILE
或在调用新生成的函数之前打开一个新会话。
盲目地使用>>
添加到$PROFILE
意味着如果您重新定义一个函数,则新定义将生效,但过时的先前定义将留存在文件中,需要手动清理;每个生成的函数后面放置的注释# quick-CD function
旨在促进此过程-有关更新就地旧定义的更复杂版本的New-QuickCD
请参见底部部分。
您可以通过各种方式使函数更加健壮和方便:使参数强制、验证路径的存在性(默认情况下)、将路径解析为绝对路径-同样,请参见底部部分。
例如,要创建一个名为dev
的函数,用于切换到W:\dev
,则需要调用:
# Generate function 'dev', which switches to 'W:\dev',
# append it to your $PROFILE file, and also define it in this session:
New-QuickCD dev W:\dev
# Call it:
dev # changes the current location to W:\dev; use 'popd' to return.
更强大、灵活的New-QuickCD
函数:
它比上一个版本有以下改进:
- 让参数为必填项。
- 验证目标目录路径是否存在。
- 定义支持
-PrintOnly
开关的功能,仅打印函数的目标目录而不改变目录。
- 首先将相对路径解析为绝对路径,这样您可以运行
New-QuickCD foo .
来定义一个切换到当前位置绝对路径的函数。
- 当您重新定义一个函数时,以前的定义会自动更新:
- 为了启用此功能,整个
$PROFILE
都被重写,使用>
重定向操作符。
- 要删除函数,仍然需要手动编辑
$PROFILE
。
- 附带基于注释的帮助; 例如,运行
help New-QuickCD -Examples
。
function New-QuickCD {
param(
[Parameter(Mandatory)] [string] $FunctionName,
[Parameter(Mandatory)] [string] $DirectoryPath
)
Set-StrictMode -Version 1; $ErrorActionPreference = 'Stop'
$fullPath = (Resolve-Path -ErrorAction Stop -LiteralPath $DirectoryPath).Path
if (Test-Path -PathType Leaf $fullPath) {
$fullPath = [IO.Path]::GetDirectoryName($fullPath)
}
$idComment = '<# quick-CD function generated with New-QuickCD #>'
$newFuncDef = @"
$idComment function global:$FunctionName { param([switch] `$PrintOnly) if (`$PrintOnly) { "$fullPath" } else { Push-Location -LiteralPath "$fullPath" } }
"@
Invoke-Expression $newFuncDef
[string] $currentProfileContent = if (Test-Path -LiteralPath $PROFILE) { Get-Content -Raw -LiteralPath $PROFILE }
$newProfileContent = $currentProfileContent -replace ('(?m)^{0} function global:{1} .+$' -f [regex]::Escape($idComment), [regex]::Escape($FunctionName)), $newFuncDef
if (-not $currentProfileContent -or $newProfileContent -ceq $currentProfileContent) {
$newProfileContent = $newProfileContent.TrimEnd() + [Environment]::NewLine * 2 + $newFuncDef
}
$newProfileContent > $PROFILE
}
[1] 相比之下,当从cmd.exe
调用批处理文件时,它们在进程中运行,类似于PowerShell在进程中运行其*.ps1
脚本的方式。另一方面,像Bash这样的POSIX类Shell默认情况下会在子进程中运行其脚本,除非使用源代码(.
, source
)。
[2] 尽管这是使用Invoke-Expression
的一种安全方法,但通常应该避免使用。
CD /?
,则可以阅读更改目录的命令使用信息。更改驱动器时,使用**/D
选项,仅更改路径时,省略/D
。在C:\dev.bat
中,您应该使用CD /D "W:\dev"
**, _(双引号不是必需的,但最好是最佳实践)_。 - CompoSet-Location
命令 (它有一个别名CD
). 通常的做法是Set-Location -Path "W:\dev"
_(缩写形式为CD "W:\dev"
)_。 - Compo/d
只能在 CMD 中使用,而不能在 PowerShell 中使用。它只能为我的批处理文件节省一行代码 - 问题是,在批处理文件中使用CD
命令不会影响 PowerShell 会话中的位置。我也想只输入dev
而不是Set-Location W:\my\possibly\long\path
... - Marc