¿Cómo comparar 2 archivos XML en Batch?

Necesitaría crear un archivo .BAT que compare 2 archivos XML, el original y uno de respaldo que están en 2 ubicaciones diferentes de mi PC.

Si el original es de versión menor al archivo de respaldo, que se copie este último más actualizado.

Para el caso que el original sea de versión mayor al de respaldo que no se copie nada.

El archivo XML a comparar:

<config version="1">
    <settings>
        <property name="Launch" value="Utherverse.exe"/>
        <property name="ProductName" value="Utherverseᆴ VWW Patcher"/>
        <property name="IsWine" value="False"/>
        <property name="DefaultBrand" value="{ca11dba6-502c-4fb8-93c2-73d47b810ea5}"/>
        <property name="ProductGUID" value="{5198947F-F7FF-49D0-86A5-F8076A94EDC9}"/>
        <property name="NeedRepair" value="false"/>
        <property name="FirstRun" value="false"/>
        <property name="Version" value="1.9.4792"/>
    </settings>
</config>

O sea que al comparar los 2 archivos XML se comparará por la Versión, en este ejemplo: <property name="Version" value="1.9.4792"/>

1 Respuesta

Respuesta
1

No sé por qué no me ha entrado esta consulta por la vía habitual (correo electrónico). Esa es la razón por lo que he tardado más de lo normal en decir algo.

He preparado un BAT de base que podría servir para empezar a hablar más en concreto. Es este:

@echo off
set carpOrig=d:\Carpeta original
set carpResp=d:\Carpeta respaldo
set nomxml=NombreArchivo.xml
set versOrig=
set versResp=
for /f "tokens=3" %%a in ('find "Version" "%carpOrig%\%nomxml%"') do set versOrig=%%a
set versOrig=%versOrig:~7,8%
set versOrig=%versOrig:.=%
for /f "tokens=3" %%a in ('find "Version" "%carpResp%\%nomxml%"') do set versResp=%%a
set versResp=%versResp:~7,8%
set versResp=%versResp:.=%
echo version original es %versOrig% y version respaldo es %versResp%
if %versOrig% LSS %versResp% echo copy /y "%carpResp%\%nomxml%" "%carpOrig%\%nomxml%"&goto :eof
echo no se hace nada

Uso variables para la carpeta original, la carpeta de respaldo y el nombre del archivo xml que tendrías que ajustar a tu caso antes de ejecutarlo. Parto de que la línea de Version tiene el formato que indicas en el ejemplo. Si no fuera así o no fuera tan rígido habría que ver como abordarlo. Por último en lugar de un COPY real solo se hace ECHO del COPY para que puedas ver el aspecto. Para que el COPY se haga de verdad habría que borrar el ECHO que lo precede.

Ya me dirás.

Hola gggirald, he probado el código y funciona perfectamente al comparar las versiones de los archivos.

Pero existe un escenario que no te comenté previamente y ahora me doy cuenta.

Cuando el archivo original patch.xml se corrompe (ejemplo: por salir del programa de una forma inusual cerrando la ventana), el archivo se vacía de contenido, vale decir, el archivo existe en la ruta original pero con 0 bytes, dentro no tiene nada.

Para que pueda probar este escenario, se tiene que corromper el archivo, por lo que ahora no lo puedo probar. Intenté editar el xml y borrar todo su contenido simulando una corrupción del archivo pero siempre adquiere contenido de alguna parte de los demás archivos.

La pregunta concreta: ¿Cuando se realiza la comparación de versiones, el código que me has pasado contempla la posibilidad de que la versión original sea nula y al no existir versión en el archivo original copie el respaldo?

Espero haberme explicado bien ya que soy novato en esto.

He tardado un poco porque se me resistía el bat al intentar incluir el tratamiento tanto de los archivos xml de longitud cero como, ya puestos, los archivos xml inexistentes. Creo que esta versión incluye ambos:

