Subrutinas y valores de retorno
Cuando un archivo por lotes empieza a crecer, escribir el mismo procesamiento una y otra vez resulta engorroso y además es un foco de errores. En esos casos resultan útiles las subrutinas (pseudofunciones).
Los archivos por lotes no tienen una sintaxis de «definición de funciones» como otros lenguajes de programación, pero con CALL :nombre_de_etiqueta se puede lograr una estructura muy parecida a una función. En este artículo se explica paso a paso, desde cómo crear una subrutina básica hasta cómo pasar argumentos y devolver números y cadenas.
Estructura básica de una subrutina
Una subrutina se compone de los siguientes elementos.
| Elemento | Descripción |
|---|---|
CALL :nombre_de_etiqueta | Llama a la subrutina |
:nombre_de_etiqueta | Etiqueta que indica el punto de inicio de la subrutina |
EXIT /B código_de_salida | Regresa de la subrutina al código que la llamó |
GOTO :EOF | Equivale a EXIT /B (sin especificar código de salida). Finaliza la subrutina |
Para separar el procesamiento principal de las subrutinas, el punto importante es colocar GOTO :EOF al final del código principal. Sin él, la ejecución continúa y pasa directamente al código de la subrutina.
@echo off
setlocal
CALL :GREET "García"
CALL :GREET "López"
GOTO :EOF
:GREET
echo ¡Hola, %~1!
EXIT /B 0
:EOF es una etiqueta especial que representa el final del archivo. GOTO :EOF finaliza la subrutina igual que EXIT /B, pero no permite especificar un código de salida y se conserva tal cual el ERRORLEVEL anterior. Si desea especificar un código de salida, use EXIT /B N. Al final del código principal se puede usar cualquiera de los dos.
Las subrutinas funcionan estén donde estén en el archivo, pero lo habitual es escribirlas todas juntas después del procesamiento principal (tras GOTO :EOF). Esto mejora la legibilidad del código.
Cómo pasar argumentos a una subrutina
Se pueden pasar argumentos a continuación del nombre de la etiqueta, como en CALL :nombre_de_etiqueta argumento1 argumento2 .... Dentro de la subrutina se accede a ellos con %1, %2…
| Notación | Significado |
|---|---|
%1 | Primer argumento (incluye las comillas) |
%~1 | Primer argumento (sin las comillas del principio y del final) |
%2 | Segundo argumento |
@echo off
setlocal
CALL :SHOW_INFO "López" "Madrid" 30
GOTO :EOF
:SHOW_INFO
echo Nombre: %~1
echo Ciudad: %~2
echo Edad: %3
EXIT /B 0

中小企業経営者のためのRPA入門 RPA導入を成功させる方法

60分でわかる! AIエージェント 超 入門

コマンドラインの黒い画面が怖いんです。

