Qual é a maneira correta de testar se um parâmetro está vazio em um arquivo em lotes?

212

Eu preciso testar se uma variável está definida ou não. Eu tentei várias técnicas, mas eles parecem falhar sempre que %1é cercada por citações como é o caso quando %1é "c:\some path with spaces".

IF NOT %1 GOTO MyLabel // This is invalid syntax
IF "%1" == "" GOTO MyLabel // Works unless %1 has double quotes which fatally kills bat execution
IF %1 == GOTO MyLabel // Gives an unexpected GOTO error.

De acordo com este site , esses são os IFtipos de sintaxe suportados . Então, não vejo uma maneira de fazer isso.

IF [NOT] ERRORLEVEL number command
IF [NOT] string1==string2 command
IF [NOT] EXIST filename command
blak3r
fonte
1
Nos meus sistemas (Windows 2003 e Windows 7), if "%1" == "" GOTO MyLabelnão mata fatalmente a execução do script, desde que %1tenha um número par de aspas duplas. Vejo que um número ímpar de aspas duplas %1mata a execução do script com este erro: The syntax of the command is incorrect.A solução abaixo, que usa colchetes para resolver o problema, foi marcada como a resposta correta, mas parece que não está melhorando. . Essa solução também falha com o mesmo erro quando %1há um número ímpar de aspas duplas.
Susam Pal
@SusamPal Interesting. Experimente a versão entre parênteses abaixo e veja se isso funciona. Aquele que eu testei mais. Acabei de atualizar a resposta aceita alguns dias atrás.
blak3r
A resposta de Dan Story parece funcionar muito bem. Eu usei a versão usando colchetes.
precisa saber é o seguinte
um bom exemplo "catch all": stackoverflow.com/questions/830565/… abrangendo arquivo / diretório e combinação genérica de seqüência de caracteres / número no argumento.
Tão frustrante - IF DEFINEDtrabalhar apenas em variáveis ​​de ambiente em vez de variáveis ​​de script é um desperdício de potencial!
precisa saber é o seguinte

Respostas:

290

Use colchetes em vez de aspas:

IF [%1] == [] GOTO MyLabel

Os parênteses são inseguros : use apenas colchetes.

Dan Story
fonte
11
parênteses não são seguros! Conteúdo, como &no %1vai quebrar a linha
jeb
14
O uso de outros caracteres como if #%1#==##ou if [%1]==[]é bom, desde que não haja espaços no argumento, caso contrário, você receberá um …was unexpected at this timeerro.
Synetech 04/04
12
E usar "%~1"é realmente uma abordagem melhor, como mencionei na minha resposta.
jamesdlin
8
Pessoal, @jamesdlin está correto. Essa abordagem não funcionará se o texto variável tiver espaços ou existir ==. Use a resposta dele.
James Ko
4
Você não deixa claro isso it DOES NOT work with spaces. Use a resposta @jamesdlin.
JB. Com a Monica.
153

Você pode usar:

IF "%~1" == "" GOTO MyLabel

para retirar o conjunto externo de aspas. Em geral, esse é um método mais confiável do que usar colchetes, porque funcionará mesmo que a variável tenha espaços.

