Comparação de arquivo em lote de variável com constante falha

Eu quero escrever um simples pedaço de código para obter um bom “carimbo de hora” formatado. Obtendo o tempo em minhas duas variables Start e End funciona bem. Também posso imprimir como 0: 0: 0. Eu quero ter um zero à esquerda se for menor que 10, mas aparentemente eu recebo um erro dizendo ‘o parâmetro 10 não foi encontrado ou foi erroneamente usado’. Descobri que esta parece ser a variável a ser comparada, mas não consegui consertar isso. Alguma ideia?

 @ECHO OFF REM Time Calculation FOR /F "skip=1 tokens=1-6" %%A IN ('WMIC Path Win32_LocalTime Get Day^,Hour^,Minute^,Second /Format:table ^| findstr /r "."') DO ( set Day=%%A set Hour=%%B set Minute=%%C set Second=%%D ) set /a Start=%Day%*8640000+%Hour%*360000+%Minute%*6000+%Second%*100 @ECHO ON ping 8.8.8.8 -n 11 @ECHO OFF FOR /F "skip=1 tokens=1-6" %%A IN ('WMIC Path Win32_LocalTime Get Day^,Hour^,Minute^,Second /Format:table ^| findstr /r "."') DO ( set Day=%%A set Hour=%%B set Minute=%%C set Second=%%D ) set /a End=%Day%*8640000+%Hour%*360000+%Minute%*6000+%Second%*100 set /a Diff=%End%-%Start% set /a Diff=(%Diff%)/100 set /a DiffSec=%Diff%%%60 set /a Diff=(%Diff%-%Diff%%%60)/60 set /a DiffMin=%Diff%%%60 set /a Diff=(%Diff%-%Diff%%%60)/60 set /a DiffHrs=%Diff% ECHO Laufzeit Auftraege loeschen: %DiffHrs%:%DiffMin%:%DiffSec% :: format with leading zeroes if %DiffSec% LSS 10 (ECHO "LESS 10")else %DiffSec% LSS 1 (ECHO "LESS 1") ::if %DiffSec% LSS 10 (set DiffSec=0%DiffSec%)else [%DiffSec%] LSS 1 (set DiffSec=00) ::if %DiffMin% LSS 10 (set DiffMin=0%DiffMin%)else [%DiffMin%] LSS 1 (set DiffMin=00) ::if %DiffHrs% LSS 10 (set DiffHrs=0%DiffHrs%)else [%DiffHrs%] LSS 1 (set DiffHrs=00) ECHO Laufzeit Auftraege loeschen: %DiffHrs%:%DiffMin%:%DiffSec% 

1. Depurando um arquivo de lote

Para depurar um arquivo de lote para localizar erros de syntax no código, é recomendável executar o arquivo em lote a partir de uma janela de prompt de comando após cada echo off ter sido modificado para ser echo ON ou removido do arquivo em lote ou comentado com o comando REM .

Por padrão, o interpretador de comandos do Windows produz cada linha de comando ou um bloco de comando inteiro começando com ( e terminando com a correspondência ) após analisar e pré-processar quais variables ​​de ambiente referenciadas com %variable% (expansão imediata) já foram substituídas pelo valor atual da variável de ambiente antes executando a linha de comando / bloco.

Com @echo off na parte superior do arquivo em lote, esse comportamento padrão é desativado, pelo que @ no início da linha de comando também desativa a saída dessa primeira linha de comando. É claro que isso é bem-vindo quando o desenvolvimento do arquivo de lote estiver concluído e o arquivo de lote estiver funcionando bem. Mas para depurar um arquivo em lote que não funciona como esperado, é melhor ver também as linhas de comando realmente executadas pelo interpretador de comandos para descobrir onde a execução do arquivo em lote é encerrada inesperadamente devido a um erro.

O comportamento do ECHO é explicado muito brevemente na saída de ajuda na execução do echo /? de dentro de uma janela de prompt de comando.

Abrir uma janela de prompt de comando resulta na boot implícita do cmd.exe com a opção /K para manter o processo de comando em execução e a janela do console aberta após a execução de um arquivo em lote ou aplicativo concluído.

Uma exceção é quando o arquivo em lote contém o comando exit sem o parâmetro /B porque, nesse caso, o processo de comando atual é sempre encerrado independentemente da hierarquia de chamadas. exit /B é igual a goto :EOF e deve ser usado em vez de apenas exit exceto que há uma boa razão para usar apenas exit . exit /B e goto :EOF requer ambas as extensões de comando como ativadas por padrão no Windows.

