【问题标题】:Mapping a network drive without hardcoding a drive letter in a batch file映射网络驱动器而不在批处理文件中硬编码驱动器号
【发布时间】:2010-09-15 09:40:23
【问题描述】:

我需要用批处理文件映射网络驱动器,但不想指定驱动器号。

批处理文件用作部署过程的一部分;我从CruiseControl.Net 调用批处理文件,批处理文件需要映射一个需要凭据才能进行身份验证的UNC 路径。然后批处理文件调用RoboCopy 将网站从输出目录部署到目的地(并排除一些文件和文件夹)。最后批量删除网盘。

问题在于,这是不可扩展的,当只有几个项目时这很好,但我们现在有 20 个项目使用这种方法并且没有驱动器号来映射。我不想重复使用驱动器号,因为它们可能会发生冲突 - 这很糟糕。

这是一个批处理文件的例子:

@echo off
net use x: \\192.168.0.1\Share\wwwroot\MyProject /user:mydomain\myuser MyP455w0rd
robocopy.exe "W:\wwwroot\MyProject" x:\ *.* /E /XO /XD "App_Data/Search" "*.svn" /XF "sitefinity.log" "Thumbs.db" /NDL /NC /NP
net use x: /delete

并为可读性而格式化:

@echo off
net use x: \\192.168.0.1\Share\wwwroot\MyProject 
    /user:mydomain\myuser MyP455w0rd
robocopy.exe "W:\wwwroot\MyProject" x:\ *.* /E /XO /XD 
    "App_Data/Search" "*.svn" /XF "sitefinity.log" "Thumbs.db" /NDL /NC /NP
net use x: /delete