jamesdlin
fonte
9
Esta é realmente uma boa maneira de fazê-lo. Ao usar ~, você tira as aspas externas, se estiverem presentes, mas depois as adiciona manualmente (garantindo apenas um conjunto). Além disso, você tem de usar aspas se o argumento contém espaços (Eu aprendi isso da maneira mais difícil quando a minha versão anterior do uso de #argumentos ou suportes em vez de aspas causado com espaços para lançar um …was unexpected at this timeerro.)
Synetech
4
isso também é aplicável a variáveis ​​gerais como% MYVAR% e não a argumentos como% 1?
simpleuser
4
@simpleuser Opa, você está certo, ele não funciona com variáveis ​​de ambiente gerais.
Jamesdlin 17/08
1
atente para seqüências de caracteres que usam double '"' como uma sequência de escape - o script falhará ao tentar a comparação. Por exemplo, ""this string will crash the comparison""Double '"' é a sequência de escape adequada e fazer uma substituição em double '"' falhará se o A string está vazia.
Tydaeus 26/10
1
@ Amálgovinus Você provavelmente poderia passar a variável como argumento para uma sub-rotina e deixar a sub-rotina usar "%~1".
jamesdlin
38

Uma das melhores soluções semi é copiar %1para uma variável e usar a expansão atrasada, como delayedExp. está sempre seguro contra qualquer conteúdo.

set "param1=%~1"
setlocal EnableDelayedExpansion
if "!param1!"=="" ( echo it is empty )
rem ... or use the DEFINED keyword now
if defined param1 echo There is something

A vantagem disso é que lidar com o param1 é absolutamente seguro.

E a configuração do param1 funcionará em muitos casos, como

test.bat hello"this is"a"test
test.bat you^&me

Mas ainda falha com conteúdos estranhos como

test.bat ^&"&

Ser capaz de obter uma resposta 100% correta para a existência

Ele detecta se %1está vazio, mas, para alguns conteúdos, não pode buscá-lo.
Isso também pode ser útil para distinguir entre um vazio %1e outro com "".
Ele usa a capacidade do CALLcomando falhar sem interromper o arquivo em lotes.

@echo off
setlocal EnableDelayedExpansion
set "arg1="
call set "arg1=%%1"

if defined arg1 goto :arg_exists

set "arg1=#"
call set "arg1=%%1"
if "!arg1!" EQU "#" (
    echo arg1 exists, but can't assigned to a variable
    REM Try to fetch it a second time without quotes
    (call set arg1=%%1)
    goto :arg_exists
)

echo arg1 is missing
exit /b

:arg_exists
echo arg1 exists, perhaps the content is '!arg1!'

Se você deseja ser 100% à prova de balas para buscar o conteúdo, pode ler Como receber até os parâmetros mais estranhos da linha de comando?

jeb
fonte
18

De IF / ?:

Se as extensões de comando estiverem ativadas, se as alterações forem as seguintes:

IF [/I] string1 compare-op string2 command
IF CMDEXTVERSION number command
IF DEFINED variable command

......

A condicional DEFINED funciona exatamente como EXISTS, exceto que recebe um nome de variável de ambiente e retorna true se a variável de ambiente estiver definida.

Andy Morris
fonte
1
Sim, eu realmente tentei essa abordagem e, pelo que pude dizer, isso só funciona com variáveis ​​de AMBIENTE. Portanto, não funcionou com% 1 ou uma variável definida dentro do arquivo em lotes.
blak3r
2
Isso é verdade apenas para %1e para os gostos. "Variáveis ​​definidas dentro do arquivo em lotes" são na verdade variáveis ​​de ambiente enquanto o arquivo em lote está em execução, e você pode usá IF DEFINED-las.
Zb226
8

Infelizmente, não tenho reputação suficiente para comentar ou votar nas respostas atuais, pois tive que escrever minhas próprias.

Originalmente, a pergunta do OP dizia "variável" em vez de "parâmetro", o que ficou muito confuso, especialmente porque esse era o link número um no google para pesquisar como testar variáveis ​​em branco. Desde a minha resposta original, Stephan editou a pergunta original para usar a terminologia correta, mas em vez de excluir minha resposta, decidi deixá-la para ajudar a esclarecer qualquer confusão, especialmente no caso do Google ainda enviar pessoas aqui para variáveis ​​também:

% 1 NÃO É VARÁVEL! É UM PARÂMETRO DE LINHA DE COMANDO.

Distinção muito importante. Um único sinal de porcentagem com um número depois que se refere a um parâmetro de linha de comando e não a uma variável.

As variáveis ​​são definidas usando o comando set e são recuperadas usando sinais de 2 por cento - um antes e um depois. Por exemplo% myvar%

Para testar uma variável vazia, use a sintaxe "se não definida" (os comandos explicitamente para variáveis ​​não requerem sinais de porcentagem), por exemplo:

set myvar1=foo  
if not defined myvar1 echo You won't see this because %myvar1% is defined.  
if not defined myvar2 echo You will see this because %myvar2% isn't defined.

(Se você quiser testar os parâmetros da linha de comando, recomendo consultar a resposta de jamesdlin.)

AutoMattTick
fonte
1
você está certo. Mas a maneira correta teria sido apenas corrigir o título de if variable is emptypara if parameter is empty. Eu acabei de fazer isso. (com o risco de invalidar algumas das respostas, que fizeram variáveis cheque em vez de parâmetros e por isso são de qualquer maneira inválida, uma vez que não respondeu a pergunta)
Stephan
1
OK, obrigado, Stephan, não sabia que você poderia fazer isso. Editei minha resposta para refletir a atualização.
AutoMattTick 27/07
6

Use "IF variável comando" para testar a variável no arquivo em lotes.

Mas se você quiser testar os parâmetros em lote, tente os códigos abaixo para evitar entradas complicadas (como "1 2" ou ab ^> cd)

set tmp="%1"
if "%tmp:"=.%"==".." (
    echo empty
) else (
    echo not empty
)
zackz
fonte
5