@echo off
set carpOrig=d:\Carpeta Original
set carpResp=d:\Carpeta Respaldo
set nomxml=NombreXML.xml
set versOrig=0
set versResp=0
call :version "%carpOrig%\%nomxml%" versOrig
call :version "%carpResp%\%nomxml%" versResp
echo version original es %versOrig% y version respaldo es %versResp%
if %versOrig% LSS %versResp% echo copy /y "%carpResp%\%nomxml%" "%carpOrig%\%nomxml%"&goto :eof
echo no se hace nada
goto :eof
:version
if not exist %1 echo No existe el archivo %1&goto :eof
if %~z1 LSS 1 echo El archivo %1 tiene longitud %~z1&goto :eof
for /f "tokens=3" %%a in ('findstr "Version" %1') do set version=%%a
set version=%version:~7,8%
set version=%version:.=%
set %2=%version%
goto :eof

Para probar el xml de longitud cero puedes buscar en tus carpetas que seguramente habrá algún archivo de longitud cero (se suelen crear cuando un programa que ha abierto un archivo aborta o se queda colgado). Si no lo encuentras a ojo puedes usar este bat:

@echo off
set unidad=c
%unidad%:
cd \
for /f "tokens=* delims=" %%x in ('dir /b /a-d /s *.*') do call :verCero "%%x"
goto :eof
:verCero
if #%~z1#==## echo El archivo %1 tiene longitud no definida&goto :eof
if %~z1 LSS 1 echo El archivo %1 tiene longitud %~z1&goto :eof
goto :eof

Tal como está buscaría archivos de longitud cero en la unidad c:. Cuando la longitud es demasiado grande para lo que permiten las comparaciones de los IF saldrá el mensaje de longitud no definida. Hay que recordar que la shell MSDOS se creó cuando los archivos no podían ser muy grandes y supongo que por eso da errores si el tamaño es muy grande pero como estamos buscando archivos de longitud cero no tiene mucha importancia. Y este bat tiene una extraña peculiaridad, en la que no he querido meterme porque no es más que una pequeña herramienta, y es que para que funcione debe abortarse con Control-C y luego elegir N para que siga ejecutándose. Una vez localizado algún archivo de longitud cero se puede abortar el BAT (esta vez eligiendo S), copiarlo en la carpeta que interese (Original o Respaldo), cambiarle el nombre para que tenga el del XML que interesa probar, y lanzar el otro BAT.

Ya me contarás.

Ya he logrado obtener el archivo de longitud cero y tienes mucha razón se crea cuando un programa que ha abierto un archivo aborta o se queda colgado.

Pero el código de arriba tiene algunos errores lógicos y no me funciona.

Para mi que estas 2 líneas con las causantes del problema:

if not exist %1 echo No existe el archivo %1&goto :eof
if %~z1 LSS 1 echo El archivo %1 tiene longitud %~z1&goto :eof

Luego de la etiqueta "version".

Si el archivo no existe, no debería ir al final, porque si hace eso, el programa lanzará el error de que no encuentra el archivo patch.xml y finalizará.

El mismo caso con la siguiente línea, si la versión es menor a 1 no puede ir al final, porque el patch xml con versión cero está corrupto y el programa volverá a finalizar.

En ambos casos debería copiar la versión del respaldo y luego si goto :eof

Por favor verifica mi razonamiento y si coincides conmigo, modificamelo para que el bat realice eso.

Un abrazo

Intuyo que el problema es que olvidé recordarte lo que ya te había dicho en mi primera respuesta:

Por último en lugar de un COPY real solo se hace ECHO del COPY para que puedas ver el aspecto. 

Los valores "iniciales" de ambas "versiones" son cero. Si la rutinilla VERSION aplicada a cada una de ellas da alguno de los dos errores previstos (archivo no existente o longitud cero) la versión correspondiente no se modifica y sigue valiendo cero mientras que la otra tendrá el valor leído. Por tanto el IF ... LSS se cumplirá y ejecutará la copia.

Si estoy en lo cierto bastaría con que elimines el ECHO que precede al COPY en ese IF.

Ya me contarás.

Tienes razón y mis disculpas por no advertirlo.

