1

Тема: CMD/BAT: вывод текста без переноса на новую строку

Как известно, команда «echo», используемая в пакетных файлах для вывода текста, добавляет символы 0x0D, 0x0A (Carriage Return и Line Feed). Чтобы избежать этого, обычно используют какую-либо внешнюю утилиту, эмулирующую вывод и не добавляющую перенос на новую строку. Однако существует способ добиться такого поведения, используя только встроенные средства командного процессора (обратите внимание на пробелы в конце команд!):

@echo off

<nul set /p strTemp=Весь этот текст 
<nul set /p strTemp=будет находиться 
<nul set /p strTemp=на одной строке.

Весь этот текст будет находиться на одной строке.

То есть, мы используем ввод с устройства «nul» в команде «set /p» для эмуляции команды «echo».

Если нужна новая строка, следует, после использования вышеуказанного метода, использовать команду «echo.»:

@echo off

<nul set /p strTemp=Весь этот текст 
<nul set /p strTemp=будет находиться 
<nul set /p strTemp=на одной строке.

echo.
echo А этот - уже на следующей.

Весь этот текст будет находиться на одной строке.
А этот - уже на следующей.

Содержимое использованной переменной окружения «strTemp» при этом не изменяется:

@echo off

set StrTemp=Некое Значение
echo Before: strTemp=[%strTemp%].

<nul set /p strTemp=Весь этот текст 
<nul set /p strTemp=будет находиться 
<nul set /p strTemp=на одной строке.

echo.
echo After: strTemp=[%strTemp%].

Before: strTemp=[Некое Значение].
Весь этот текст будет находиться на одной строке.
After: strTemp=[Некое Значение].

Данный метод можно оформить, например, в виде внутренней процедуры (EchoWithoutCrLf), что делает его нагляднее за счёт использования кавычек:

@echo off

call :EchoWithoutCrLf "Весь этот текст "
call :EchoWithoutCrLf "будет находиться "
call :EchoWithoutCrLf "на одной строке."

exit /b 0
rem ==========================================================================

rem ==========================================================================
rem Процедура EchoWithoutCrLf
rem 
rem %1 : текст для вывода.
rem ==========================================================================
:EchoWithoutCrLf
    
    <nul set /p strTemp=%~1
    exit /b 0
rem ==========================================================================

Весь этот текст будет находиться на одной строке.

Используя требуемое количество символов BackSpace (0x08) при выводе текста, и аналогичное количество символов Space (0x20) и BackSpace (0x08) после вывода текста, можно получить вывод поверх предыдущего текста. Как это выглядит:
1) выводится некий текст длины S символов + S символов BackSpace (для того, чтобы вернуть курсор на начало строки);
2) делается пауза для демонстрации выведенного текста;
3) выводится S символов пробелов (чтобы гарантированно затереть ранее выведенный текст) + S символов BackSpace (для того, чтобы опять вернуть курсор на начало строки).

Здесь важно помнить, что если длина текста превысит ширину окна консоли, то текст будет автоматически перенесён на следующую строку, и вернуть курсор в первоначальную позицию, без сторонних средств, не удастся.

1. Использование команды «set /p»:

@echo off

<nul set /p strTemp=Можно делать вывод
pause > nul
<nul set /p strTemp=                  

<nul set /p strTemp=и поверх предыдущего текста.
pause > nul
<nul set /p strTemp=                            

2. Использование процедуры «EchoWithoutCrLf»:

@echo off

call :EchoWithoutCrLf "Можно делать вывод"
pause > nul
call :EchoWithoutCrLf "                  "

call :EchoWithoutCrLf "и поверх предыдущего текста."
pause > nul
call :EchoWithoutCrLf "                            "

exit /b 0
rem ==========================================================================

rem ==========================================================================
rem Процедура EchoWithoutCrLf
rem 
rem %1 : текст для вывода.
rem ==========================================================================
:EchoWithoutCrLf
    
    <nul set /p strTemp=%~1
    exit /b 0
rem ==========================================================================

Чтобы не рассчитывать потребное количество BackSpace'ов и Space'ов вручную, подобный вывод также можно оформить в виде процедуры, точнее, пары основных процедур (процедура «EchoOut» выводит текст, процедура «EchoClear» «стирает» его) и нескольких вспомогательных («FillBackSpace», «FillSpace», «FillString» и «StrLen», см. комментарии в коде; в процедурах используется методика CMD/BAT: возврат значений из процедур через параметры). Вызывайте их с одной и той же строкой в качестве параметра (в процедуре «EchoClear» переданная строка используется только для подсчёта длины). Таким образом, например,  можно эмулировать команду «pause» без переноса курсора на новую строку после вывода текста «Для продолжения нажмите любую клавишу . . .». Также, чтобы каждый раз не дублировать один и тот же текст при вызове процедур «EchoOut»/«EchoClear», можно использовать переменную окружения:

@echo off