【问题讨论】:

    标签: batch-file network-drive


    【解决方案1】:

    如果您没有同时连接多个网络共享,您可以让net use * 为您分配一个空闲盘符。之后,您可以使用 robocopy 通过其 UNC 路径访问共享,并使用 net use * /delete 释放任何连接的共享。

    类似这样的:

    @echo off
    net use * \\192.168.0.1\Share\wwwroot\MyProject /user:mydomain\myuser MyP455w0rd
    robocopy.exe "W:\wwwroot\MyProject" "\\192.168.0.1\Share\wwwroot\MyProject" *.* /E /XO /XD "App_Data/Search" "*.svn" /XF "sitefinity.log" "Thumbs.db" /NDL /NC /NP
    net use * /delete /yes
    

    编辑:

    正如我从一些研究中了解到的,您可以简单地映射共享而不分配驱动器号。然后匿名映射,仅通过其远程 UNC 路径。这样,您还可以通过仅指定其远程名称来删除映射。

    这应该可行:

    @echo off
    net use \\192.168.0.1\Share\wwwroot\MyProject /user:mydomain\myuser MyP455w0rd
    robocopy.exe "W:\wwwroot\MyProject" "\\192.168.0.1\Share\wwwroot\MyProject" *.* /E /XO /XD "App_Data/Search" "*.svn" /XF "sitefinity.log" "Thumbs.db" /NDL /NC /NP
    net use \\192.168.0.1\Share\wwwroot\MyProject /delete
    

    【讨论】:

    • 有可能同时连接多个网络共享,否则我总是可以为每个项目分配 Z: 或硬编码字母。 net use * /delete 也将释放所有连接的共享,这将是服务器上的问题
    • 这比我的解决方案好多了:)
    • net use ... /delete 似乎没有立即清除凭据。在调用它直到进程退出后,对有凭证的位置运行 robocopy 仍然有效。 klist purge 似乎可以解决问题,但可能有点矫枉过正。
    【解决方案2】:

    最简单的方法:

    pushd "\\server\share\wwwroot\My Project"
    robocopy . x:\ *.* /E ...etc
    popd
    

    Pushd 将自动将网络驱动器映射到可用的盘符,popd 将删除它。它们是内部 CMD 命令;使用 UNC 路径需要启用扩展。批处理示例:

    @echo off
    pushd %1
      echo. --- Processing %1 as "%cd%"
      echo. ...
      rem do thing here
      echo. Done.
    popd
    

    注意:在这个使用命令行参数的实现中,命令行上的路径必须被引用。

    示例会话:

    E:\temp>.\demo-pushd.bat "\\server\share\wwwroot\My Project"
     --- Processing "\\server\share\wwwroot\My Project" as "X:\wwwroot\My Project"
        ...
        Done.
    
    E:\temp> net use X:
    The network connection could not be found.
    
    More help is available by typing NET HELPMSG 2250.
    E:\temp>
    

    【讨论】:

    • 无法按照 OP 的要求定义凭据。
    • @thomas:今天这里的 6 个答案中只有 2 个说明了凭据的使用,但这是唯一值得否决的答案?对于它解决的部分问题,使用 pushd 代替 For 循环更加直接和简单。
    • 其他答案使用net 命令。虽然他们没有说明如何使用凭据,但至少是可能的。
    【解决方案3】:

    有些人可能会发现以下批处理文件很有用。

    它不依赖外部程序。

    批处理文件包含一个函数:freedrive,它找到一个空闲的驱动器号并将其返回到一个变量中。它还可以正确检测没有介质的光驱占用驱动器号。

    @echo off
    setlocal
    
    call :freedrive mydriveletter && goto :cont
    echo ERROR: No free drive letter found.
    goto :exit
    :cont
    echo Found drive letter: %mydriveletter%
    
    goto :exit
    
    rem Finds a free drive letter.
    rem
    rem Parameters:
    rem     %1 = Output variable name.
    rem
    rem Example:
    rem     call :freedrive mydriveletter && goto :cont
    rem     echo ERROR: No free drive letter found.
    rem     goto :EOF
    rem     :cont
    rem     echo Found drive letter: %mydriveletter%
    :freedrive
    setlocal EnableDelayedExpansion
    set exitcode=0
    set "output_var=%~1"
    for %%i in (A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z) do (
        set "drive=%%i:"
        rem If 'subst' fails, the drive letter is already in use.
        rem This way we can even detect optical drives that have a drive
        rem letter but no media in them, a case that goes undetected when
        rem using 'if exist'.
        subst !drive! %SystemDrive%\ >nul
        if !errorlevel! == 0 (
            subst !drive! /d >nul
            goto :freedrive0
        )
    )
    set exitcode=1
    set drive=
    :freedrive0
    endlocal & set "%output_var%=%drive%" & exit /b %exitcode%
    
    :exit
    pause
    

    【讨论】:

    • 我喜欢这种方法;不是“检查可用的东西并使用它”,而是“使用它,看看它是否有效”。
    【解决方案4】:

    我使用它让 NET 选择一个空闲的驱动器号,然后使用 NET 找出它分配的字母:

    net use * \\server\share
    for /f "tokens=2" %%i in ('net use ^| find "\\server\share"') do set netdrive=%%i
    echo %netdrive% has been mapped
    

    【讨论】:

      【解决方案5】:

      好吧...这可能不是很吸引人,但我现在就是这样做的;一个基本的 try catch 方法。尝试映射驱动器,如果它正在使用中,则转到下一步。我仅尝试了 2 次就说明了这一点,但将其扩展到 4、10 或更多驱动器号并不难。

      是的,它确实冒犯了我的编程敏感性,我不喜欢代码的重复。不幸的是,我不知道如何将路径和凭据传递到批处理文件中,因为我自己不调用它,CruiseControl.net 调用它时不带参数。

      @echo off
      
      :START
      net use z: \\192.168.0.1\Share\wwwroot\MyProject /user:mydomain\myuser MyP455w0rd
      if %ERRORLEVEL% ==2 goto Y
      ROBOCOPY HERE
      net use z: /delete
      exit
      
      :Y
      net use y: \\192.168.0.1\Share\wwwroot\MyProject /user:mydomain\myuser MyP455w0rd
      if %ERRORLEVEL% ==2 goto W
      ROBOCOPY HERE
      net use y: /delete
      exit
      
      :W
      sleep 20
      goto START
      

      【讨论】:

        【解决方案6】:

        一种可能的解决方案是将pushd command 应用于肯定存在的UNC 路径,因此会创建一个指向该路径的临时驱动器号。当前目录,即临时驱动器的根目录,可以确定获取盘符。最后,必须使用popd命令删除临时盘符:

        pushd "\\%COMPUTERNAME%\ADMIN$"
        rem /* Do stuff here with the root of the temporarily created drive
        rem    used as the current working directory... */
        rem // Store the current drive in variable `DRIVE` for later use:
        for /F "delims=:" %%D in ("%CD%") do set "DRIVE=%%D:"
        popd
        echo The next free drive letter is `%DRIVE%`.
        

        变量COMPUTERNAME 和共享\\%COMPUTERNAME%\ADMIN$ 应该存在于所有现代(基于NT)的Windows 系统上。


        如果您不希望当前环境因pushd 尝试派生空闲驱动器号而暂时“污染”,您可能需要使用以下方法:

        for /F "delims=:" %%D in ('
            pushd "\\%COMPUTERNAME%\ADMIN$" ^& ^
                for %%Z in ^(.^) do popd
        ') do set "DRIVE=%%D:"
        echo The next free drive letter is `%DRIVE%`.
        

        【讨论】:

          【解决方案7】:

          @longneck,我用的是这样的:

          for /f "tokens=2" %%i in ('net use * \\server\share') do (
              if defined netdrive goto :cont
              set netdrive=%%i
          )
          
          :cont
          echo.%netdrive% has been mapped
          pause
          
          net use %netdrive% /d /y
          pause
          

          这避免了通过 find 对 net 管道的第二次调用。

          您还可以对此进行扩展,以便在子例程中执行此操作,而不是直接调用 set netdrive。这使您可以进行更多的错误检查或处理。

          for /f "tokens=2" %%i in ('net use * \\server\share') do (
              if defined netdrive goto :cont
              call :parse %%i
          )
          
          :parse
          if ~%1==%1~ goto :eof
          if Z:==%1 (
              set netdrive=%1
          )
          goto :eof
          
          :cont
          echo.%netdrive% has been mapped
          pause
          
          net use %netdrive% /d /y
          pause
          

          这里的 parse 子例程并不是很有用,只是说明了这个概念。

          【讨论】:

            猜你喜欢
            • 2021-02-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多