No te preocupes. Lo importante es encontrar la causa antes de empezar a darle vueltas. No sería la primera vez que un detalle tan simple me lleva a hacer un montón de pruebas antes de encontrar la razón. Por cierto que creo que deberías determinar si el programa que usas cambia o no la variable ERRORLEVEL. Puedes usar algo como esto:

@echo off
Echo Errorlevel vale %ERRORLEVEL%
type tarari.ppp
Echo Errorlevel vale %ERRORLEVEL%
Program
Echo Errorlevel vale %ERRORLEVEL%

El primer echo mostrará un errorlevel de cero, como corresponde a la correcta ejecución el comando anterior (@echo off). El segundo un errorlevel de uno porque supongo que no tienes ningún archivo que se llame tarari.ppp. Si en el tercero la ejecución de PROGRAM reescribe el ERRORLEVEL devolverá un valor de cero si ha ido bien.

El resultado me dio:

Errorlevel vale 0
El sistema no puede encontrar el archivo especificado.
Errorlevel vale 1
Errorlevel vale 1

Es un poco raro ese último valor del errorlevel. Si la ejecución del PROGRAM no ha dado ningún error es sorprendente que el errorlevel pase a valer 1. Pensaba que podía ser un valor "heredado" del comando anterior (en el que hubo un error y por tanto es razonable el valor 1 para el errorlevel) pero la ejecución deL propio comando ECHO debería haberlo "normalizado" a cero. De todas formas, para dejar más claro este punto, probaría con esto:

@echo off
Echo Errorlevel vale %ERRORLEVEL%
type tarari.ppp
Echo Errorlevel vale %ERRORLEVEL%
Echo Errorlevel vale %ERRORLEVEL%
Program
Echo Errorlevel vale %ERRORLEVEL%

Si las cosas son como pienso debería salir:

Errorlevel vale 0
El sistema no puede encontrar el archivo especificado.
Errorlevel vale 1
Errorlevel vale 0
Errorlevel vale 1

Y, si es así, repetiría la prueba con el XML de longitud cero, a ver que errorlevel da PROGRAM en este caso.

Bueno, el tema es complejo y te tendré que dar mas datos del juego, sino no podrás comprender.

Este juego es de un chat 3D llamado Utherverse, si yo arranco el programa con el ejecutable Utherverse.exe, me lleva a un lugar por defecto del juego, una especie de entrada, recepción de jugadores.

Pero además del ejecutable, yo puedo lanzar el programa poniendo una dirección "web" en el navegador con el sitio exacto que deseo ir dentro del juego, así no me lleva al lugar por defecto.

Los lugares, sitios del juego tienen una url asociada a ellos.

Puse entre comillas la palabra "web" porque las urls son especiales para este juego y son del tipo:

vww://

NO www://

Al yo poner una dirección que comience por vww://.... se lanza el juego automáticamente y me lleva al lugar del juego que puse en esa URL.

Porque toda esta explicación previa?

Porque si yo uso el .EXE del juego, los errores resultan de una manera, pero si yo lanzo el programa con la URL, los errores son otros.

CODIGO del programa lanzado con el EJECUTABLE 

@echo off
cd "C:/Program Files (x86)/Utherverse Digital Inc/PuabloSecreto 3DChat/Utherverse VWW Client/"
Echo Errorlevel vale %ERRORLEVEL%
type tarari.ppp
Echo Errorlevel vale %ERRORLEVEL%
Echo Errorlevel vale %ERRORLEVEL%
Utherverse.exe
Echo Errorlevel vale %ERRORLEVEL%

Devuelve:

Errorlevel vale 0
El sistema no puede encontrar el archivo especificado.
Errorlevel vale 1
Errorlevel vale 1
Errorlevel vale 0

CODIGO del programa lanzado desde URL vww.

@echo off
Set "lugar=vww://pueblosecreto.vww/@13598"
Echo Errorlevel vale %ERRORLEVEL%
type tarari.ppp
Echo Errorlevel vale %ERRORLEVEL%
Echo Errorlevel vale %ERRORLEVEL%
start %lugar%
Echo Errorlevel vale %ERRORLEVEL%