知識・才能ゼロでもらく~に月10万円稼ぐ! よくわかるAI副業超入門
Si se pasa un argumento encerrado entre comillas, como "López", con %1 se obtiene "López" (con comillas) y con %~1 se obtiene López (sin comillas). Al trabajar con cadenas es más seguro usar %~1.
Valores de retorno numéricos (ERRORLEVEL)
Para devolver un número desde una subrutina, se especifica un código de salida (N) con EXIT /B N. En el código que la llama, el valor se recibe con %ERRORLEVEL% o con IF ERRORLEVEL N.
Por convención, se devuelve 0 en caso de éxito y un valor de 1 o superior en caso de error o fallo.
@echo off
setlocal
CALL :IS_EVEN 4
IF %ERRORLEVEL% EQU 0 (
echo 4 es par.
) ELSE (
echo 4 es impar.
)
CALL :IS_EVEN 7
IF %ERRORLEVEL% EQU 0 (
echo 7 es par.
) ELSE (
echo 7 es impar.
)
GOTO :EOF
:IS_EVEN
set /a CHECK=%1 %% 2
EXIT /B %CHECK%
El %% de set /a CHECK=%1 %% 2 es el carácter de escape que representa el propio símbolo % dentro de un archivo por lotes. %1 %% 2 calcula «el resto de dividir %1 entre 2». Si el resto es 0, es par; si es 1, es impar.
Si no se especifica un número en EXIT /B, se devuelve tal cual el ERRORLEVEL de ese momento. Para evitar que se devuelva un valor no deseado, se recomienda especificarlo de forma explícita, como en EXIT /B 0.
Valores de retorno de cadenas (truco de endlocal & set)
A diferencia de los números, devolver una cadena es algo más complicado. Si se usa setlocal dentro de la subrutina, el ámbito de las variables se cierra, de modo que al llamar a endlocal todas las variables de la subrutina desaparecen.
La solución a este problema es la técnica de combinar endlocal & set nombre_de_variable=valor en una sola línea.
Por qué funciona al combinarlo en una sola línea
El Símbolo del sistema analiza la línea completa antes de ejecutarla. Al escribir endlocal & set RESULT=valor, el valor del lado derecho de set se analiza en el estado anterior a la ejecución de endlocal. Es decir, después de que el valor de la variable de la subrutina se expande en el lado derecho de set, se ejecuta endlocal y la variable queda definida en el ámbito del código que llamó.
@echo off
setlocal
CALL :GET_GREETING "García" RESULT
echo %RESULT%
GOTO :EOF
:GET_GREETING
setlocal
set NAME=%~1
set GREETING=¡Hola, %NAME%!
endlocal & set "%~2=%GREETING%"
EXIT /B 0
En este patrón, se pasa en el segundo argumento (%~2) el nombre de la variable donde se almacenará el valor de retorno. Como el código que llama puede elegir libremente el nombre de la variable, se gana versatilidad.
Patrón con un nombre de variable fijo
En lugar de pasar el nombre de la variable como argumento, también se usa mucho, por su sencillez, un nombre de variable fijo (por ejemplo, RESULT).
@echo off
setlocal
CALL :FORMAT_DATE
echo Fecha con formato: %RESULT%
GOTO :EOF
:FORMAT_DATE
setlocal
set YEAR=%DATE:~0,4%
set MONTH=%DATE:~5,2%
set DAY=%DATE:~8,2%
set LOCAL_RESULT=%YEAR%%MONTH%%DAY%
endlocal & set "RESULT=%LOCAL_RESULT%"
EXIT /B 0
El formato de %DATE% cambia según la configuración regional. El ejemplo anterior asume el formato yyyy/MM/dd (por ejemplo, 2026/04/24), por lo que en otros entornos la fecha no se extraerá correctamente. Para saber cómo obtener la fecha de forma independiente del entorno, consulte el artículo relacionado sobre cómo obtener la fecha y la hora.
endlocal & set nombre_de_variable=valor debe escribirse siempre en una sola línea. Si se divide en dos líneas, las variables de la subrutina desaparecen en el momento de endlocal y el valor de set queda vacío.
Cuando otro comando sigue en la misma línea (set nombre_de_variable=valor & ...), el espacio anterior al & pasa a formar parte del valor. Además, si el valor contiene caracteres especiales como & o ), puede producirse un error de sintaxis. Encerrar todo entre comillas, como en set "nombre_de_variable=valor", permite evitarlo. Como el manejo de valores que contienen % o ! es complejo, con esta técnica lo más seguro es limitarse a devolver cadenas sencillas de caracteres alfanuméricos.
Al colocar varios set en una misma línea, como en endlocal & set "%~2=%VAL1%" & set "%~3=%VAL2%", se pueden devolver varios valores a la vez. Resulta útil combinarlo con el patrón de recibir los nombres de variable por argumento.
Ejemplo práctico: subrutina para comprobar la existencia de archivos
Como ejemplo práctico que combina lo visto hasta ahora, se muestra una subrutina que comprueba la existencia de varios archivos y devuelve el número de archivos que faltan.
@echo off
setlocal
CALL :CHECK_FILES
IF %ERRORLEVEL% NEQ 0 (
echo Faltan %ERRORLEVEL% archivos necesarios. Se cancela el procesamiento.
GOTO :EOF
)
echo Se confirmó que todos los archivos existen. Se inicia el procesamiento.
GOTO :EOF
:CHECK_FILES
setlocal
set MISSING=0
for %%f in (config.ini data.csv template.xlsx) do (
IF NOT EXIST "%%f" (
echo Advertencia: no se encuentra %%f.
set /a MISSING+=1
)
)
endlocal & exit /b %MISSING%
En la última línea, %MISSING% se expande antes de que se ejecute endlocal, por lo que el valor de la variable de la subrutina se puede devolver como código de salida al código que la llamó.
Ejemplo de ejecución cuando no existe config.ini:
Cuando existen todos los archivos:
Resumen
| Operación | Notación |
|---|---|
| Llamar a una subrutina | CALL :nombre_de_etiqueta argumento1 argumento2 ... |
| Finalizar una subrutina | EXIT /B código_de_salida |
| Hacer referencia a los argumentos | %1, %~1 (sin comillas) |
| Devolver un número | EXIT /B N → en el código que llama, consultar %ERRORLEVEL% |
| Devolver una cadena | Ejecutar endlocal & set nombre_de_variable=valor en una sola línea |
| Evitar que la ejecución continúe en la subrutina | Colocar GOTO :EOF al final del código principal |