简体   繁体   中英

Subversion client hook doesn't show echo

This is my precommit hook. When I start a commit, my external diff tool does pops up, but the cmd window shows nothing, and it doesn't read my keyboard input.

@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

我的subversion配置

Did I miss anything?

According to the help documentation of TortoiseSVN you need to do explicit redirection to the console (device 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. eg

 echo Checking Status > con pause < con > con 

Unfortunately, this does not work with the choice command as it does not seem to accept input redirected from device con :

 >>> choice < con ERROR: The handle is invalid. 

A simple work-around is to use the set /P command which does support redirection from con :

@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

The disadvantage of this is that the entry needs to be terminated with RETURN , in contrast to the choice command, which accepts a single-key input.


Here is a better approach which features a single-key prompt similar to the choice command. This (mis-)uses the xcopy command and its ability to accept redirected input into the copy confirmation prompt that appears when the /W switch is supplied. Take also a look at the explanatory remarks:

@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

If an unexpected input is provided, the prompt is reprinted on the next line and new user input is awaited.


To avoid reprinting the prompt text in case of unexpected user input, use the following script:

@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

This is a comprehensive solution that allows usage of special characters like ! , ^ and " too.


Here is a quite similar solution relying on the replace command, together with its /W switch, also without reprinting the prompt in case of unexpected user input. Since replace prints the character of the key-press in a separate line, there is no need to split it off of another string (opposed to 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM