Subversion 客户端挂钩不显示回显

Subversion client hook doesn't show echo

这是我的预提交挂钩。当我开始提交时,我的外部差异工具会弹出,但 cmd window 什么都不显示,它也不会读取我的键盘输入。

@echo off
for /F "usebackq" %%i IN (%1) DO (svn diff %%i >nul)
echo hello
choice /M "Continue to commit?" 
SET userChoice=%ERRORLEVEL%
IF %userChoice% equ 1 exit /b 0
IF %userChoice% equ 2 exit /b 1

我错过了什么吗?

根据 help documentation of TortoiseSVN,您需要显式重定向到控制台(设备 con):

When debugging hook scripts you may want to echo progress lines to the DOS console, or insert a pause to stop the console window disappearing when the script completes. Because I/O is redirected this will not normally work. However you can redirect input and output explicitly to CON to overcome this. e.g.

echo Checking Status > con
pause < con > con

不幸的是,这不适用于 choice 命令,因为它似乎不接受从设备 con:

重定向的输入
>>> choice < con
ERROR: The handle is invalid.

一个简单的解决方法是使用支持从 con:

重定向的 set /P 命令
@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Loop structure for prompting user:
:CONFIRM
rem // Display prompt and await user input:
< con > con set /P SELECT="Continue? [Yes/No]?"
rem // Check user input case-insensitively:
if /I "%SELECT%"=="Y"   exit /B 0
if /I "%SELECT%"=="Yes" exit /B 0
if /I "%SELECT%"=="N"   exit /B 1
if /I "%SELECT%"=="No"  exit /B 1
rem // Repeat prompt in case of invalid entry:
goto :CONFIRM

endlocal
exit /B

这样做的缺点是条目需要用RETURN来终止,与choice命令相反,后者接受单-按键输入。


这里有一个更好的方法,它具有类似于 choice 命令的单键提示。这(误)使用 xcopy 命令及其接受重定向输入到提供 /W 开关时出现的复制确认提示的能力。也看看注释:

@echo off
setlocal EnableExtensions EnableDelayedExpansion

rem // Loop structure for prompting user:
:CONFIRM
rem // Display prompt without trailing line-break:
< nul > con set /P ="Continue? [Y/N]?"
rem /* Let `xcopy` wait for a single-key entry using its `/W` switch;
rem    `/L` tells `xcopy` to actually copy nothing; to avoid problems
rem    with inexistent paths, use this script's path as source file
rem    and the system's temporary directory as the target location: */
< con (
    for /F "delims=" %%L in ('
        xcopy /L /W "%~f0" "%TMP%\"
    ') do (
        rem /* Capture the copy confirmation prompt of `xcopy /L /W`,
        rem    `Press any key when ready to begin copying file(s)`,
        rem    together with the character entered by the user, which
        rem    is appended to the prompt message: */
        set "CHAR=%%L"
        rem /* Leave loop upon first iteration, because we are not
        rem    interested in any copying results and summaries: */
        goto :SKIP
    )
)
:SKIP
rem // Extract the last character, which is the actual user input:
set "CHAR=!CHAR:~-1!"
rem // Display user entry:
> con echo(!CHAR!
rem // Check user input case-insensitively:
if /I "!CHAR!"=="Y" exit /B 0
if /I "!CHAR!"=="N" exit /B 1
rem // Repeat prompt in case of invalid entry:
goto :CONFIRM

endlocal
exit /B

如果提供了意外输入,将在下一行重新打印提示并等待新的用户输入。


为避免在意外用户输入的情况下重新打印提示文本,请使用以下脚本:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "MESSAGE=Confirm? [Y/N]?"

rem // Retrieve back-space character:
for /F %%Z in ('prompt $H ^& for %%Z in ^(.^) do rem/') do set "BS=%%Z"
rem // Retrieve carriage-return character:
for /F %%Z in ('copy /Z "%~f0" nul') do set "CR=%%Z"

rem // Loop structure for prompting user:
:CONFIRM
setlocal EnableDelayedExpansion
rem // Display prompt without trailing line-break:
> con < nul set /P ="%BS%  !CR!!MESSAGE!"
endlocal
rem /* Let `xcopy` wait for a single-key entry using its `/W` switch;
rem    `/L` tells `xcopy` to actually copy nothing; to avoid problems
rem    with inexistent paths, use this script's path as source file
rem    and the system's temporary directory as the target location: */
< con (
    for /F "delims=" %%L in ('
        xcopy /L /W "%~f0" "%TMP%\"
    ') do (
        rem /* Capture the copy confirmation prompt of `xcopy /L /W`,
        rem    `Press any key when ready to begin copying file(s)`,
        rem    together with the character entered by the user, which
        rem    is appended to the prompt message: */
        set "CHAR=%%L"
        rem /* Leave loop upon first iteration, because we are not
        rem    interested in any copying results and summaries: */
        goto :SKIP
    )
)
:SKIP
setlocal EnableDelayedExpansion
rem // Extract the last character, which is the actual user input:
set "CHAR=!CHAR:~-1!"
rem // Display user entry without trailing line-break:
> con < nul set /P ="!CHAR!"
rem // Check user input case-insensitively:
if /I "!CHAR!"=="Y" exit /B 0
if /I "!CHAR!"=="N" exit /B 1
endlocal
rem // Repeat prompt in case of invalid entry:
goto :CONFIRM
echo/

endlocal
exit /B

这是一个全面的解决方案,允许使用 !^" 等特殊字符。


这是一个非常相似的解决方案,它依赖于 replace 命令及其 /W 开关,在出现意外的用户输入时也不会重新打印提示。由于 replace 将按键字符打印在单独的一行中,因此无需将其与另一个字符串分开(与 xcopy /W 相反):

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem // Define constants here:
set "MESSAGE=Confirm? [Y/N]?"

rem // Retrieve back-space character:
for /F %%Z in ('prompt $H ^& for %%Z in ^(.^) do rem/') do set "BS=%%Z"
rem // Retrieve carriage-return character:
for /F %%Z in ('copy /Z "%~f0" nul') do set "CR=%%Z"

rem // Loop structure for prompting user:
:CONFIRM
setlocal EnableDelayedExpansion
rem // Display prompt without trailing line-break:
> con < nul set /P ="%BS%  !CR!!MESSAGE!"
endlocal
rem /* Let `replace` wait for a single-key entry using its `/W` switch;
rem    `/U` tells `replace` to actually do nothing; to avoid problems
rem    with inexistent paths, use this script's path as source file
rem    and its parent directory as the target location: */
< con (
    for /F "skip=1 delims=" %%L in ('
        replace /W /U "%~f0" "."
    ') do (
        rem /* Capture the confirmation user input of `replace /W /U`,
        rem    skipping the prompt `Press any key to continue . . .`: */
        set "CHAR=%%L"
        rem /* Leave loop upon first iteration, because we are not
        rem    interested in any replacement results and summaries: */
        goto :SKIP
    )
)
:SKIP
setlocal EnableDelayedExpansion
rem // Display user entry without trailing line-break:
> con < nul set /P ="!CHAR!"
rem // Check user input case-insensitively:
if /I "!CHAR!"=="Y" exit /B 0
if /I "!CHAR!"=="N" exit /B 1
endlocal
rem // Repeat prompt in case of invalid entry:
goto :CONFIRM
echo/

endlocal
exit /B