用于更改从 powershell 中运行的目录的批处理文件不执行任何操作

Mar*_*arc 3 powershell batch-file working-directory

我的 PATH 上有一个小的“dev.bat”批处理文件,我运行该文件以切换到W:\. 这在 CMD 中可以正常工作,但在 PowerShell(或 PWSH)中运行时则不行。

从 PowerShell 运行 .bat 文件没有其他问题。

PS C:\> type C:\dev.bat
W:
CD W:\dev
PS C:\> dev.bat

me@computer C:\
> W:

me@computer W:\dev
> CD W:\dev

PS C:\> echo "Why did dev.bat not change directory??"
Why did dev.bat not change directory??

PS C:\> W:
PS W:\>
Run Code Online (Sandbox Code Playgroud)

不,cmd /c dev.bat没有区别。

mkl*_*nt0 6

当从 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')
Run Code Online (Sandbox Code Playgroud)

如果根本不需要涉及批处理文件,而您只想要一种方便的方法来创建更改到预定义位置(工作目录)的自定义函数,请将以下函数放入您的$PROFILE文件中:

# Helper function to place in $PROFILE, which generates custom quick-cd
# functions, based on a function name and target directory path.
function New-QuickCD ($Name, $LiteralPath) {
  $funcDef = @"
function global:$Name { Push-Location -LiteralPath "$LiteralPath" } # quick-CD function
"@
  Invoke-Expression $funcDef # define in current session too
  $funcDef >> $PROFILE # append to $PROFILE
}
Run Code Online (Sandbox Code Playgroud)

注意

  • 生成的函数使用Push-Location而不是Set-Location通过Pop-Location( popd)轻松返回到先前的位置。

  • 为方便起见,生成的函数也在创建时通过[2]当前会话中定义,因此您不必重新加载(点源)或打开新会话,然后才能调用新生成的函数。Invoke-Expression$PROFILE

  • 盲目追加$PROFILEwith>>是指如果你重新定义一个函数,新的定义会生效,但是之前的过时的会在文件中徘徊,需要手动清理;# 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.
Run Code Online (Sandbox Code Playgroud)

更强大、更灵活的New-QuickCD功能:

它在上述版本的基础上改进如下:

  • 它使参数成为强制性的。
  • 它验证目标目录路径是否存在。
  • 它定义了支持-PrintOnly开关的函数,该开关仅打印函数的目标目录,而不对其进行更改。
  • 它首先将相对路径解析为绝对路径,以便您可以运行New-QuickCD foo .以定义切换到当前位置的绝对路径的函数。
  • 当你重新定义一个函数时,之前的定义会自动更新:
    • 为了启用此功能$PROFILE,使用>重定向运算符重写为一个整体。
    • 删除函数,您仍然必须$PROFILE手动编辑。
  • 它带有基于评论的帮助;运行help New-QuickCD -Examples,例如。
function New-QuickCD {
  <#
  .SYNOPSIS
    Creates a custom quick-CD function.

  .DESCRIPTION
    Creates a custom quick-CD function and appends it your $PROFILE file.

    Such a function changes to a fixed location (directory) stored inside the 
    function, specified at creation time to allow for quickly changing to
    frequently used directories using a short name.

    For convenience, a newly created function is also defined for the running
    session (not just for all future sessions).

    The quick-CD functions use Push-Location to change location, which
    enables you to easily return to the previously active location with
    Pop-Location (popd).

    To determine what location a given quick-CD function *would* change to,
    invoke it with the -PrintOnly switch.

  .PARAMETER FunctionName
  The name of the quick-CD function to define.

  .PARAMETER DirectoryPath
  The literal path of the directory the quick-CD function should change to.
  If given a relative path, it is resolved to an absolute one first.
  For convenience, you may specify a *file* path, in which case that file's
  parent path is used.

  .NOTES
    Your $PROFILE file is recreated every time you use this function, using the
    > redirection operator, so as to support updating functions in place.

    To *remove* a quick-CD function, edit $PROFILE manually.

  .EXAMPLE
    New-QuickCD dev W:\dev

    Adds a 'dev' function to $PROFILE, which on invocation changes the current
    location to W:\dev
    * Call just 'dev' to change to W:\dev. Use popd to return to the previous
      location.
    * Call 'dev -PrintOnly' to print what location function 'dev' *would*
      change to.

  .EXAMPLE
    New-QuickCD proj .

    Adds a 'proj' function to $PROFILE, which on invocation changes to the 
    the location that is current at the time of calling New-QuickCd.

  #>
  param(
    [Parameter(Mandatory)] [string] $FunctionName,
    [Parameter(Mandatory)] [string] $DirectoryPath
  )

  Set-StrictMode -Version 1; $ErrorActionPreference = 'Stop'

  # Resolve the path to a full path. Fail if it doesn't exist.
  $fullPath = (Resolve-Path -ErrorAction Stop -LiteralPath $DirectoryPath).Path
  # As a courtesy, if the path is a *file*, we use its parent path instead.
  if (Test-Path -PathType Leaf $fullPath) {
    $fullPath = [IO.Path]::GetDirectoryName($fullPath)
  }

  # Define a comment that identifies the functions we add to $PROFILE as
  # quick-CD functions.
  $idComment = '<# quick-CD function generated with New-QuickCD #>'

  # Generate the new function's source code...
  #  * on a *single line*, which enables easy filtering when updating $PROFILE below
  #  * with a distinctive comment at the end of the line that identifies the
  #    function as a quick-CD function.
  #  * with the global: scope specifier, which makes it easier to call the
  #    same definition with Invok-Expression to make the function available in the
  #    current session too.
  $newFuncDef = @"
$idComment function global:$FunctionName { param([switch] `$PrintOnly) if (`$PrintOnly) { "$fullPath" } else { Push-Location -LiteralPath "$fullPath" } }
"@
  # ... define it in the current session (doing this *before* updating $PROFILE ensures early exit if the function name is invalid)
  Invoke-Expression $newFuncDef
  # ... and update $PROFILE:
  # Get the current content of $PROFILE
  [string] $currentProfileContent =  if (Test-Path -LiteralPath $PROFILE)  { Get-Content -Raw -LiteralPath $PROFILE }
  # Try to replace an existing definition.
  $newProfileContent = $currentProfileContent -replace ('(?m)^{0} function global:{1} .+$' -f [regex]::Escape($idComment), [regex]::Escape($FunctionName)), $newFuncDef
  if (-not $currentProfileContent -or $newProfileContent -ceq $currentProfileContent) { # Profile didn't exist or nothing was replaced -> we must append the new definition.
    $newProfileContent = $newProfileContent.TrimEnd() + [Environment]::NewLine * 2 + $newFuncDef
  }
  # Write the file.
  $newProfileContent > $PROFILE

}
Run Code Online (Sandbox Code Playgroud)

[1] 相比之下,批处理文件从 调用时在进程内运行cmd.exe,类似于 PowerShell*.ps1在进程内运行其脚本的方式。另一方面,类似于 POSIX 的 shell,例如 Bash,默认情况下在子进程中运行它们的脚本,除非使用了采购 ( ., source)

[2] 虽然这是 的安全使用Invoke-Expression,但通常应避免使用