Eu testei com o código abaixo e está tudo bem.

@echo off

set varEmpty=
if not "%varEmpty%"=="" (
    echo varEmpty is not empty
) else (
    echo varEmpty is empty
)
set varNotEmpty=hasValue
if not "%varNotEmpty%"=="" (
    echo varNotEmpty is not empty
) else (
    echo varNotEmpty is empty
)
Kate
fonte
Sua solução falha quando a variável contém uma cotação. Btw existe a sintaxe IF DEFINED varpor uma razão
jeb
4

Eu costumo usar isso:

IF "%1."=="." GOTO MyLabel

Se% 1 estiver vazio, o FI comparará "." para "." que avaliará como verdadeiro.

aforia
fonte
4
Isto está incorreto. É o mesmo que: SE "% 1" == "" o motivo desta postagem foi se o próprio% 1 tem aspas, isso falhará.
blak3r
Você está tentando testar se% 1 está vazio ou se possui aspas? A questão não é clara. Minha resposta funciona se nada for especificado na linha de comando para% 1.
Aphoria 31/03
> Você está tentando testar se% 1 está vazio ou se possui aspas? @aphoria, tem que ser capaz de lidar com ambos.
21412 Synetech
Eu usei isso para descobrir se% 1% havia sido definido, funcionou muito bem para mim. Obrigado, boa resposta!
Charlie A
4

Criei esse pequeno script em lote com base nas respostas aqui, pois existem muitas válidas. Sinta-se à vontade para adicionar isso, desde que você siga o mesmo formato:

REM Parameter-testing

Setlocal EnableDelayedExpansion EnableExtensions

IF NOT "%~1"=="" (echo Percent Tilde 1 failed with quotes) ELSE (echo SUCCESS)
IF NOT [%~1]==[] (echo Percent Tilde 1 failed with brackets) ELSE (echo SUCCESS)
IF NOT  "%1"=="" (echo Quotes one failed) ELSE (echo SUCCESS)
IF NOT [%1]==[] (echo Brackets one failed) ELSE (echo SUCCESS)
IF NOT "%1."=="." (echo Appended dot quotes one failed) ELSE (echo SUCCESS)
IF NOT [%1.]==[.] (echo Appended dot brackets one failed) ELSE (echo SUCCESS)

pause
kayleeFrye_onDeck
fonte
4

String vazia é um par de double-quotes/ "", podemos apenas testar o comprimento:

set ARG=%1
if not defined ARG goto nomore

set CHAR=%ARG:~2,1%
if defined CHAR goto goon

então teste 2 caracteres contra double-quotes:

if ^%ARG:~1,1% == ^" if ^%ARG:~0,1% == ^" goto blank
::else
goto goon

Aqui está um script em lote com o qual você pode jogar. Eu acho que pega corretamente a corda vazia.

Este é apenas um exemplo, você só precisa personalizar 2 (ou 3?) Etapas acima, de acordo com o seu script.

@echo off
if not "%OS%"=="Windows_NT" goto EOF
:: I guess we need enableExtensions, CMIIW
setLocal enableExtensions
set i=0
set script=%0

:LOOP
set /a i=%i%+1

set A1=%1
if not defined A1 goto nomore

:: Assumption:
:: Empty string is (exactly) a pair of double-quotes ("")

:: Step out if str length is more than 2
set C3=%A1:~2,1%
if defined C3 goto goon

:: Check the first and second char for double-quotes
:: Any characters will do fine since we test it *literally*
if ^%A1:~1,1% == ^" if ^%A1:~0,1% == ^" goto blank
goto goon