Clicar duas vezes em um arquivo em lotes resulta em iniciar o cmd.exe com a opção /C para fechar o processo de comando e sua janela de console automaticamente quando a execução do aplicativo ou arquivo em lote for concluída independentemente do motivo da execução de um arquivo em lote. Esse comportamento ao fechar automaticamente a janela do console não é bom para debugging de um arquivo em lotes porque a mensagem de erro não pode ser vista quando a execução do arquivo em lotes foi encerrada devido a um erro de syntax.

Para mais detalhes sobre as opções do interpretador de comandos do Windows, execute em uma janela de prompt de comando o comando: cmd /?

Como sair intencionalmente a execução de um arquivo de lote usando goto :EOF (dois pontos aqui é importante a título de exceção) ou exit /B (apenas um alias interno para goto :EOF ) é explicado na ajuda desses dois comandos exibidos correndo goto /? e exit /? em uma janela de prompt de comando.

Para depurar um arquivo de lote maior, pode ser útil usar um goto temporariamente adicionado no topo do arquivo de lote para pular para um determinado bloco e um goto :EOF para sair do processamento em lote após o bloqueio para debugging.

A propósito: :: é um label inválido frequentemente usado para comentários em arquivos em lote, já que as linhas de label nunca são exibidas na execução de um arquivo em lote. Mas no bloco de comandos de um FOR, os labels de loop não podem ser usados ​​porque o interpretador de comandos do Windows não consegue interpretar corretamente um loop FOR com labels dentro do bloco de comandos. Por esse motivo, é melhor usar o comando REM (observação) para comentários, pois esse comando é projetado para comentários em arquivos em lote e realmente funciona em qualquer lugar em um arquivo em lotes.

2. Erro no arquivo em lotes

Ao executar o arquivo em lote postado em questão com @ECHO OFF comentado substituindo-o por rem @echo off (executar uma substituição no editor de texto) de dentro de uma janela de prompt de comandos, ele pode ser visto facilmente em qual linha o erro ocorre:

 if %DiffSec% LSS 10 (ECHO "LESS 10")else %DiffSec% LSS 1 (ECHO "LESS 1") 

Se o valor atual da variável de ambiente DiffSec não for menor que 10 , a ramificação ELSE será executada pelo interpretador de comandos do Windows, que começa com o número 10 .

O interpretador de comandos do Windows não pode encontrar um aplicativo com esse nome no diretório atual ou em qualquer diretório especificado na lista de diretórios separados por ponto-e-vírgula da variável de ambiente PATH com uma extensão de arquivo especificada na lista de extensão de arquivo separada por ponto-e-vírgula da variável de ambiente PATHEXT .

O erro está aqui, obviamente, no comando IF ausente para a próxima comparação. Então o código correto seria

 if %DiffSec% LSS 10 (ECHO "LESS 10") else if %DiffSec% LSS 1 ECHO "LESS 1" 

Isso seria mais fácil de ler ao escrever a condição em várias linhas:

 if %DiffSec% LSS 10 ( ECHO "LESS 10" ) else if %DiffSec% LSS 1 ( ECHO "LESS 1" ) 

A syntax está agora correta.

Mas a segunda condição não faz sentido, como JosefZ já mencionou em seu comentário. Se o valor de DiffSec for 10 ou maior, resultando na execução do comando IF na ramificação ELSE , essa condição definitivamente também nunca será verdadeira. Então mais sentido faria:

 if %DiffSec% LSS 1 (ECHO LESS 1) else if %DiffSec% LSS 10 ECHO LESS 10 

Ou alternativamente

 if %DiffSec% LSS 1 ( ECHO LESS 1 ) else if %DiffSec% LSS 10 ( ECHO LESS 10 ) 

Para obter mais informações sobre condições válidas do IF ELSE em arquivos em lote, consulte, por exemplo, as respostas em

  • IF ELSE erro de syntax dentro do arquivo em lotes?
  • script em lote – se existir ./sdcard/file.any usando adb

3. Adicione zero à esquerda para números <10

As variables ​​de ambiente são sempre do tipo string. Para expressões aritméticas, o valor de string de uma variável de ambiente é convertido em um inteiro de 32 bits assinado, se possível, e o resultado da expressão aritmética é convertido de volta do inteiro de 32 bits assinado para string.

Também uma condição IF como if %DiffSec% LSS 10 fosse expandido antes da execução por exemplo para if 5 LSS 10 resultasse na conversão de 5 (0x35) de string para inteiro e 10 (0x31 0x30) também de string para inteiro para comparar os dois números inteiros.

Portanto, seria um pouco mais rápido evitar uma comparação desse tipo se isso for possível.

É muito fácil adicionar um zero inicial a um número menor que 10 sem realmente testar o valor usando a substituição de string.