set strText="Конечно, Ваш редактор должен позволять вводить символ BackSpace (0x08)."
call :EchoOut %strText%
pause > nul
call :EchoClear %strText%

set strText="Например, редактор Far Manager'а сие позволяет, используйте Ctrl-Q, Ctrl-H."
call :EchoOut %strText%
pause > nul
call :EchoClear %strText%

rem Эмулируем команду PAUSE без переноса на новую строку
set strText="Нажмите любую клавишу для продолжения..."
call :EchoOut  %strText%
pause > nul
call :EchoClear %strText%

echo Продолжаем вывод в той же строке...

exit /b 0
rem ==========================================================================

rem ==========================================================================
rem Процедура EchoOut
rem 
rem %1 : текст для вывода.
rem ==========================================================================
:EchoOut
    rem ---- Получаем строку, заполненную символами BackSpace (0x08) в переменную strBackSpace
    call :FillBackSpace "%~1" strBackSpace
    
    <nul set /p strTemp=%~1%strBackSpace%
    
    exit /b 0
rem ==========================================================================

rem ==========================================================================
rem Процедура EchoClear
rem 
rem %1 : текст для очистки; используется только для получения длины строки.
rem ==========================================================================
:EchoClear
    rem ---- Получаем строку, заполненную символами Space (0x20) в переменную strSpace
    call :FillSpace "%~1" strSpace
    
    rem ---- Получаем строку, заполненную символами BackSpace (0x08) в переменную strBackSpace
    call :FillBackSpace "%~1" strBackSpace
    
    <nul set /p strTemp=%strSpace%%strBackSpace%
    
    exit /b 0
rem ==========================================================================


rem ==========================================================================
rem Процедура FillBackSpace
rem 
rem %1 : строка текста; используется только для получения длины строки.
rem %2 : имя переменной для возврата строки, заполненной символами BackSpace (0x08)
rem ==========================================================================
:FillBackSpace
    setlocal enableextensions enabledelayedexpansion
    
    rem ---- Получаем длину переданной строки в переменную intStrLen ---------
    call :StrLen "%~1" intStrLen
    
    rem ---- Получаем строку, заполненную символами BackSpace (0x08) в переменную strTempString
    call :FillString "" %intStrLen% strTempString
    
    endlocal & set %~2=%strTempString%
    exit /b 0
rem ==========================================================================

rem ==========================================================================
rem Процедура FillSpace
rem 
rem %1 : строка текста; используется только для получения длины строки.
rem %2 : имя переменной для возврата строки, заполненной символами Space (0x20)
rem ==========================================================================
:FillSpace
    setlocal enableextensions enabledelayedexpansion
    
    rem ---- Получаем длину переданной строки в переменную intStrLen ---------
    call :StrLen "%~1" intStrLen
    
    rem ---- Получаем строку, заполненную символами Space (0x20) в переменную strTempString
    call :FillString " " %intStrLen% strTempString
    
    endlocal & set %~2=%strTempString%
    exit /b 0
rem ==========================================================================

rem ==========================================================================
rem Процедура FillString
rem 
rem %1 : символ или строка текста
rem %2 : число повторов
rem %3 : имя переменной для возврата строки, заполненной %2 раз символами %1
rem ==========================================================================
:FillString
    setlocal enableextensions enabledelayedexpansion
    
    set strTempString=
    
    for /l %%i in (1,1,%~2) do (
        set strTempString=!strTempString!%~1
    )
    
    endlocal & set %~3=%strTempString%
    exit /b 0
rem ==========================================================================

rem ==========================================================================
rem Процедура StrLen
rem 
rem %1 : строка текста
rem %2 : имя переменной для возврата длины строки
rem ==========================================================================
:StrLen
    setlocal enableextensions enabledelayedexpansion
    
    set strString=%~1
    set /a intStrLen = 0
    
    for /l %%i in (0,1,1024) do (
        set strTempString=!strString:~%%i,1!
        
        if [!strTempString!] neq [] (
            set /a intStrLen+=1
        )
    )
    
    endlocal & set /a %~2=%intStrLen%
    exit /b 0
rem ==========================================================================

Осталось добавить, что использование данного метода я увидел в коде amel27 на форуме OSzone.net.

2

Re: CMD/BAT: вывод текста без переноса на новую строку

Вернёмся немного назад, а именно к:

alexii пишет:

Здесь важно помнить, что если длина текста превысит ширину окна консоли, то текст будет автоматически перенесён на следующую строку, и вернуть курсор в первоначальную позицию, без сторонних средств, не удастся.

Одним из вариантов может быть принудительное ограничение длины выводимого текста шириной окна консоли. Для этого добавим ещё одну вспомогательную процедуру «GetConsoleWidth», которая будет возвращать ширину окна консоли (на самом деле будем возвращать, как нам требуется, на единицу меньше), и немного изменим процедуры «EchoOut»/«EchoClear»:

@echo off