:goon
echo.args[%i%]: [%1]
shift
goto LOOP

:blank
echo.args[%i%]: [%1] is empty string
shift
goto LOOP

:nomore
echo.
echo.command line:
echo.%script% %*

:EOF

Este resultado do teste de tortura:

.test.bat :: ""  ">"""bl" " "< "">"  (")(") "" :: ""-"  " "( )"">\>" ""
args[1]: [::]
args[2]: [""] is empty string
args[3]: [">"""bl" "]
args[4]: ["< "">"]
args[5]: [(")(")]
args[6]: [""] is empty string
args[7]: [::]
args[8]: [""-"  "]
args[9]: ["( )"">\>"]
args[10]: [""] is empty string

command line:
.test.bat :: ""  ">"""bl" " "< "">"  (")(") "" :: ""-"  " "( )"">\>" ""
user6801759
fonte
Está errado, uma string vazia não é uma string com duas aspas duplas e ""uma string vazia é uma string vazia `` e no lote uma variável vazia é uma variável indefinida. Você pode definir que, para seus argumentos, as aspas externas serão removidas, mas a sequência ainda estará vazia com um comprimento de 0
jeb
Nesse contexto (argumentos win cmd), você não pode denotar uma string vazia de nenhuma outra maneira, nem mesmo com aspas simples duplas, que são igualmente vazias no shell unix.
user6801759
Sim, também ""são usados ​​para cadeias vazias, mas é muito fácil %~1de remover as aspas externas. não há necessidade de usar uma solução tão complexa
jeb
Você está certo, eu poderia afirmar indevidamente, não apenas para uma string vazia, mas uma maneira segura de enumerar argumentos. Outras maneiras como "%~1"==""falharão com argumentos como "Long File Name".txtqual é um argumento válido. edit : E definitivamente não é complexo como você disse. Apenas 2 etapas acima para o teste, o restante é um exemplo detalhado.
user6801759
"Se ARG não definido" parece funcionar perfeitamente (minha variável não vem da linha de comando)
GrayFace
4

Você pode usar

if defined (variable) echo That's defined!
if not defined (variable) echo Nope. Undefined.
noname
fonte
1
Bem-vindo ao SO! Sua resposta parece ser uma boa resposta para a pergunta. Depois de ter [reputação] suficiente ( stackoverflow.com/help/whats-reputation ), você poderá comentar em qualquer postagem. Verifique também o que posso fazer .
precisa saber é o seguinte
3

Script 1:

Entrada ("Remover Quotes.cmd" "Este é um teste")

@ECHO OFF

REM Set "string" variable to "first" command line parameter
SET STRING=%1

REM Remove Quotes [Only Remove Quotes if NOT Null]
IF DEFINED STRING SET STRING=%STRING:"=%

REM IF %1 [or String] is NULL GOTO MyLabel
IF NOT DEFINED STRING GOTO MyLabel


REM   OR   IF "." equals "." GOTO MyLabel
IF "%STRING%." == "." GOTO MyLabel

REM GOTO End of File
GOTO :EOF

:MyLabel
ECHO Welcome!

PAUSE

Saída (não há,% 1 NÃO estava em branco, vazio ou NULL):


Execute ("Remove Quotes.cmd") sem nenhum parâmetro com o script acima 1

Saída (% 1 está em branco, vazio ou NULL):

Welcome!

Press any key to continue . . .

Nota: Se você definir uma variável dentro de um IF ( ) ELSE ( ) instrução, ela não estará disponível para DEFINED até que saia da instrução "SE" (a menos que a opção "Expansão atrasada da variável" esteja ativada; uma vez ativado, use um ponto de exclamação "!" No lugar do símbolo de porcentagem "%"}.

Por exemplo:

Script 2:

Entrada ("Remover Quotes.cmd" "Este é um teste")

@ECHO OFF

SETLOCAL EnableDelayedExpansion

SET STRING=%0
IF 1==1 (
  SET STRING=%1
  ECHO String in IF Statement='%STRING%'
  ECHO String in IF Statement [delayed expansion]='!STRING!'
) 

ECHO String out of IF Statement='%STRING%'

REM Remove Quotes [Only Remove Quotes if NOT Null]
IF DEFINED STRING SET STRING=%STRING:"=%

