批处理文件以更改在Powershell中运行的目录没有任何作用 [英] Batch file to change directory run from within powershell does nothing

查看:70
本文介绍了批处理文件以更改在Powershell中运行的目录没有任何作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的PATH上有一个小的"dev.bat"批处理文件,运行该文件可切换到W:\中的开发项目目录.从CMD可以正常工作,但从PowerShell(或PWSH)运行时则不能.

I have a small "dev.bat" batch file on my PATH which I run to switch to my development project directory in W:\. This works fine from CMD but not when run from PowerShell (or PWSH).

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

I have no other problems running .bat files from PowerShell.

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:\>

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

推荐答案

从PowerShell运行时,批处理文件始终在(cmd.exe)子进程 [1]中运行] ,因为PowerShell本身不理解批处理语言.

When run from PowerShell, batch files invariably run in a (cmd.exe) child process[1], given that PowerShell itself doesn't understand the batch language.

在子进程中更改工作目录仅限于该子进程(及其自己的子进程),并且对调用进程没有影响; 子进程无法更改调用进程的工作目录.

Changing the working directory in a child process is limited to that child process (and its own children), and has no effect on the calling process; a child process cannot change the calling process' working directory.

您唯一的选择是:

  • 将您的批处理文件 echo (打印)到所需的工作目录
  • 在PowerShell中捕获该路径并将其传递给Set-Location
  • have your batch file echo (print) the desired working directory
  • capture that path in PowerShell and pass it to Set-Location

如果您不想更改批处理文件,请使用以下解决方法:

If you don't want to change your batch file, use the following workaround:

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文件:


If batch files needn't be involved at all, and you just want a convenient way to create custom functions that change to a predefined location (working directory), place the following function in your $PROFILE file:

# 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
}

注意:

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

  • The generated functions use Push-Location rather than Set-Location to enable easy returning to the previous location with Pop-Location (popd).

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

For convenience, generated functions are also defined in the current session via Invoke-Expression[2] on creation, so you don't have to reload (dot-source) $PROFILE or open a new session before you can call the newly generated function.

>>盲目附加到$PROFILE意味着如果重新定义一个函数,新定义将生效,但是过时的前一个定义将在文件中徘徊,需要手动清理;在每个生成的函数之后放置的注释# quick-CD function旨在简化此操作-请参见底部的New-QuickCD的更复杂版本,该版本可以更新现有的旧定义.

Blindly appending to $PROFILE with >> means that if you redefine a function, the new definition will take effect, but the obsolete previous one will linger in the file, requiring manual cleanup; the comment # quick-CD function placed after each generated function is meant to facilitate that - see the bottom section for a more sophisticated version of New-QuickCD that updates old definitions in place.

您可以通过多种方式使该功能更强大,更方便:使参数成为必需参数,验证路径的存在(默认情况下),将路径解析为绝对路径-再次参见底部.

You can make the function more robust and convenient in a variety of ways: making the parameters mandatory, verifying the path's existence (by default), resolving the path to an absolute one - again, see the bottom section.

例如,要创建一个名为dev的函数并切换到W:\dev,您可以调用:

E.g., to create a function named dev that switches to W:\dev, you'd then call:

# 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功能:

在上述版本上进行了如下改进:


More robust, flexible New-QuickCD function:

It improves on the above version as follows:

  • 它使参数成为必需.
  • 它验证目标目录路径的存在.
  • 它定义了支持-PrintOnly开关的功能,该开关仅打印功能的目标目录,而无需对其进行更改.
  • 它首先将相对路径解析为绝对路径,因此您可以运行New-QuickCD foo .定义一个函数,该函数会切换到当前位置的绝对路径.
  • 重新定义一个函数时,先前的定义会自动更新:
    • 为了启用此功能,请使用>重定向运算符将$PROFILE整体重写.
    • 删除功能,您仍然必须手动编辑$PROFILE.
    • It makes the parameters mandatory.
    • It verifies the existence of the target directory path.
    • It defines the functions with support for a -PrintOnly switch that merely prints the function's target directory, without changing to it.
    • It resolves a relative path to an absolute one first, so that you can run New-QuickCD foo . to define a function that switches to the absolute path of the current location.
    • When you redefine a function, the previous definition is automatically updated:
      • In order to enable this functionality $PROFILE is rewritten as a whole, using the > redirection operator.
      • To remove functions, you must still edit $PROFILE manually.
      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
      
      }
      


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


      [1] By contrast, batch files run in-process when invoked from cmd.exe, analogous to how PowerShell runs its *.ps1 scripts in-process. POSIX-like shells such as Bash, on the other hand, by default run their scripts in a child process, except when sourcing is used (., source)

      [2]虽然这是对Invoke-Expression的安全使用,但它

      [2] While this is a safe use of Invoke-Expression, it should generally be avoided.

      这篇关于批处理文件以更改在Powershell中运行的目录没有任何作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