Como sobrescrever a mesma linha na saída do comando do arquivo em lotes

13

Quero escrever algumas informações de status que substituam a última linha quando eu fizer alguma coisa.

No exemplo de arquivo bat, quero que o texto Step 2sobrescreva Step 1na mesma linha na saída do comando.

echo off

echo Step 1
REM Do some stuff...

echo Step 2
REM do some other stuff...
temor
fonte

Respostas:

8

Este script fará exatamente o que você pediu:

@echo off

setlocal enableextensions enabledelayedexpansion
for /f %%a in ('copy /Z "%~dpf0" nul') do set "ASCII_13=%%a"

set /p "=Step 1" <NUL
REM Do some stuff...

set /p "=!ASCII_13!Step 2" <NUL
REM do some other stuff...

Encontrei esta resposta examinando o código em uma biblioteca de manipulação de caracteres de script em lote escrita por Dave Benham chamada CharLib .

Esta biblioteca é capaz de criar todos os caracteres no intervalo de 1 a 255.

Para obter mais informações, consulte o tópico "É possível converter um caractere em seu valor ASCII?"

Editar 2017-4-26: Parece que o código acima não funciona mais . Não tenho certeza de quando esse comportamento teria mudado, mas definitivamente funcionava. O CRpersonagem após o =agora é retirado pelo setcomando. Ah, parece haver uma mudança na maneira como setfunciona entre o XP e versões posteriores do Windows. Veja a excelente resposta em Substituir linha no arquivo em lotes do Windows? (duplicado) para mais detalhes e exemplos adicionais de códigos.

Uma alternativa, então, é colocar um caractere que não seja um espaço em branco antes de !ASCII_13!e providenciar para que ele seja apagado também, talvez inserindo um backspace (que não seja retirado) seguido por um espaço ou anexando espaços ao final do string de prompt (que não é removida.)

Por exemplo:

@echo off

setlocal enableextensions enabledelayedexpansion
for /f %%a in ('copy /Z "%~dpf0" nul') do set "ASCII_13=%%a"

set /p "=Step 1" <NUL
REM Do some stuff...

set /p "=x!ASCII_13!Step 2 " <NUL
REM do some other stuff...
timfoden
fonte
2
Para mim, isso apenas imprime "Etapa 1Etapa 2" em vez de apenas "Etapa 2" na tela.
galmok
@ galmok Isso também começou a acontecer comigo, mas tenho certeza de que nunca costumava acontecer (pois eu tenho um script que costumava funcionar perfeitamente corretamente recentemente mudou para o comportamento que você descreve.) Parece que o setcomando agora retira qualquer Caracteres CR e LF (bem como espaços) após o =. Tente colocar um caractere não-espaço em branco entre a =ea!ACSII_13!
timfoden
Existe uma maneira de fazer tudo isso ... Na verdade, eu vim para ver se mais alguém notou, porque eu queria ver se eles descobriram até que ponto isso foi. ... Posso escrever uma RESPOSTA no estouro de pilha? OU .. como uma pergunta e respondê-la imediatamente? Suponho que eu poderia escrever em questão sobre o que você poderia fazer com isso. Eu tenho que comer alguma coisa, mas eu atualizarei isso com um link para você.
Brent Rittenhouse
Escreva! ASCII_13! após a Etapa 1 para evitar a remoção, portanto, na etapa 2, você codificaria:set /p "=Step 2 " <NUL
Zimba
6

Para responder à pergunta:

@echo off
CALL :EVALUATE "chr(8)"
SET backspace=%result%
<nul set /p =Step 1
:: DO SOME STUFF....
<nul set /p =%backspace%
<nul set /p =2
:: DO SOME OTHER STUFF....
<nul set /p =%backspace%%backspace%%backspace%%backspace%%backspace%%backspace%
<nul set /p =End   
GOTO:EOF



:EVALUATE           -- evaluate with VBS and return to result variable
::                  -- %~1: VBS string to evaluate
:: extra info: http://groups.google.com/group/alt.msdos.batch.nt/browse_thread/thread/9092aad97cd0f917

@IF [%1]==[] ECHO Input argument missing & GOTO :EOF 