set strText="Естественно, если длина выведенного текста превысит (ширина окна консоли командного процессора - 1) символов, произойдёт автоматический перенос текста на следующую строку. В этом нет отличий от команды ECHO."

call :EchoOut  %strText%
pause > nul
call :EchoClear %strText%

set strText="Чтобы этого не произошло, ограничим длину выводимого текста рамками ширины окна консоли команлного процессора, то есть, обрежем этот текст."

call :EchoOut  %strText%
pause > nul
call :EchoClear %strText%

exit /b 0
rem ==========================================================================

rem ==========================================================================
rem Процедура EchoOut
rem 
rem %1 : текст для вывода.
rem ==========================================================================
:EchoOut
    setlocal enableextensions enabledelayedexpansion
    
    set strText=%~1
    
    rem ---- Получаем строку, заполненную символами BackSpace (0x08) в переменную strBackSpace
    call :FillBackSpace "%strText%" strBackSpace
    
    call :GetConsoleWidth intCols
    
    <nul set /p strTemp=!strText:~0,%intCols%!%strBackSpace%
    
    endlocal
    exit /b 0
rem ==========================================================================


rem ==========================================================================
rem Процедура EchoClear
rem 
rem %1 : текст для очистки; используется только для получения длины строки.
rem ==========================================================================
:EchoClear
    rem ---- Получаем строку, заполненную символами Space (0x20) в переменную strSpace
    call :FillSpace "%~1" strSpace
    
    rem ---- Получаем строку, заполненную символами BackSpace (0x08) в переменную strBackSpace
    call :FillBackSpace "%~1" strBackSpace
    
    call :GetConsoleWidth intCols
    
    <nul set /p strTemp=!strSpace:~0,%intCols%!!strBackSpace:~0,%intCols%!
    
    exit /b 0
rem ==========================================================================


rem ==========================================================================
rem Процедура FillBackSpace
rem 
rem %1 : строка текста; используется только для получения длины строки.
rem %2 : имя переменной для возврата строки, заполненной символами BackSpace (0x08)
rem ==========================================================================
:FillBackSpace
    setlocal enableextensions enabledelayedexpansion
    
    rem ---- Получаем длину переданной строки в переменную intStrLen ---------
    call :StrLen "%~1" intStrLen
    
    rem ---- Получаем строку, заполненную символами BackSpace (0x08) в переменную strTempString
    call :FillString "" %intStrLen% strTempString
    
    endlocal & set %~2=%strTempString%
    exit /b 0
rem ==========================================================================

rem ==========================================================================
rem Процедура FillSpace
rem 
rem %1 : строка текста; используется только для получения длины строки.
rem %2 : имя переменной для возврата строки, заполненной символами Space (0x20)
rem ==========================================================================
:FillSpace
    setlocal enableextensions enabledelayedexpansion
    
    rem ---- Получаем длину переданной строки в переменную intStrLen ---------
    call :StrLen "%~1" intStrLen
    
    rem ---- Получаем строку, заполненную символами Space (0x20) в переменную strTempString
    call :FillString " " %intStrLen% strTempString
    
    endlocal & set %~2=%strTempString%
    exit /b 0
rem ==========================================================================

rem ==========================================================================
rem Процедура FillString
rem 
rem %1 : символ или строка текста
rem %2 : число повторов
rem %3 : имя переменной для возврата строки, заполненной %2 раз символами %1
rem ==========================================================================
:FillString
    setlocal enableextensions enabledelayedexpansion
    
    set strTempString=
    
    for /l %%i in (1,1,%~2) do (
        set strTempString=!strTempString!%~1
    )
    
    endlocal & set %~3=%strTempString%
    exit /b 0
rem ==========================================================================

rem ==========================================================================
rem Процедура StrLen
rem 
rem %1 : строка текста
rem %2 : имя переменной для возврата длины строки
rem ==========================================================================
:StrLen
    setlocal enableextensions enabledelayedexpansion
    
    set strString=%~1
    set /a intStrLen = 0
    
    if "!strString!" neq "" (
        for /l %%i in (0,1,1024) do (
            set strTempString=!strString:~%%i,1!
            rem echo strTempString=[%strTempString%]
            
            if [!strTempString!] neq [] (
                set /a intStrLen+=1
            )
        )
    )
    
    endlocal & set /a %~2=%intStrLen%
    exit /b 0
rem ==========================================================================

rem ==========================================================================
rem Процедура GetConsoleWidth
rem 
rem %1 : имя переменной для возврата (ширина окна консоли - 1)
rem ==========================================================================
:GetConsoleWidth
    setlocal enableextensions enabledelayedexpansion
    
    rem ---- Получаем (ширина окна консоли - 1) ------------------------------
    for /f "tokens=2" %%i in ('start /wait /b mode con: ^| find.exe /i "Столбцы"') do set /a intCols=%%i - 1
    
    endlocal & set /a %~1=%intCols%
    exit /b 0
rem ==========================================================================