ECHO String without Quotes=%STRING% 

REM IF %1 is NULL GOTO MyLabel
IF NOT DEFINED STRING GOTO MyLabel

REM GOTO End of File
GOTO :EOF

:MyLabel
ECHO Welcome!

ENDLOCAL
PAUSE

Resultado:

C:\Users\Test>"C:\Users\Test\Documents\Batch Files\Remove Quotes.cmd" "This is a Test"  
String in IF Statement='"C:\Users\Test\Documents\Batch Files\Remove Quotes.cmd"'  
String in IF Statement [delayed expansion]='"This is a Test"'  
String out of IF Statement='"This is a Test"'  
String without Quotes=This is a Test  

C:\Users\Test>  

Nota: Ele também removerá aspas de dentro da string.

Por exemplo (usando o script 1 ou 2): C: \ Usuários \ Teste \ Documentos \ Arquivos em lote> "Remover Quotes.cmd" "Este é" um "Teste"

Saída (Script 2):

String in IF Statement='"C:\Users\Test\Documents\Batch Files\Remove Quotes.cmd"'  
String in IF Statement [delayed expansion]='"This is "a" Test"'  
String out of IF Statement='"This is "a" Test"'  
String without Quotes=This is a Test  

Execute ("Remove Quotes.cmd") sem nenhum parâmetro no Script 2:

Resultado:

Welcome!

Press any key to continue . . .
Sr. Rick
fonte
3

Para resumir:

set str=%~1
if not defined str ( echo Empty string )

Esse código exibirá "String vazia" se% 1 estiver "" ou "ou vazio. Adicionado à resposta aceita que está incorreta no momento.

GrayFace
fonte
0

Eu tive muitos problemas com muitas respostas na rede. A maioria funciona para a maioria das coisas, mas sempre há uma caixa de canto que quebra cada uma.
Talvez não funcione se tiver aspas, talvez quebre se não tiver aspas, erro de sintaxe se o var tiver um espaço, alguns funcionarão apenas em parâmetros (ao contrário de variáveis ​​de ambiente), outras técnicas permitem um vazio conjunto de aspas para passar como 'definido', e algumas mais complicadas não permitirão que você encadeie elsedepois.

Aqui está uma solução que eu estou feliz com, por favor me avise se você encontrar uma caixa de esquina para a qual não funcionará.

:ifSet
if "%~1"=="" (Exit /B 1) else (Exit /B 0)

Ter essa sub-rotina no seu script ou no seu .batdeve funcionar.
Então, se você quiser escrever (em pseudo):

if (var)
then something
else somethingElse

Você pode escrever:

(Call :ifSet %var% && (
    Echo something
)) || (
    Echo something else
)

Funcionou em todos os meus testes:

(Call :ifSet && ECHO y) || ECHO n
(Call :ifSet a && ECHO y) || ECHO n
(Call :ifSet "" && ECHO y) || ECHO n
(Call :ifSet "a" && ECHO y) || ECHO n
(Call :ifSet "a a" && ECHO y) || ECHO n

Echo'd n, y, n, y,y


Mais exemplos:

  • Quer apenas verificar if?Call :ifSet %var% && Echo set
  • Apenas se não (apenas o resto)? Call :ifSet %var% || Echo set
  • Verificando um argumento passado; funciona bem.Call :ifSet %1 && Echo set
  • Não queria entupir seus scripts / código enganoso, então você o colocou no seu próprio ifSet.bat? Sem problemas:((Call ifSet.bat %var%) && Echo set) || (Echo not set)