@ECHO wsh.echo "result="^&eval("%~1") > %temp%\evaluate_tmp_67354.vbs 
@FOR /f "delims=" %%a IN ('cscript //nologo %temp%\evaluate_tmp_67354.vbs') do @SET "%%a" 
@DEL %temp%\evaluate_tmp_67354.vbs
::ECHO %result%
@GOTO:EOF

Resultado:

End

Basicamente, o que o script faz, é gravações Step 1, em seguida, vai para trás um lugar, substituições 1, e no final vai completamente para trás e substitui Step 2com End.

Voltando ao exemplo, o caractere ASCII especial 8, que é o backspace. Como não sei como escrevê-lo no cmd, uso a função: EVALUATE, que executa a função VBS Chr () e coloca o caractere backspace na variável result. Se alguém souber uma maneira melhor, por favor, informe.

Davor Josipovic
fonte
11
No PowerShell, você também pode armazenar $host.ui.RawUI.CursorPositionem uma variável e restaurá-la posteriormente, para que o cursor volte para onde estava e sobrescreva sua saída. Apenas uma opção para PowerShell, mas pode ser um pouco mais limpo do que o seu material VBS :-)
mihi
4
@echo off
for /F "tokens=1 delims=# " %%a in ('"prompt #$H# & echo on & for %%b in (1) do rem"') do set "BSPACE=%%a"
<nul set /p =Step 1
ping 127.0.0.1 >nul
<nul set /p =%BSPACE%
<nul set /p =2
ping 127.0.0.1 >nul
<nul set /p =%BSPACE%
<nul set /p =3
ping 127.0.0.1 >nul
<nul set /p =%BSPACE%%BSPACE%%BSPACE%%BSPACE%%BSPACE%%BSPACE%
<nul set /p =End.  
pause

EXPLICAÇÃO:

for /F "tokens=1 delims=# " %%a in ('"prompt #$H# & echo on & for %%b in (1) do rem"') do set "BSPACE=%%a"

isso definirá o caractere de backspace na variável BSPACE. Agora, para ver o resultado, digite:

echo ab%BSPACE%c

resultado: ac

você pode usar essa variável BSPACE mais de uma vez para excluir vários caracteres.

Agora, se você deseja definir um retorno de carro em uma variável, use

for /F "usebackq" %%a in (copiar / Z "% ~ dpf0" nulo) DO (set "cr=%%a")

para ver o resultado, digite: setlocal EnableDelayedExpansion & echo asfasdhlfashflkashflksa!CR!***

a saída será: ***asfasdhlfashflkashflksa

A resposta de @ davour acima também é bonita, mas a resposta de @ timfoden não funcionou para mim.

Sourav Ghosh
fonte
A resposta de @ timfoden não funcionou porque! CR! deve ir no final da etapa anterior. Além disso, em vez de todos esses% BSPACE% s, você também pode! CR! e preencha com espaços em branco:set /p "=.!CR!End. " <NUL&echo:
Zimba
3

Você não pode. Geralmente, você pode conseguir esse tipo de coisa incluindo um caractere de retorno de carro (0x0D) no arquivo que colocará o cursor de volta na primeira coluna na mesma linha. Mas neste caso, não funciona; o CR é comido silenciosamente.

Além disso, obter o CR é meio complicado e pelo menos aqui envolveu um editor de texto. Eu sugiro que você escreva um pequeno programa utilitário que faça isso por você, na verdade não é muito difícil. O pequeno programa C a seguir pode ser suficiente (se você não precisar de Unicode):

#include <stdio.h>

int main(int argc, char* argv[]) {
  if (argc < 2) return 1;
  printf("\r%s", argv[1]);
}

Não faz nada além de imprimir um caractere CR e, em seguida, o texto que você especificar como seu primeiro argumento. Uso da seguinte maneira:

@echo off
<nul set /P X=Step 1
pause>nul 2>nul
over.exe "Step 2"

A última linha é a chamada para esse programa. A segunda linha é o idioma normal do lote para imprimir texto sem quebras de linha à direita (o que é importante nesse caso, caso contrário, você não poderá sobrescrever a linha). Você também pode usar o programa acima nesse local.

Uma maneira levemente hacky, mas a única onde você pode ter certeza de onde acaba, seria usar clsantes da saída da etapa. Vai piscar, mas dessa forma você sempre escreve no canto superior esquerdo. E derrube tudo o que estava visível no console (e é por isso que eu não recomendo); a maioria dos usuários não gosta muito disso.