Primeiro, o valor atual da variável de ambiente é prefixado com um (para um número de dois dígitos) ou mais 0 (para 3, 4 ou até mais dígitos).

 set "DiffSec=0%DiffSec%" 

Em seguida, os últimos caracteres X como 2 para um número de dois dígitos são atribuídos a partir do valor atual da variável de ambiente para a variável de ambiente.

 set "DiffSec=%DiffSec:~-2%" 

A substituição de strings é explicada na ajuda do comando SET output executando em uma janela de prompt de comandos set /? .

O resultado das duas linhas é que o DiffSec tem valores de 0 a 99 após essas duas linhas sempre um número de dois dígitos no intervalo de 00 a 99 .

4. Análise de uma expressão aritmética

Uma expressão aritmética que é a string após set /a é interpretada pelo interpretador de comandos do Windows completamente diferente de outras strings.

Espaços e tabulações são separadores de palavras, mas não têm mais nenhum significado especial. Portanto, é aconselhável usar espaços para tornar a expressão aritmética melhor legível.

Então há muitos operadores que estão listados na ajuda do comando SET exibido na execução em uma janela de prompt de comandos set /? .

Números inteiros decimais, octais e hexadecimais são interpretados em uma expressão aritmética como inteiros.

E, por último, cada outra string é interpretada como o nome de uma variável de ambiente cujo valor atual é convertido de string para inteiro.

Por essa razão, não é aconselhável usar expansão imediata ou retardada em uma expressão aritmética.

O valor de referência de uma variável de ambiente com %variable% em uma expressão aritmética não é bom quando usado em um bloco de comando no qual o valor atual da variável de ambiente substitui a referência de variável já na análise do bloco de comando inteiro antes de executar o primeiro comando.

Valor de referência de uma variável de ambiente com !variable! dentro de uma expressão aritmética também não é bom porque requer a ativação da expansão atrasada que resulta no tratamento de pontos de exclamação em strings não mais como caracteres literais.

Então, é melhor simplesmente escrever os nomes das variables ​​em uma expressão aritmética sem cercar sinais de porcentagem ou pontos de exclamação se isso for possível porque o nome da variável não contém um caractere de espaço e começa com um caractere que não pode ser interpretado como caractere inteiro pelo Windows intérprete de comando.

Veja também resposta em Por que não há saída de seqüência de caracteres com ‘echo% var%’ depois de usar ‘set var = text’ na linha de comando? para detalhes sobre como atribuir um valor a uma variável de ambiente usando apenas set ou set /P (prompt) ou set /A (expressão aritmética).

5. Código fixo e otimizado

O código em questão pode ser corrigido e otimizado para este código:

 @echo off rem Time Calculation for /F "skip=1 tokens=1-4" %%A in ('%SystemRoot%\System32\wbem\wmic.exe PATH Win32_LocalTime GET Day^,Hour^,Minute^,Second') do ( set Day=%%A set Hour=%%B set Minute=%%C set Second=%%D ) set /A TimeStart=Day * 86400 + Hour * 3600 + Minute *60 + Second @echo on %SystemRoot%\System32\ping.exe 8.8.8.8 -n 11 @echo off for /F "skip=1 tokens=1-4" %%A in ('%SystemRoot%\System32\wbem\wmic.exe PATH Win32_LocalTime GET Day^,Hour^,Minute^,Second') do ( set Day=%%A set Hour=%%B set Minute=%%C set Second=%%D ) set /A TimeEnd=Day * 86400 + Hour * 3600 + Minute *60 + Second set /A TimeDiff=TimeEnd - TimeStart set /A DiffSec=TimeDiff %% 60 set /A TimeDiff=(TimeDiff - DiffSec) / 60 set /A DiffMin= TimeDiff %% 60 set /A DiffHrs=(TimeDiff - DiffMin) / 60 set "DiffSec=0%DiffSec%" set "DiffSec=%DiffSec:~-2%" set "DiffMin=0%DiffMin%" set "DiffMin=%DiffMin:~-2%" set "DiffHrs=0%DiffHrs%" set "DiffHrs=%DiffHrs:~-2%" echo Time needed for orders deletion: %DiffHrs%:%DiffMin%:%DiffSec% 

Para entender os comandos usados ​​e como eles funcionam, abra uma janela de prompt de comando, execute os seguintes comandos e leia com atenção todas as páginas de ajuda exibidas para cada comando.

  • echo /?
  • for /?
  • ping /?
  • rem /?
  • set /?
  • wmic /?
  • wmic path /?
 if %DiffSec% LSS 10 (ECHO "LESS 10")else IF %DiffSec% LSS 1 (ECHO "LESS 1") 

você precisa de um if depois do else .