Hashbrown
fonte
Encontrei um caso; Se você tentar a sintaxe if com else, e o último comando no bloco then falhar, o else executará (Call ifSet.bat YES && (Echo true & Echo x | findstr y)) || Echotrue false
:,
Na verdade, se isso é um problema, e você pode lidar com uma linha extra, você pode atenuar isso chamando ifSetpor conta própria, em seguida, usandoIf %errorlevel% EQU 0 (x) Else (y)
Hashbrown
Em quais casos a resposta aceita (use colchetes) não funciona? E você pode explicar o que o Sair / B faz
blak3r
por alguma razão, quando testei, ele quebrou ao usar setvariáveis ​​(como [%bob%], apenas trabalhando para argumentos como [%2]), mas agora verificando novamente como funciona. Eu acho que a minha única queixa com []agora é ele permite aspas vazias passar, enquanto isso não vai (por isso é até para o seu caso de uso, realmente)
Hashbrown
O /Bsinalizador impede a saída do lote inteiro, saindo apenas da sub-rotina ou calldo lote. A menos que você esteja perguntando o que todo o comando está fazendo; ele está retornando um código de erro de deixar o script chamado saber o resultado da sub-rotina ( 0passagem, 1falha), utilizáveis lógica booleana via na resposta, ou via %errorlevel%nos comentários acima
Hashbrown
0

Usando! em vez de "para verificação de string vazia

@echo off
SET a=
SET b=Hello
IF !%a%! == !! echo String a is empty
IF !%b%! == !! echo String b is empty
Lin W
fonte
1
A dica é muito ruim. As aspas podem lidar com espaços e caracteres especiais pelo menos, mas os pontos de exclamação não. E pontos de exclamação ficar realmente desagradável aqui quando a expansão atrasada está habilitado
jeb
0

Eu entrei com menos de um mês (apesar de ter sido solicitado há 8 anos) ... Espero que ele já tenha passado dos arquivos em lotes. ;-) Eu costumava fazer isso o tempo todo. Não tenho certeza de qual é o objetivo final. Se ele é preguiçoso como eu, meu go.bat funciona para coisas assim. (Veja abaixo) Mas, 1, o comando no OP pode ser inválido se você estiver usando diretamente a entrada como um comando. ou seja,

"C:/Users/Me"

é um comando inválido (ou costumava ser se você estivesse em uma unidade diferente). Você precisa dividi-lo em duas partes.

C:
cd /Users/Me

E, 2, o que significa 'definido' ou 'indefinido'? GIGO. Eu uso o padrão para detectar erros. Se a entrada não for capturada, ela será removida para ajudar (ou um comando padrão). Portanto, nenhuma entrada não é um erro. Você pode tentar cd para a entrada e capturar o erro, se houver um. (Ok, usar go "downloads (apenas um paren) é capturado pelo DOS. (Harsh!))

cd "%1"
if %errorlevel% neq 0 goto :error

E, 3, aspas são necessárias apenas no caminho, não no comando. ou seja,

"cd C:\Users" 

era ruim (ou costumava ser antigamente), a menos que você estivesse em uma unidade diferente.

cd "\Users" 

é funcional.

cd "\Users\Dr Whos infinite storage space"

funciona se você tiver espaços no seu caminho.

@REM go.bat
@REM The @ sigh prevents echo on the current command
@REM The echo on/off turns on/off the echo command. Turn on for debugging
@REM You can't see this.
@echo off
if "help" == "%1" goto :help

if "c" == "%1" C:
if "c" == "%1" goto :done

if "d" == "%1" D:
if "d" == "%1" goto :done

if "home"=="%1" %homedrive%
if "home"=="%1" cd %homepath%
if "home"=="%1" if %errorlevel% neq 0 goto :error
if "home"=="%1" goto :done

if "docs" == "%1" goto :docs

@REM goto :help
echo Default command
cd %1
if %errorlevel% neq 0 goto :error
goto :done

:help
echo "Type go and a code for a location/directory
echo For example
echo go D
echo will change disks (D:)
echo go home
echo will change directories to the users home directory (%homepath%)
echo go pictures
echo will change directories to %homepath%\pictures
echo Notes
echo @ sigh prevents echo on the current command
echo The echo on/off turns on/off the echo command. Turn on for debugging
echo Paths (only) with folder names with spaces need to be inclosed in         quotes (not the ommand)
goto :done

:docs
echo executing "%homedrive%%homepath%\Documents"
%homedrive%
cd "%homepath%\Documents"\test error\
if %errorlevel% neq 0 goto :error
goto :done

:error
echo Error: Input (%1 %2 %3 %4 %5 %6 %7 %8 %9) or command is invalid
echo go help for help
goto :done

:done
Xorange
fonte
existe uma razão para cdo /dswitch: cd /d "C:\Users\Me" (and the proper path delimeter for Windows is `, not /).
247 Stephan Stephan