Joey
fonte
OK, suspeitava que não era possível fazer com a linha de comando limpa. Se eu quiser que um utilitário faça isso, é melhor colocar a coisa inteira em um utilitário. O que eu realmente queria era apenas uma contagem regressiva visível que esperasse 5 segundos, contando cada segundo até terminar.
temor
4
temor: Se você estiver no Vista ou posterior, ele timeout 5funcionará.
Joey
Ótimo! obrigado! timeout 5 funciona!
temor
4
Eu deveria ter colocado o meu verdadeiro problema pela primeira vez ...
temor
Claro que você pode; basta adicionar retorno de carro após a etapa 1! Além disso, aqui estão alguns códigos para obter retorno de carro sem envolver o editor de texto:for /f %%a in ('copy /Z "%~f0" nul') do set "CR=%%a"
Zimba
1

Obter caracteres de nova linha, escreva o próximo passo, volte ao início da linha, escreva a próxima linha:

@echo off
cls
setlocal enabledelayedexpansion
echo Here's how it's done:

for /f %%a in ('copy /Z "%~f0" nul') do set "Newline=%%a"
set /p "=Step 1!Newline!" <NUL
REM Do some stuff...
ping -n 2 localhost > nul
set /p "=Step 2!Newline!" <NUL
ping -n 2 localhost > nul
set /p "=Step 3 " <NUL
REM do some other stuff...
ping -n 2 localhost > nul
echo:
echo Done.
Zimba
fonte
Isso só funcionará se Step 1 Step 2 Step 3todos tiverem o mesmo comprimento. Caso contrário, o texto do primeiro set /ppermanece. Não tenho certeza de como corrigir isso.
Ste
Esse problema pode ser corrigido usando caracteres de espaço em branco adicionais no próximo prompt. para cobrir os caracteres anteriores. Exemplo set /p "=Step 2SPACESPACESPACESPACE!Newline!" <NUL em oposição a set /p "=Step 2!Newline!" <NUL. Troque SPACEpelo personagem.
Ste
Se as linhas anteriores forem mais longas, adicione espaços para apagar caracteres extras, ou retroceda do fim da linha para o início ou apenas retroceda para os caracteres extras.
Zimba
0

Você precisaria de uma biblioteca de cursor de tela como cursesou ncurses, mas não estou muito familiarizado com o uso deles. Ambas as bibliotecas foram desenvolvidas para sistemas Unix, mas você pode encontrá-las para Windows (no ambiente Cygwin ou GnuWin32 ).

Não conheço nenhuma maneira fácil de acessá-los em um arquivo em lotes no estilo DOS. Pode ser melhor programar em uma linguagem compilada. Dê uma olhada no Ncurses HOWTO para ter uma idéia melhor de como será útil.

charlatão quixote
fonte
maldições são basicamente apenas para terminais e emuladores de terminal. Eles realmente não são mapeados para a API do console nativo do Windows.
Joey
NÃO há necessidade de bibliotecas externas; funciona bem com as ferramentas CMD.
Zimba
0

Solução 1

Com o Notepad ++ , é possível inserir o Backspace ←caractere (ASCII 0x08) diretamente, usando seu Painel de inserção de códigos ASCII (Editar> Painel de caracteres).

Minha solução é inserir o [BS]caractere diretamente e, como outras soluções postadas aqui, usá-lo várias vezes para excluir os caracteres anteriores:

Código

@ECHO OFF

SET "BACKSPACE_x7=[BS][BS][BS][BS][BS][BS][BS]"

SET /P "DUMMY_VAR=Step 1" < NUL
REM Do some stuff...

SET /P "DUMMY_VAR=%BACKSPACE_x7%Step 2" < NUL
REM Do some other stuff...

GOTO :EOF

Captura de tela do Notepad ++

Substituição em lote do Windows


Resultado

Saída CMD


Solução 2

Outra possibilidade é usar cecho (comando echo com suporte a cores) para inserir a Carriage Return ↵como um caractere unicode (U + 000D):

Código

@ECHO OFF

SET CARRIAGE_RETURN={\u000D}

cecho Step 1
REM Do some stuff...

cecho %CARRIAGE_RETURN%Step 2
REM Do some other stuff...

GOTO :EOF

Nota: cecho_x64.exeroda significativamente mais rápido na minha máquina do que cecho.exe.

andronoide
fonte