¿Cuál es la forma correcta de probar si un parámetro está vacío en un archivo por lotes?

214

Necesito probar si una variable está configurada o no. He intentado varias técnicas, pero parecen fallar siempre que %1está rodeado de comillas, como el caso cuando %1es así "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.

Según este sitio , estos son los IFtipos de sintaxis compatibles . Entonces, no veo una manera de hacerlo.

IF [NOT] ERRORLEVEL number command
IF [NOT] string1==string2 command
IF [NOT] EXIST filename command
blak3r
fuente
1
En mis sistemas (Windows 2003 y Windows 7), if "%1" == "" GOTO MyLabelno mata fatalmente la ejecución del script siempre que %1tenga un número par de comillas dobles. Veo que un número impar de comillas dobles %1mata la ejecución del script con este error: The syntax of the command is incorrect.la siguiente solución que utiliza corchetes para resolver el problema se ha marcado como la respuesta correcta, pero no parece estar mejorando. . Esa solución también falla con el mismo error cuando %1tiene un número impar de comillas dobles.
Susam Pal
@SusamPal Interesante. Pruebe la versión entre paréntesis debajo y vea si eso funciona. Ese probé más. Acabo de actualizar la respuesta aceptada hace un par de días.
blak3r
La respuesta de Dan Story parece funcionar bien. Usé la versión entre corchetes.
Susam Pal
un buen ejemplo de "capturar todo": stackoverflow.com/questions/830565/… que cubre tanto el archivo / directorio como la combinación genérica de cadena / número en el argumento.
Tan frustrante: ¡ IF DEFINEDsolo trabajar en variables de entorno en lugar de variables de script es una pérdida de potencial!
kayleeFrye_onDeck

Respuestas:

292

Use corchetes en lugar de comillas:

IF [%1] == [] GOTO MyLabel

Los paréntesis son inseguros : solo use corchetes.

Dan Story
fuente
11
paréntesis no son seguros! Contenido como &en %1romperá la línea
jeb
14
Usar otros caracteres como if #%1#==##o if [%1]==[]es bueno siempre que no haya espacios en el argumento, de lo contrario obtendrá un …was unexpected at this timeerror.
Synetech
12
Y usar "%~1"es realmente un mejor enfoque, como mencioné en mi respuesta.
jamesdlin
8
Chicos, @jamesdlin es correcto. Este enfoque no funcionará si el texto variable tiene espacios o es ==. Use su respuesta en su lugar.
James Ko
44
No dejas en claro eso it DOES NOT work with spaces. Utilice la respuesta @jamesdlin en su lugar.
JB. Con monica.
153

Puedes usar:

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

para quitar el conjunto exterior de comillas. En general, este es un método más confiable que el uso de corchetes porque funcionará incluso si la variable tiene espacios.