Devuelve:

Errorlevel vale 0
El sistema no puede encontrar el archivo especificado.
Errorlevel vale 1
Errorlevel vale 1
Errorlevel vale 1

Conclusión: Con el ejecutable devuelve el 0 al final, con la url, devuelve 1, pero todo se ejecuta normalmente en ambos casos.

Realmente la cosa parece un poco complicada pero, no obstante, voy a hacer unos comentarios:

1. He comprobado que sí que hay "herencia" del ERRORLEVEL y que el comando ECHO no resetea el ERRORLEVEL. Me ha sorprendido un poco y he buscado en la red. Según da a entender user95319 en https://stackoverflow.com/questions/1113727/what-is-the-easiest-way-to-reset-errorlevel-to-zero parece que no lo hacen los "comandos internos" de la shell MSDOS a diferencia de los otros comandos de esa shell. Para resetearlo se puede usar algún otro comando como "ver > nul" (como también indican ahí).

2. En la primera ejecución que me indicas, se usa directamente el programa ejecutable (UTHERVERSE.EXE). Por tanto si el ERRORLEVEL pasa a cero, como se ve en tus resultados, indica que ese ejecutable si que "toca" el ERRORLEVEL. Sin embargo la segunda "ejecución" no es una verdadera ejecución porque usa START (que es un comando externo) seguido de algo que no es un ejecutable sino una URL. Probablemente el ERRORLEVEL que se muestra en este caso es el "heredado" aunque también podría ser el que devuelve el comando START en sí. Habría que resetearlo antes del START, como indico en el punto anterior, para dilucidar entre esas dos posibilidades. También habría que ver que ocurre si en lugar de "START %lugar%" se utilizara directamente "%lugar%". Parece que START hace algo parecido a "Abrir" y si un tipo de archivo tiene asociado un programa de apertura lo usa directamente, pero no sé como se encaja eso en el ERRORLEVEL.

3. Sí, como decías, existe un PATCH.LOG pero no recoge los errores puedes añadir una redirección de las salidas a la invocación que hagas que hagas del ejecutable o apertura de URL. Algo como "utherverse > uther.log 2>&1" en el primer caso o "start %lugar% > uther.log 2>&1" en el segundo. Se supone que así recogerán los mensaje de error (además de cualquier otro mensaje que genere la ejecución o apertura).

Te comento, he probado ambas redirecciones, el archivo uther.log se crea, pero vacio con 0 bytes.

Y si le quito el comando "start" a la variable %lugar% no reconoce el comando la shell.

Errorlevel vale 0
El sistema no puede encontrar el archivo especificado.
Errorlevel vale 1
Errorlevel vale 1
"vww:" no se reconoce como un comando interno o externo,
programa o archivo por lotes ejecutable.
Errorlevel vale 9009

En realidad, esto se volvió más un desafío de mi parte, que la verdadera utilidad que busco.

Estoy muy conforme con los esfuerzos que has echo y creo que podríamos dejarlo por aquí.

Si por curiosidad, en algún momento que tengas libre deseas bajarte el juego y probarlo te dejo la url: http://www.pueblosecreto.com/  donde hay que registrarse y crear una cuenta y la url propiamente del juego: http://www.pueblosecreto.com/download.aspx donde supongo podrás ejecutarlo hasta la ventanita del login si no creaste una cuenta antes. 

Pero no lo hagas por mi, ya que veo que es muy complejo este tema y son muchos esfuerzos y horas en algo que no vale verdaderamente la pena.

Un abrazo y muy agradecido por compartir tus conocimientos.

Me ha gustado mucho que hayas seguido mis sugerencias para intentar avanzar algo más en este tema, incluso cuando parecía que estaba claro que ya podías darlo por cerrado. En esta ocasión ha quedado claro que no es lo mismo "start loquesea" que "loquesea" sin más.

Estoy de acuerdo en dejarlo aquí. Al menos hasta que te surja algún otro problema.

Ha sido un placer colaborar contigo.

Añade tu respuesta

Haz clic para o

Más respuestas relacionadas