jamesdlin
fuente
9
De hecho, esta es una buena manera de hacerlo. Al usar ~, elimina las comillas externas si están presentes, pero luego las agrega de nuevo manualmente (asegurando solo un conjunto). Por otra parte, usted tiene que usar comillas si el argumento contiene espacios (I aprendido de la manera difícil cuando mi versión anterior de utilizar #o corchetes en lugar de cotizaciones causó argumentos con espacios para lanzar un …was unexpected at this timeerror.)
Synetech
44
¿esto también es aplicable para variables generales como% MYVAR% en lugar de los argumentos como% 1?
simpleuser
44
@simpleuser Vaya, tienes razón, no funciona con variables de entorno generales.
jamesdlin
1
tenga cuidado con las cadenas que usan el doble '"' como secuencia de escape: el guión se bloqueará al intentar la comparación. Por ejemplo, el ""this string will crash the comparison""doble '"' es la secuencia de escape adecuada y la sustitución en doble '"' fallará si el cadena está vacía.
Tydaeus
1
@Amalgovinus Probablemente podría pasar la variable como argumento a una subrutina y dejar que la subrutina use "%~1".
jamesdlin
38

Una de las mejores soluciones semi es copiar %1en una variable y luego usar expansión demorada, como delayedExp. siempre es seguro contra cualquier contenido.

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

La ventaja de esto es que tratar con param1 es absolutamente seguro.

Y la configuración de param1 funcionará en muchos casos, como

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

Pero todavía falla con contenidos extraños como

test.bat ^&"&

Para poder obtener una respuesta 100% correcta para la existencia

Detecta si %1está vacío, pero para algunos contenidos no puede recuperar el contenido.
Esto también puede ser útil para distinguir entre un vacío %1y uno con "".
Utiliza la capacidad del CALLcomando para fallar sin abortar el archivo por 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!'

Si desea ser 100% a prueba de balas para obtener el contenido, puede leer ¿Cómo recibir incluso los parámetros de línea de comando más extraños?

jeb
fuente
18

De IF / ?:

Si las extensiones de comando están habilitadas, IF cambia de la siguiente manera:

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

......

El condicional DEFINIDO funciona igual que EXISTE, excepto que toma un nombre de variable de entorno y devuelve verdadero si la variable de entorno está definida.

Andy Morris
fuente
1
Sí, en realidad probé este enfoque y, por lo que pude ver, esto solo funciona con las variables de ENTORNO. Por lo tanto, no funcionó con% 1 o una variable definida dentro del archivo por lotes.
blak3r
2
Eso solo es cierto para %1y los gustos. Las "variables definidas dentro del archivo por lotes" en realidad son variables de entorno mientras se ejecuta el archivo por lotes, y puede usarlas IF DEFINED.
zb226
8

Desafortunadamente no tengo suficiente reputación para comentar o votar sobre las respuestas actuales a las que he tenido que escribir las mías.

Originalmente, la pregunta del OP decía "variable" en lugar de "parámetro", lo cual se volvió muy confuso, especialmente porque este era el enlace número uno en google para buscar cómo probar las variables en blanco. Desde mi respuesta original, Stephan ha editado la pregunta original para usar la terminología correcta, pero en lugar de eliminar mi respuesta, decidí dejarla para ayudar a aclarar cualquier confusión, especialmente en caso de que Google todavía envíe personas aquí para variables también:

¡% 1 NO ES UNA VARABLE! ES UN PARÁMETRO DE LÍNEA DE MANDO.

Muy importante distinción. Un solo signo de porcentaje con un número después de que se refiere a un parámetro de línea de comando, no a una variable.

Las variables se establecen con el comando set y se recuperan con signos de 2 por ciento, uno antes y otro después. Por ejemplo% myvar%

Para probar una variable vacía, use la sintaxis "si no está definida" (los comandos explícitamente para las variables no requieren ningún signo de porcentaje), por ejemplo:

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.

(Si desea probar los parámetros de la línea de comandos, le recomiendo consultar la respuesta de jamesdlin).

AutoMattTick
fuente
1
tienes razón. Pero la forma correcta habría sido simplemente corregir el título de if variable is emptya if parameter is empty. Solo lo hice. (a riesgo de invalidar algunas de las respuestas, que verificaron variables en lugar de parámetros y, por lo tanto, no son válidas de todos modos, ya que no respondieron la pregunta)
Stephan
1
OK, gracias Stephan, no me di cuenta de que podías hacer eso. He editado mi respuesta para reflejar la actualización.
AutoMattTick
6

Utilice el "comando variable IF IFIN DEFINED" para probar la variable en el archivo por lotes.

Pero si desea probar los parámetros por lotes, intente con los códigos a continuación para evitar entradas complicadas (como "1 2" o ab ^> cd)

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

Pruebo con el siguiente código y está bien.

@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
fuente
Su solución falla cuando la variable contiene una cita. Por cierto, existe la sintaxis IF DEFINED varpor una razón
jeb
4

Usualmente uso esto:

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

Si% 1 está vacío, el IF comparará "." a "." que se evaluará como verdadero.

aforia
fuente
44
Esto es incorrecto. Esto es lo mismo que: SI "% 1" == "" la razón de esta publicación fue si% 1 tiene citas, esto falla.
blak3r
¿Estás tratando de probar si% 1 está vacío o si tiene comillas? La pregunta no está clara. Mi respuesta funciona si no se especifica nada en la línea de comando para% 1.
aphoria
> ¿Estás tratando de probar si% 1 está vacío o si tiene comillas? @aphoria, tiene que ser capaz de manejar ambos.
Synetech
Usé esto para resolver si% 1% se había configurado, funcionó muy bien para mí. Gracias, buena respuesta!
Charlie A
4

Creé este pequeño script por lotes basado en las respuestas aquí, ya que hay muchas válidas. Siéntase libre de agregar a esto siempre que siga el mismo 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
fuente
4

La cadena vacía es un par de double-quotes/ "", solo podemos probar la longitud:

set ARG=%1
if not defined ARG goto nomore

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

luego prueba que son 2 caracteres contra double-quotes:

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

Aquí hay un script por lotes con el que puedes jugar. Creo que atrapa correctamente la cadena vacía.

Este es solo un ejemplo, solo necesita personalizar 2 (¿o 3?) Pasos anteriores de acuerdo con su 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

El resultado de esta prueba 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" " "< "">"  (")(") "" :: ""-"  " "( )"">\>" ""
usuario6801759
fuente
Está mal, una cadena vacía no es una cadena con dos comillas dobles, ""una cadena vacía es una cadena vacía `` y en lote una variable vacía es una variable indefinida. Puede definir que para sus argumentos se eliminarán las comillas externas, pero luego la cadena aún está vacía con una longitud de 0
jeb
En este contexto (argumentos de ganar cmd), no puede denotar una cadena vacía de ninguna otra manera, ni siquiera con comillas simples dobles que igualmente están vacías bajo unix shell.
user6801759
Sí, también ""se usan para cadenas vacías, pero es bastante fácil de usar %~1para eliminar las comillas externas. no hay necesidad de usar una solución tan compleja
jeb
Tiene razón, podría decir incorrectamente, no solo para una cadena vacía, sino una forma segura de enumerar argumentos. Otras formas tales como "%~1"==""fallarán con argumentos como "Long File Name".txtcuál es un argumento válido. editar : Y definitivamente no es complejo como dijiste. Solo 2 pasos anteriores para la prueba, el resto son ejemplos detallados.
user6801759
"si no se define ARG" parece funcionar perfectamente (mi variable no proviene de la línea de comando)
GrayFace
3

Guión 1:

Entrada ("Quitar Quotes.cmd" "Esta es una prueba")

@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

Salida (No hay ninguno,% 1 NO estaba en blanco, vacío o NULL):


Ejecutar ("Quitar Quotes.cmd") sin ningún parámetro con el script anterior 1

Salida (% 1 está en blanco, vacío o NULL):

Welcome!

Press any key to continue . . .

Nota: Si establece una variable dentro de una IF ( ) ELSE ( )instrucción, no estará disponible para DEFINIR hasta después de que salga de la instrucción "SI" (a menos que esté habilitada la "Expansión de variable retrasada"; una vez habilitada, use un signo de exclamación "!" En lugar de símbolo de porcentaje "%"}.

Por ejemplo:

Guión 2:

Entrada ("Quitar Quotes.cmd" "Esta es una prueba")

@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

Salida:

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: También eliminará las comillas del interior de la cadena.

Por ejemplo (usando el script 1 o 2): C: \ Users \ Test \ Documents \ Batch Files> "Remove Quotes.cmd" "Esto es" una "prueba"

Salida (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  

Ejecute ("Eliminar Quotes.cmd") sin ningún parámetro en el Script 2:

Salida:

Welcome!

Press any key to continue . . .
Señor rick
fuente
3

Para resumir las cosas:

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

Este código generará "Cadena vacía" si% 1 es "" o "o está vacío. Se agregó a la respuesta aceptada que actualmente es incorrecta.

GrayFace
fuente
0

He tenido muchos problemas con muchas respuestas en la red. La mayoría funciona para la mayoría de las cosas, pero siempre hay un caso de esquina que rompe cada uno.
Tal vez no funcione si tiene comillas, tal vez se rompa si no tiene comillas, error de sintaxis si la var tiene un espacio, algunas solo funcionarán en parámetros (a diferencia de las variables de entorno), otras técnicas permiten un vacío un conjunto de citas para pasar como 'definido', y algunas más complicadas no le permitirán encadenar unelse después.

Aquí hay una solución con la que estoy satisfecho, avíseme si encuentra un caso de esquina para el que no funcionará.

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

Tener esa subrutina, ya sea en su script, o en su propio .bat, debería funcionar.
Entonces, si quisieras escribir (en pseudo):

if (var)
then something
else somethingElse

Puedes escribir:

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

Funcionó para todas mis pruebas:

(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


Más ejemplos:

  • ¿Quieres comprobarlo if?Call :ifSet %var% && Echo set
  • ¿Solo si no (solo el otro)? Call :ifSet %var% || Echo set
  • Comprobando un argumento pasado; funciona bien.Call :ifSet %1 && Echo set
  • ¿No quería obstruir sus scripts / código de duplicación, por lo que lo puso en su cuenta ifSet.bat? No hay problema:((Call ifSet.bat %var%) && Echo set) || (Echo not set)
Hashbrown
fuente
Encontrado un caso; Si prueba la sintaxis if con else, y el último comando en el bloque then falla, el else ejecutará:, (Call ifSet.bat YES && (Echo true & Echo x | findstr y)) || Echoechos true y false
Hashbrown
En realidad, si esto es un problema, y ​​puede lidiar con una línea extra, puede mitigar esto llamando ifSetpor su cuenta, luego usandoIf %errorlevel% EQU 0 (x) Else (y)
Hashbrown
¿En qué casos no funciona la respuesta aceptada (usar corchetes)? Y se puede explicar lo que la salida / B hace
blak3r
por alguna razón cuando lo probé se rompió al usar setvariables (como [%bob%], solo funcionaba para argumentos como [%2]) pero ahora volver a verificarlo funciona. Supongo que mi única queja []ahora es que deja pasar las comillas vacías, mientras que esto no lo hará (por lo que depende de su caso de uso, en realidad)
Hashbrown
La /Bbandera detiene la salida de todo el lote, solo sale de la subrutina o call'd batch. A menos que esté preguntando qué está haciendo todo el comando ; devuelve un código de error para que el script de llamada conozca el resultado de la subrutina ( 0pasar, 1fallar), que se puede usar a través de la lógica booleana en la respuesta o %errorlevel%en los comentarios anteriores
Hashbrown
0

Utilizando ! en lugar de "para comprobar la cadena vacía

@echo off
SET a=
SET b=Hello
IF !%a%! == !! echo String a is empty
IF !%b%! == !! echo String b is empty
Lin W
fuente
1
La propina es realmente mala. Las comillas pueden manejar espacios y caracteres especiales al menos, pero los signos de exclamación no. Y los signos de exclamación se vuelven realmente desagradables aquí cuando se habilita la expansión retrasada
jeb
0

Llegué en menos de un mes (a pesar de que se le preguntó hace 8 años) ... Espero que ahora ya haya superado los archivos por lotes. ;-) Solía ​​hacer esto todo el tiempo. Sin embargo, no estoy seguro de cuál es el objetivo final. Si es flojo como yo, mi go.bat funciona para cosas como esa. (Ver más abajo) Pero, 1, el comando en el OP podría ser inválido si está utilizando directamente la entrada como un comando. es decir,

"C:/Users/Me"

es un comando no válido (o solía serlo si estuviera en una unidad diferente). Necesitas dividirlo en dos partes.

C:
cd /Users/Me

Y, 2, ¿qué significa 'definido' o 'indefinido'? GIGO Yo uso el valor predeterminado para detectar errores. Si la entrada no queda atrapada, cae para ayudar (o un comando predeterminado). Por lo tanto, ninguna entrada no es un error. Puede intentar cd a la entrada y detectar el error si hay uno. (Ok, el uso de descargas "ir" (solo un par) es capturado por DOS. (¡Duro!))

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

Y, 3, las comillas se necesitan solo alrededor de la ruta, no el comando. es decir,

"cd C:\Users" 

estaba mal (o solía hacerlo en los viejos tiempos) a menos que estuvieras en un disco diferente.

cd "\Users" 

Es funcional.

cd "\Users\Dr Whos infinite storage space"

funciona si tienes espacios en tu camino.

@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
fuente
hay una razón para cdel /dcambio de cd /d "C:\Users\Me" (and the proper path delimeter for Windows is ' : ', no /).
Stephan