Lenguaje de Comandos - Scripts
Lenguaje de Comandos - Scripts
Lenguaje de Comandos - Scripts
BAT
(1ª parte)
¿Qué es un script en lenguaje de comandos? No es nada más que un fichero de texto, que puede
generarse con el simple cuaderno de notas, y cuya extensión es .bat o .cmd. Su contenido son los
comandos que ejecutaríamos en una consola de comandos (cmd) y cuyo fin es evitar las tareas
repetitivas que podríamos realizar en una consola de comandos.
Aunque esta es la definición clásica, no debemos perder de vista que desde una consola de comandos
podemos realizar, mediante comandos, prácticamente todo lo que la imaginación nos permita. Todo lo
que se configura, o las tareas de mantenimiento que realizamos en Windows se pueden hacer desde una
consola de comandos. Igualmente existen muchos comandos que son sólo de consola.
Revisemos un poco la tipología de los comandos: un comando es "algo" que o bien entiende
directamente el shell (el intérprete de comandos, en este caso el cmd.exe) o bien es un programa con
extensión .com o .exe -o incluso un visual basic- que no use la interfaz gráfica y que por tanto esté
orientado a la consola. Un ejemplo clásico es el programa ipconfig. Este programa (de consola) nos da
la configuración TCP/IP de la máquina. O bien el programa ping.
ENTORNO DE UN PROGRAMA
Todos los sistemas operativos, y Windows no es una excepción, tienen un área de datos llamada
"entorno". No es nada más que un área donde se guardan ciertas variables con su contenido.
Es importante entender que cada programa de Windows tiene su entorno (igual o diferente a otro
programa). Los entornos se heredan. Cada programa (y el propio intérprete de comandos, cmd.exe, es un
programa más) cuando se lanza, "hereda" dicho entorno. Por heredar, no quiere decir que "use" el
mismo que el programa padre, sino que al lanzarse, el "loader" -cargador- del sistema operativo realiza
una copia del entorno padre en una nueva área de datos y al lanzar el programa le da como dirección del
área de entorno esa nueva copia del entorno del "padre".
En otras palabras, cualquier modificación en las variables de entorno dentro de un programa no afecta al
sistema ni al resto de programas, ya que lo que haría es modificar su propio entorno: la copia del entorno
original del programa padre.
El sistema operativo al cargarse predefine ya una serie de variables de entorno. Podemos verlas, bien
con botón derecho en Mi PC / propiedades / pestaña de opciones avanzadas / botón de variables de
entorno, o bien de una manera más simple, lanzando el intérprete de comandos (cmd.exe) y tecleando el
comando "set" (sin comillas).
NOTA: Realmente, aunque lo veamos en conjunto, existen dos entornos: uno del sistema y uno de
usuario, pero la visión de ambos es el conjunto de los dos.
Acabamos de ver nuestro primer comando: "set". Este comando nos permite no sólo ver todas las
variables, sino también definir, cambiar, borrar su contenido y algunas opciones más.
1
COMPUTERNAME=MIMÁQUINA
ComSpec=C:\WINDOWS\system32\cmd.exe
FP_NO_HOST_CHECK=NO
HOMEDRIVE=C:
HOMEPATH=\Documents and Settings\mi usuario
NUMBER_OF_PROCESSORS=1
OS=Windows_NT
Path=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;.....etc
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH
PROCESSOR_ARCHITECTURE=x86
PROCESSOR_IDENTIFIER=x86 Family 6 Model 7 Stepping 3, GenuineIntel
PROCESSOR_LEVEL=6
PROCESSOR_REVISION=0703
ProgramFiles=C:\Archivos de programa
...etc...
Las variables, dentro de una consola de comandos o bien dentro de un script se referencian para poder
ver su contenido encerradas entre símbolos de %. Por ejemplo, en el caso anterior, para ver el contenido
de la variable COMPUTERNAME, simplemente podemos ejecutar:
echo %COMPUTERNAME%
MIMÁQUINA
set COMPUTERNAME=nuevoNOMBRE
Pero...
* ¿Esto realmente tiene el efecto de cambiar el nombre del ordenador? por supuesto que no. Esto sólo
cambia el contenido de dicha variable. ¿Dónde lo cambia? pues tal y como hemos comentado
anteriormente, lo cambia en el entorno del programa, es decir, en la copia del entorno original heredado
por el programa. En nuestro caso, al ejecutarlo desde un cmd.exe, implica que cambia el entorno del
cmd.exe (y sólo de él, es decir, si tuviésemos lanzadas dos consolas de comandos, cada una de ellas con
cmd.exe, sólo se cambiaría en el cmd.exe que hayamos ejecutado ese comando "set"). Y ¿para que
puede servir? simple, si recordamos que un programa hereda una copia del entorno del programa que lo
lance, esto implicará que todo lo que lancemos desde esa consola de comandos, tendrá el contenido de
esa variable modificado.
* ¿Cómo podemos crear una nueva variable de entorno? tan simple como darle un nombre y asignarle su
contenido. Por ejemplo:
set nuevo=prueba
Esto creará si no existe, o modificará el contenido si existiese, de una variable de entorno llamada
"nuevo", y le asignará el contenido de "prueba".
* ¿Podemos asignar a una variable el contenido de otra?: sí, por supuesto. Simplemente recordando que
el contenido de una variable es precisamente el nombre de la variable encerrada entre símbolos %.
2
set otra=%nuevo%
Esto creará la variable "otra" con el contenido de la variable "nuevo", el cual era el texto "prueba". Si
después de ejecutar el comando anterior realizamos:
echo %otra%
su resultado será:
prueba
set otra=
En este caso borrará la variable. Si ahora damos el comando "set" que nos muestra todas las variables, la
variable "otra" ya no aparecerá.
* ¿Puedo concatenar textos con variables en una asignación?: Sí, por supuesto. Por ejemplo:
El resultado de lo anterior será que la variable "otra_de_nuevo" contendrá el valor "Esto es una prueba
de concatenación".
* ¿Podemos tener contenidos numéricos y no sólo alfabéticos, y por tanto realizar operaciones
matemáticas con el comando set? sí, se puede. La manera es con el modificador /a. Pongamos un
ejemplo:
set /a i=%n%/4
Veremos que esto nos devuelve por pantalla el valor 58 (es decir la división de 234/4) y además se lo
asigna a la variable "i".
Es necesario el modificador /a, ya que si no lo hubiésemos puesto, la expresión:
set i=%n%/4
no hubiese realizado la operación matemática y se hubiese quedado con el contenido (como literal) de:
234/4
es decir, una cadena de caracteres con ese contenido y no con el resultado matemático de la división.
Fijémonos, por curiosidad en este último caso y se puede ir realizando una prueba en una consola de
comandos.
Realicemos:
set n=234
set i=%n%/4
echo %i%
3
veremos entonces:
234/4
Si ahora realizamos:
set /a j=%i%
¿Que contendrá? ¿Contendrá el literal de texto "234/4"? ¿O sólo "234" ya que el carácter "/" no es
numérico? ¿O contendrá "58"?
La asignación a una variable numérica, puede ser decimal, hexadecimal u octal.
Podemos hacer:
set /a i=14
set /a i=0x0E
en ambos casos contendrá el decimal 14 (recordemos que el hexadecimal 0E equivale al decimal 14).
set /a i=021
hay que tener cuidado con esto, ya que el octal 021 es el decimal 17. Podéis comprobarlo en pantalla
simplemente tecleando la línea anterior y viendo el resultado. Siempre los resultados nos los expresará
en decimal.
Esto puede ser útil para convertir directamente de hexadecimal o bien de octal a decimal de una manera
rápida.
* Ya que hemos empezado con el comando "set" vamos a finalizar todo lo que dicho comando puede
hacer. Además de lo anterior:
set p
nos mostrará en pantalla "todas" las variables de entorno que empiecen por p.
Y para finalizar el modificador /p. Es decir, una sintaxis del estilo:
set /p variable=[literal]
lo que hace es muestra el "literal" en pantalla y el cursor se queda a continuación esperando que
metamos un dato. Cuando lo metemos y tecleamos "intro", lo que hayamos tecleado se asignará a la
variable de entorno definida en el set. Por ejemplo:
Introduce datos:_
4
datos por pantalla. Al pulsar la tecla "intro", se los asignará a dicha variable y continuará la ejecución
del script.
CARACTERES ESPECIALES
Hay ciertos caracteres "especiales" que hay que usar con extremada precaución: por ejemplo, evitar
usar, a no ser que lo necesitemos explícitamente, caracteres reservados como &, >, <, |, %, =, ^.
Pongamos un ejemplo. El carácter & ("and" en inglés, es decir "y" en castellano) se interpreta como un
"y", por tanto, ejecuta lo que hay antes del & "y" además a continuación, ejecuta lo que hay después del
&. Es decir, si ejecutásemos:
set var=a&a
"a" no se reconoce como un comando interno o externo, programa o archivo por lotes ejecutable
* ¿Qué ha hecho el sistema? Ha ejecutado el comando que hay antes del &, es decir:
set var=a
Por tanto le ha asignado a la variable de entorno "var" el contenido "a". A continuación, el intérprete de
comandos intenta ejecutar lo que hay después del &, es decir intenta interpretar el comando "a". Como
"a" no es un comando reconocido, ni un .exe, ni un .com, ni el nombre de otro script nos dará el error
mostrado anteriormente. Curiosamente:
set var=a&cmd
Pero... ¿y si realmente queremos asignar el contenido del literal "a&a" a la variable "var", cómo lo
hacemos? Simplemente anteponiendo el carácter ^ a cualquiera de los caracteres especiales. En nuestro
caso deberíamos haber hecho:
set var=a^&a
echo %var%
nos seguirá mostrando un error, ya que aunque realmente tiene el contenido "a&a", nos mostrará "a" e
intentará ejecutar lo que va a continuación: intentará la ejecución de un comando llamado "a".
Para ver que realmente tiene el contenido solicitado, podemos hacer dos cosas:
set v
el nos mostrará el contenido de todas las variables que empiezan por "v" y podremos comprobar que la
variable "var" contiene lo que realmente deseábamos. O bien, redirigir la salida a un archivo (los
símbolos de redirección y su significado los veremos más adelante).
5
NOTA: Un error bastante corriente cuando se usan caracteres de asignación y concatenación (&) en la
misma línea puede ser:
¿Cuál es, o puede ser, el error cometido? pues...recordemos que todo lo anterior al & se ejecuta como un
comando. Es decir: ¿qué hemos asignado a la variable "var"? podemos suponer que "prueba". Pero no es
así, hemos asignado todo lo anterior al &, es decir "prueba ", con un espacio en blanco al final. Esto nos
puede causar problemas posteriores si lo que queríamos hacer era asignar sólo "prueba".
echo *%var%*
*prueba *
(2ª parte)
Hemos visto hasta el momento que las variables de entorno no son nada más que cadenas de caracteres,
las cuales mediante algún modificador especial en el comando set (/a) pueden tratarse como numéricas
para realizar algunas pequeñas operaciones matemáticas.
Vamos a ver primero las funciones que podemos aplicar a las variables de caracteres y en segundo lugar
las operaciones matemáticas que podemos hacer así como su precisión cuando el contenido es numérico.
Igualmente veremos las llamadas variables dinámicas de entorno: variables que aunque no veamos
mediante el comando "set" pueden usarse en instrucciones de comandos.
Tengamos siempre presente que los caracteres "especiales" deben llevar el símbolo ^ precediéndolos.
Por ejemplo, un error que viene en la propia ayuda de Windows del comando set, es que la propia ayuda
nos dice: "El comando SET no permitirá que un signo de igual sea parte de una variable". Esto no es
"del todo" verdad. Si hacemos:
set var=a^=b
la variable "var" quedará con el contenido "a=b". Se puede comprobar si a continuación de lo anterior
ejecutamos:
set v
%var:~n,m%
6
Esto nos extrae de la variable "var", la subcadena desde la posición "n" con longitud "m". "n" es el
desplazamiento empezando a contar desde 0, por tanto, la primera posición es la 0, la segunda la 1, etc.
Pongamos un ejemplo:
set var=123456789
echo %var:~1,4%
nos mostrará la subcadena de "var" que va desde la posición 2 (recordemos: con desplazamiento +1 ya
que empieza por 0) y con longitud 4. Es decir, veremos: "2345".
%var:~1%
nos mostrará desde la posición 2 (offset de 1), hasta el *final* de la variable (ya que no se ha
especificado longitud). Por tanto, mostrará en el ejemplo anterior: "23456789".
Igualmente:
%var:~,5%
al no especificarse posición se asume desde el principio de la cadena y por tanto nos mostrará 5
caracteres iniciales. En nuestro caso "12345".
NOTA: si "n" es un número negativo, se refiere a la longitud de toda la cadena menos ese número. Por
ejemplo:
set var=123456789
echo %var:~-2%
set var=123456789
echo %var:~,-2%
nos mostrará todo menos los dos últimos caracteres de la cadena, es decir "1234567".
%var:str1=str2%
Este comando buscará todas las ocurrencias de la subcadena "str1" dentro de "var" cambiándolas por
"str2".
Por ejemplo:
7
echo %var:cómo=que tal%
Igualmente podríamos asignárselo de nuevo a "var" o bien a otra nueva variable de entorno. Por
ejemplo:
NOTA1: puede omitirse "str2". En ese caso la cadena str1 dentro de la variable var quedará eliminado.
Por ejemplo:
set var=12345xx6
set var=%var:xx=%
echo %var%
NOTA2: se cambiarán todas las ocurrencias de "str1". Si "str1" figura varias veces se cambiará todas
las veces en la cadena de caracteres.
Con el modificador /a en el comando set podemos realizar operaciones matemáticas *simples* sobre
variables.
Debemos primero hablar de la precisión: sólo se opera con números enteros. Por tanto la división de 5/2
nos dará como contenido un 2. Igualmente la máxima precisión es 2^32.
Por "expresión matemática" puede entenderse cualquiera de las siguientes y en orden de precedencia de
mayor a menor (extraído de la propia ayuda del comando set):
Si usamos una variable de entorno dentro de una expresión matemática y dicha variable no existe, será
asumido que contiene un cero.
Veamos los matices de lo anterior, ya que si no tenemos en cuenta algunas matizaciones ya comentadas
en este documento habrá expresiones que nos darán error.
8
IMPORTANTE: Volvamos a recordar que hay símbolos especiales que no pueden escribirse
directamente, por ejemplo: &, <, >, |, o incluso ^. Cada uno de ellos debe ir precedido por un ^. Es decir,
el bit a bit exclusivo ^, debe escribirse como ^^ ya que si no el intérprete de comandos lo interpretará
erróneamente.
set /a x=x+1
set /a x+=1
Lo mismo:
set /a z-=(x-y)/2+5^<^<2
Quizá no sea tan trivial el ver que el resultado de lo anterior es -16. Intentad razonarlo.
set /a x=(y=3+4)*(z=2+1)
x vale 21, y vale 7, z vale 3
Son variables que aunque no veamos mediante el comando "set" tienen contenido que va variando o
puede variar dinámicamente.
Un ejemplo de esto es la variable %TIME%. Cada vez que ejecutemos:
echo %TIME%
%DATE% - se expande a la fecha actual usando el mismo formato que el comando DATE.
%TIME% - se expande a la hora actual usando el mismo formato que el comando TIME.
%CMDEXTVERSION% - se expande al número actual de versión de las extensiones del comando del
procesador (lo veremos posteriormente)
Aunque lo veremos en detalle posteriormente, vamos a introducir el por qué algunas veces aparecen las
variables de entorno en un script encerradas entre símbolos "!" en vez de los clásicos "%" y cuándo
debemos usar el símbolo "!".
ATENCIÓN: El intérprete de comandos lee una línea de ejecución completa y lo primero que hace es
sustituir las variables de entorno antes de procesarla.
Esta máxima debemos tenerla presente siempre. Veamos un ejemplo que, aunque aparentemente está
bien escrito, nos producirá siempre resultados anómalos o no esperados:
set var=antes
if %var%==antes (set var=despues&if %var%==despues echo "Cambiado a despues")
Hemos introducido el comando "if". Realmente un "if" no es nada más que un "si" condicional en
castellano. Es decir: si se cumple una expresión, entonces se ejecuta lo que va a continuación.
Si además ponemos la palabra reservada "else", esto se ejecutará en caso contrario. Recordemos
igualmente que los paréntesis tienen precedencia.
Veamos entonces el conjunto de las instrucciones anteriores: asignamos "antes" a la variable "var".
En la segunda línea preguntamos: Si la variable "var" contiene "antes" -que sí que es verdad-,
ejecutaremos: asignar a la variable "var" la palabra "despues" y además a continuación comprobar que si
tiene el contenido de "despues" -lo cual parece que es evidente puesto que acabamos de hacerlo-
sacaremos por pantalla el texto "Cambiado a despues".
Nos parece por tanto obvio que lo que vamos a ver después de ejecutar los comandos anteriores es
"Cambiado a despues".
Pues bien, si lo hacemos...sorprendentemente veremos que no sale nada. ¿No lo cambia? Realmente lo
cambia, porque si a continuación de lo anterior ejecutamos:
echo %var%
veremos que contiene "despues". Entonces ¿por qué no ha ejecutado correctamente lo que tenemos
después del & en la línea anterior?
Volvamos a leer ahora despacio el párrafo que he comenzado anteriormente por "ATENCIÓN".
Repasemos lo que hace por defecto el intérprete de comandos: lee la línea *entera* y en ese momento el
contenido de las variables de entorno queda sustituido por el contenido real. A continuación, ejecuta la
línea entera. Es decir lo primero que hace el intérprete de comandos es la "expansión" de la línea, y por
tanto, antes de ejecutarla, quedaría:
10
Este es el funcionamiento por defecto del intérprete de comandos. Si queremos que no funcione así, y
que haga dinámicamente la expansión de las variables (y no "antes" de ejecutar la línea de comando)
hay que ejecutar dos cosas:
1) Arrancar el intérprete de comandos con el parámetro /V:ON para indicarle que debe expandir las
variables. (Si estamos en un script, colocar como primera línea del script: setlocal
ENABLEDELAYEDEXPANSION. Lo veremos más en detalle posteriormente)
2) Referenciar las variables que deben ser expandidas en ese momento, y no en el momento de leer la
línea por primera vez mediante los símbolos de "!" en vez de "%".
cmd /V:ON
2) Ejecutamos:
set var=antes
if %var%==antes (set var=despues&if !var!==despues echo "Cambiado a despues")
(3ª parte)
Cuando ejecutamos un comando en una consola puede ser de dos tipos: o bien un comando interno de la
shell de comandos (cmd.exe) o bien un programa externo (.com, .exe, .bat, etc.).
Lo primero que verifica el intérprete de comandos es si es un comando interno (por ejemplo "copy"). Si
lo es, lo ejecuta directamente. Si no pertenece a su tabla interna de comandos, empieza su búsqueda:
1) Si se ha tecleado extensión del comando, por ejemplo: "ping.exe" se buscará sólo con dicha
extensión. Si no se ha tecleado extensión, es decir, simplemente es "ping", lo primero que hace el
sistema es localizar la variable de entorno %PATHEXT% para ver las posibles extensiones y su orden
de búsqueda.
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH
11
2) Hemos hablado de que intentará localizar los anteriores. Pero ¿dónde?
2.3) Si al final de esta búsqueda lo encuentra, lo ejecutará. En otro caso nos enviará un
mensaje de error como por ejemplo:
C:\>micomando
"micomando" no se reconoce como un comando interno o externo, programa o archivo por lotes
ejecutable.
*** Debemos fijarnos en que básicamente un comando es "algo" que puede solicitar datos de entrada,
posteriormente hace "algo" y por último nos da datos de salida.
Por ejemplo:
El comando (interno) copy, recibe datos, en este caso de la propia línea de comandos (y en la consola).
Esos datos son dos: "c:\carpeta\archivo.dat" y "d:\backup". A continuación ejecuta la copia. Y por
último nos informa de cómo ha ido esa copia. Nos puede dar dos tipos de mensajes:
En general los programas o comandos tienen una entrada de datos estándar (STDIN), y una salida de
datos cuando ocurre un funcionamiento correcto (STDOUT) y cuando ocurre algún tipo de error
(STDERR).
Fijémonos que las tres, en los casos de comando de consola, en general, están redirigidas a la propia
consola. Tanto la entrada de datos como los mensajes, sean o no de error, "entran" y "salen" por
pantalla. Pero... el comando o programa, internamente los redirige a salidas o entradas que pueden
físicamente estar redirigidas, o ser redirigidas desde dispositivos que no sean la consola.
Técnicamente, esto se hace a nivel del "handle" (manejador) del fichero. Algunas veces en la literatura
hispana esto se ha mal traducido por controlador. Básicamente un "handle" es un simple número que
referencia internamente en un programa a un fichero. Fijémonos que realmente la entrada de datos es
"como" si estamos leyendo de un fichero y la salida es como si se "escribe" en un fichero, aunque en el
ejemplo que acabamos de ver, estos ficheros son redirigidos a la propia consola.
Existen tres números, o handles, que históricamente en informática han representado siempre lo mismo:
el 0 (STDIN), el 1 (STDOUT) y el 2 (STDERR). El resto son libres, a voluntad del programador.
Por tanto, y de cara interna a un programa, los datos que recibe de entrada se leen por STDIN, los
mensajes de salida se escriben en STDOUT, y los mensajes de error en STDERR y además se activa un
código de error (aunque algunas veces es costoso de distinguir, ya que sólo está en la cabeza del
programador que lo haya realizado, qué es lo que él considera mensaje informativo y lo que considera
mensaje de error... pero esto es otra cuestión).
12
Debido a la estandarización del STDIN, STDOUT y STDERR ("handles" 0, 1 y 2) lo que normalmente
hace un sistema operativo es redirigir estos a la consola. Este es el comportamiento estándar del sistema,
pero nosotros podemos igualmente hacer que sean redirigidos a donde queramos, tal y como veremos
posteriormente.
Por ejemplo, en el caso del ejemplo anterior del comando "copy", podemos hacer:
El símbolo ">" está indicando que todo lo que vaya a salir por el STDOUT se escribirá, en vez de en la
consola, en un archivo de texto c:\log.txt. Repito, porque es importante el matiz: STDOUT a fichero de
texto.
Es decir, si el comando copy no puede copiar, recordemos que el mensaje de error lo saca en STDERR,
por tanto en el fichero de texto no se escribiría nada, ya que en el ejemplo anterior, con el símbolo >,
sólo se ha redirigido el STDOUT. El STDERR queda con los valores predeterminados por el sistema
operativo: es decir, se escribe el mensaje de error en consola.
*** Hemos comentado que a voluntad del programador de un comando o programa, este puede terminar
correctamente o bien establecer un código de error al finalizar (escriba o no además un mensaje
explicativo). El sistema operativo es el encargado de capturar esos códigos de error y los deja a nuestra
disposición en una variable de entorno llamada %ERRORLEVEL%
Podemos, por ejemplo, verificar cual es el comportamiento del comando "copy". Para ello vamos a
realizar el siguiente ejemplo creándonos una carpeta de trabajo llamada c:\tmp
c:
cd \
md tmp
echo "texto de un fichero" > c:\tmp\fichero.txt
Con esto nos hemos posicionado en c: (por si no lo estuviésemos), hemos ido a su carpeta raíz (cd \), y
hemos creado una carpeta llamada tmp en donde estuviésemos posicionados en ese momento (md tmp)
-md es abreviatura de "makedir": crear directorio-. La última línea (echo) nos mostraría por pantalla
"texto de un fichero", es decir nos lo mostraría en el STDOUT. Pero con el símbolo ">" lo que hacemos
es redirigir el STDOUT (la consola en este caso) a un fichero que en nuestro caso es: c:\tmp\fichero.txt.
Si lo abrimos con el cuaderno de notas, veremos realmente el texto anterior.
echo %ERRORLEVEL%
veremos que el comando anterior nos informa que ha terminado con 0 (se considera normalmente 0 una
finalización correcta).
Vamos a provocar que el copy termine con error para ver cual es su comportamiento en este caso. Un
método muy sencillo seria proteger contra escritura el fichero de salida y luego intentar escribir en él. Es
decir:
13
Vemos que ahora en pantalla nos informa que "Acceso denegado". Si a continuación ejecutamos:
echo %ERRORLEVEL%
NOTA: en general los mensajes de error deben buscarse en la documentación del programa o comando
ya que son a voluntad del programador. El sistema operativo lo único que hace es situarnos el código de
retorno en %ERRORLEVEL% y nosotros podremos tomar acciones en un script de comandos
analizando dicha variable de entorno cuando nos interese controlar los códigos de terminación.
Bien, lo anterior no ha sido más que un ejemplo para intentar introducir los "redirectores" del sistema
operativo.
OPERADORES DE REDIRECCIÓN
> Escribe la salida del comando (normalmente STDOUT) en un fichero o un dispositivo (puede por
tanto redirigirse a otro dispositivo, no sólo a archivo), en lugar de en la ventana del Símbolo del sistema.
< Lee la entrada (STDIN) del comando desde un archivo, en lugar de leerla desde la consola.
>> Añade la salida del comando al final de un archivo sin eliminar la información que ya está en él.
>& Escribe la salida de un controlador en la entrada de otro controlador (lo vemos posteriormente en
detalle)
| Lee la salida de un comando y la escribe en la entrada de otro comando. Mal traducido por
canalización o filtro ("pipe")
Hemos visto que con >& se escribe la salida de un controlador ("handle") en otro. Vayamos a un
ejemplo práctico. En los ejemplos anteriores hemos visto que la salida de un comando como el copy
podemos escribirlo en un fichero con el símbolo >. Pero también hemos comentado que sólo redirige en
STDOUT, por tanto, si el copy enviase un mensaje de error (STDERR) esto no se vería en el fichero.
Esto es un problema si lo que queremos es crear un log de ejecución de un script para poder ver
posteriormente la ejecución: los mensajes de error precisamente los habríamos perdido.
Pero... recordemos que > escribe el STDOUT. Si fuésemos capaces de "redirigir" el STDERR al
STDOUT, entonces el > nos escribiría todos los mensajes, de funcionamiento correcto o erróneo en el
fichero.
Es decir, si hacemos:
sólo los mensajes de funcionamiento correcto quedarían en c:\log.txt. Pero en cambio si redirigimos la
salida de errores: STDERR ("handle" numero 2) al STDOUT ("handle" numero 1) y luego escribimos,
en ese caso todo quedaría reflejado en el archivo log. Por tanto:
*** Operador de canalización ("|") o "pipe". Este es de los más interesantes ya que permite pasar "toda"
una salida de datos a un programa que espera "toda" una entrada de datos en flujo. No todos los
14
programas están preparados para admitir este tipo de redirección y debe leerse la documentación de cada
uno de ellos al respecto.
Tenemos tres programas del sistema operativo "more" (más), "find" (buscar) y "sort" (ordenar) que
admiten pipes y que nos pueden servir de ejemplo.
Veamos. Un dir /s nos muestra todos los archivos desde donde estemos posicionados incluyendo todos
los archivos en subcarpetas. Si ejecutamos:
dir c:\ /s
veremos que empiezan a pasar pantallas de datos que no somos capaces de leer. Podemos redirigir la
salida de este comando al "more". Realmente el "more" no es nada más que un programa que admite
como entrada toda la salida de otro comando y que lleva un contador de líneas para irnos mostrando en
pantalla 24 líneas y luego esperar a que pulsemos una tecla.
Un ejemplo de canalización y redirección: imaginemos que queremos en nuestro disco todos los
archivos con extensión .log y dejar la información en un fichero de salida.
Los parámetros que he puesto, tanto en el comando dir, como en el comando find, siempre podemos
verlos ejecutando dir /? y find /? para buscar los que mejor se adapten a lo que queremos.
Los filtros ("pipes") no sólo admiten canalización. También pueden usarse, como cualquier programa,
con comandos de redirección para leer entradas y escribir salidas en archivos. Por ejemplo, imaginemos
que tenemos un fichero con nombres y queremos ordenarlo alfabéticamente. Simplemente:
Esto indicaría que la entrada al comando sort viene redirigida desde el fichero lista.txt y que la salida, en
vez de dejarla en pantalla, nos la escriba en listaalf.txt.
*** Los símbolos de redirección y canalización son potentísimos y a veces, incluso a un programador
avezado le pueden causar más de un problema o costarle su correcta interpretación.
En ejemplos que iremos viendo a lo largo de estos capítulos quedará más claro el concepto.
OPERADORES CONDICIONALES
Lo que exista después del operador && se ejecutará "sólo" si la instrucción previa a él -en la misma
línea- ha terminado correctamente (código 0).
15
Por contra, si usamos || sólo se ejecutará si la terminación de la instrucción previa termina
incorrectamente (código distinto de cero)
Por ejemplo:
Aunque esto no tiene nada que ver con los scripts, podemos fijarnos en que al abrir un intérprete de
comandos (cmd.exe) no se pueden realizar funciones de copiar y pegar, lo cual puede sernos incómodo
en Windows.
La acción "pegar", es simplemente botón derecho (sin haber marcado nada previamente). Lo que
tengamos en el portapapeles (lo hayamos llevado mediante la acción anterior, o bien lo hayamos
copiado desde cualquier otra ventana Windows) lo pegará en donde esté el cursor.
(4ª parte)
Acabamos de ver unos conceptos sobre entorno, comandos, variables, etc., que son básicos para poder
realizar un script, pero hasta el momento no hemos visto ninguna instrucción excepto las puestas en los
ejemplos previos, ni tampoco hemos visto cómo el shell ejecuta el script y cómo se puede tener control
sobre la ejecución.
Básicamente un script de comandos, tal y como hemos comentado, no es nada más que escribir en un
archivo de texto con extensión .bat o .cmd todas las instrucciones que iríamos haciendo manualmente.
Además, dentro del propio script, en función de resultados que vayamos obteniendo se pueden tomar
acciones.
Instrucción: ECHO, su objetivo es mostrar por el STDOUT (normalmente la consola) el literal que
exista a continuación. Recordemos que lo primero que hace el intérprete de comandos es "expandir" las
instrucciones, es decir, si existe una variable de entorno (delimitada por símbolos %), lo primero que
hace es sustituir dicha variable por su contenido. Es decir, si escribimos en un script:
1) Realiza la sustitución de las variables de entorno, por tanto la línea quedaría como:
16
ECHO esto es una prueba
Como ECHO es un comando interno que escribe en el STDOUT lo que hay a continuación, nos
mostrará el literal "esto es una prueba".
2) Intentaría ejecutar la instrucción. "ECHA" no es el nombre de un comando interno, por tanto buscaría
en la carpeta en curso y posteriormente en el PATH un programa (otro script, o un .com, o un .exe, etc...
de acuerdo a las reglas vistas anteriormente). Si lo encuentra ejecutará el programa o script "ECHA" y si
no lo encuentra nos dirá que sucede un error.
Estas dos últimas son instrucciones a ejecutar sólo dentro de un script. Veamos su uso. Nos creamos un
script llamado por ejemplo: pr.cmd en mi usuario y en una nueva carpeta de pruebas llamada "tmp" con
el contenido:
set var=Hola
echo %var% mundo.
set var=
Es decir, nos ha ido mostrando todas las líneas que va ejecutando y mezclado con las salidas que a su
vez va dando el script.
Si queremos suprimir la salida de las líneas y dejar solamente la salida de los comandos, es decir "Hola
mundo", lo primero que debemos decirle al script es que no queremos "ECO" de lo que va traduciendo.
Este "ECO" es útil cuando estamos depurando y probando un script, pero, cuando lo tenemos finalizado
y correcto, lo lógico es ver sólo la salida y no línea por línea. En este caso podemos poner como primera
línea del script:
@ECHO OFF
17
El símbolo @ como primer carácter en una línea del script indica que no queremos que esa línea se
"vea" en la ejecución. Si además le damos la orden ECHO OFF, le estamos diciendo que hasta el final
del script, o bien hasta que se encuentre otra orden ECHO ON, no muestre nada de la ejecución (sí de la
salida de instrucciones) por pantalla.
Es decir, en nuestro caso, si no queremos ver nada más que el literal "Hola mundo", nos quedan dos
posibilidades:
Vamos a hacer un paréntesis en este capítulo e introducir el concepto de nombre lógico de dispositivo.
Hay ciertos dispositivos que podemos usar en cualquier comando o dentro de un script con nombres
reservados que se refieren a dispositivos, normalmente lógicos, de nuestra máquina. Por ejemplo:
CON se refiere a la consola. Lo que se envíe o reciba desde la consola se puede referenciar con CON.
LPT1 se refiere a la impresora. Lo que se envíe a él se escribirá en la impresora (si está asignada a ese
puerto).
Aunque existen más dispositivos lógicos, llegado este punto sólo vamos a ver estos tres. Veamos unos
ejemplos:
1) Imaginemos que hemos recibido una clave de registro de un producto por mail, y lo queremos
guardar en un fichero llamado licencia.txt en una carpeta determinada.
La manera clásica de hacerlo mediante Windows es abrir el cuaderno de notas, copiar la clave desde el
mail recibido y guardar el fichero en la carpeta que queremos con el nombre licencia.txt. Bien,
podríamos hacer en una consola de comandos:
En este punto se quedará el cursor esperando una entrada. Tecleamos la clave recibida (o la copiamos
desde el mail), y para finalizar la entrada de datos le damos a CTRL-Z (CTRL-Z es la marca estándar de
fin de fichero). Es decir, hemos copiado desde la consola a un fichero de la misma forma que si
hubiésemos copiado un fichero a otro fichero.
3) No queremos ver, por ejemplo después de un comando copy la línea de "xx archivos copiados" que
siempre nos informa el copy. Simplemente redirigimos la salida al dispositivo nulo.
18
Una vez hecho el paréntesis anterior, vamos a ver las instrucciones de control flujo. Básicamente son 3.
Permite en un punto determinado de nuestro script "saltar" a otro punto del script. Normalmente se usa
después de una pregunta lógica por algún resultado (por ejemplo, dentro de una instrucción IF).
GOTO nombre_etiqueta
En este caso, al ejecutar la instrucción GOTO, buscará en el script la línea :nombre_etiqueta y bifurcará
a dicha línea.
NOTA: Existe una etiqueta implícita en cualquier script que no es necesario definir, :EOF (End Of File
- Fin de fichero). Por tanto, la ejecución de la instrucción GOTO :EOF implica que se saltará hasta el
final del fichero y por tanto finalizará el script.
Recordemos que lo primero que se hace es la expansión de variables. Hay que tener muchísimo cuidado
con esto y no me gustan las instrucciones del estilo anterior a menos que tengamos delimitadores
específicos. Voy a intentar explicarlo. Imaginemos que tenemos una variable "var1" con contenido "1" y
otra "var2" con contenido también "1". La instrucción:
Pero... ¿y si "var1" contiene "Hola mundo", que sucedería? Pues un desastre. Al expandir la expresión
quedaría:
Detrás del hola no hay símbolo de comparación, por lo cual la instrucción es sintácticamente incorrecta.
Lo mismo pasaría si "var1" o "var2" no tienen contenido.
En estos casos, el truco consiste en encerrarlas entre algún delimitador. Por ejemplo, comillas -ya que
estas definen un literal. Es decir:
19
if "%var1%"=="%var2%" echo "valen lo mismo"
El /i realiza la comparación de tal manera que no se distingue entre mayúsculas y minúsculas. Es
decir "HOLA" es lo mismo que "hOLa".
Fijémonos que hemos puesto no un == (igualdad) en este caos, sino que por primera vez se ha puesto
"operadorDeComparacion". Este operador que veremos a continuación, y el símbolo == no es nada más
que uno de ellos.
OPERADORES DE COMPARACIÓN
Especifica un operador de comparación de tres letras. En la siguiente tabla puede ver los valores de
operadorDeComparación.
PARÁMETROS
Cualquier script es capaz de recibir parámetros desde la línea invocante. Se considera parámetro
cualquier literal enviado en la línea que invoca al script.
El sistema considera que los parámetros están separados por espacios en blanco o bien por el
delimitador ";".
Cada parámetro se referencia dentro del script por %1, %2, %3, etc...
Vamos a crear un script que reciba dos parámetros, el nombre de un fichero y el nombre de una carpeta.
El objetivo del script es copiar el archivo desde la carpeta en donde estemos posicionados en la carpeta
destino, siempre y cuando no exista ya en el destino.
Una manera grosera de realizar el script, llamémosle copia.cmd, sin verificar que los parámetros son
correctos, sería:
@echo off
if not exist %2\%1 copy %2 %1
20
al desarrollar la instrucción el script, realizaría:
lo cual es precisamente lo que queríamos hacer. He comentado que este script, tal y como está, está
realizado de una manera grosera. Si el nombre del archivo o el nombre de la carpeta destino tuviese
espacios en blanco el sistema consideraría que dicho espacio en blanco es un delimitador de parámetros
y por tanto nos llegarían al script más parámetros de los deseados. Realmente para realizar
correctamente el script deberíamos hacer:
@echo off
if {%1}=={} goto error
if {%2}=={} goto error
if {%3} NEQ {} goto error
:error
echo parámetros erróneos. Deben recibirse dos parámetros.
goto :EOF
:error1
echo Nombre del fichero origen no existe.
goto :EOF
:error2
echo Carpeta destino no existe.
goto :EOF
:aviso:
echo El archivo ya existe en el destino. No se ha realizado copia.
goto :EOF
:correcto
echo Archivo copiado correctamente.
goto :EOF
Nótese que hemos entrecomillado cada uno de los parámetros. De esta manera sólo se recibirán dos
parámetros ya que si no, se recibirían 4 al usar como delimitador el sistema los espacios en blanco entre
ellos.
21
* Verificamos que hemos recibido 2 parámetros y sólo 2.
* Verificamos que exista el fichero origen
* Verificamos que exista la carpeta destino. Para verificarlo usamos un pequeño truco: preguntar por el
dispositivo "nul" en la carpeta destino. Si no existe, es que la carpeta no existe.
* Verificamos que de acuerdo a las especificaciones del script no exista ya el fichero en la carpeta
destino.
* Realizamos la copia. Redirigimos todo a nul para que no de mensajes de archivos copiados.
* Comprobamos el código de retorno y si no es cero enviamos el mensaje correspondiente.
* Damos un mensaje de copia correcta cuando se ha finalizado correctamente el script.
Bien, el ejemplo anterior nos puede servir para la introducción de lo que son los parámetros y cómo se
deben usar o referenciar en el script. Vamos a ver toda la casuística más en detalle.
@echo off
echo Parámetro 0: %0
%1 a %9 son los posibles parámetros enviados en la línea de comandos. Realmente pueden pasarse más
de 9 parámetros pero sólo 9 de ellos serán accesibles directamente. Si pasásemos más, hay que usar el
comando shift, el cual tiene por objeto desplazar los parámetros. Es decir, al ejecutar shift lo que se hace
es que el %2 pasa a ser %1 (el %1 se pierde por lo que debemos guardarlo), el %3 pasa a ser el %2,
etc... De tal manera que al final el %9 pasa a contener el posible décimo parámetro pasado. Realicemos
como prueba:
@echo off
echo Antes de desplazar
echo Parametro 1: %1
echo Parametro 2: %2
echo Parametro 3: %3
echo Parametro 4: %4
echo Parametro 5: %5
echo Parametro 6: %6
echo Parametro 7: %7
echo Parametro 8: %8
echo Parametro 9: %9
shift
echo Despues de desplazar
echo Parametro 1: %1
echo Parametro 2: %2
echo Parametro 3: %3
echo Parametro 4: %4
echo Parametro 5: %5
echo Parametro 6: %6
echo Parametro 7: %7
echo Parametro 8: %8
echo Parametro 9: %9
shift
echo Despues de desplazar de nuevo
echo Parametro 1: %1
echo Parametro 2: %2
echo Parametro 3: %3
echo Parametro 4: %4
22
echo Parametro 5: %5
echo Parametro 6: %6
echo Parametro 7: %7
echo Parametro 8: %8
echo Parametro 9: %9
parm.cmd 1 2 3 4 5 6 7 8 9 10 11
@echo off
echo Toda la línea de parámetros es: %*
y ejecutamos
parm.cmd 1 2 3 4 5 6 7 8 9 10
podemos comprobar la salida. Esto puede ser útil si luego queremos tratar todo como una sola línea y
usamos comandos específicos, que veremos más adelante, para tratar dicha línea.
A los parámetros y sólo a los parámetros, pueden anteponérseles modificadores los cuales realizan una
conversión especifica. Vamos a ver un ejemplo y a continuación daremos todas las posibilidades.
@echo off
echo Parámetro 1 expandido: %~f1
echo Parámetro 2 expandido: %~f2
Al ejecutar:
nos devolverá:
Es decir, el delimitador %~f por delante del numero de parámetro nos expande el parámetro
considerándolo como si fuese nombre de archivo (sin verificarlo) al nombre completo de unidad, ruta y
archivo en donde se esté ejecutando el script.
Realmente todos los modificadores posibles de parámetros son los que se describen a continuación:
23
%~d1 Expande %1 a una letra de unidad.
%~p1 Expande %1 a una ruta de acceso.
%~n1 Expande %1 a un nombre de archivo.
%~x1 Expande %1 a una extensión de archivo.
%~s1 Ruta de acceso expandida que únicamente contiene nombres cortos.
%~a1 Expande %1 a atributos de archivo.
%~t1 Expande %1 a una fecha/hora de archivo.
%~z1 Expande %1 a un tamaño de archivo.
%~$PATH:1 Busca los directorios enumerados en la variable de entorno PATH y expande %1 al
nombre completo del primer directorio encontrado. Si el nombre de la variable de entorno no está
definido o la búsqueda no encuentra el archivo, este modificador se expande a la cadena vacía.
La tabla siguiente enumera las combinaciones posibles de modificadores y calificadores que puede usar
para obtener resultados compuestos.
NOTA: En los ejemplos anteriores, se puede reemplazar %1 y PATH con otros valores de parámetros
de proceso por lotes.
No se pueden manipular parámetros de proceso en los scripts de la misma manera en que se manipulan
variables de entorno (funciones de manejo de cadenas vistas anteriormente). No se pueden buscar y
reemplazar valores ni examinar subcadenas. Sin embargo, se puede asignar el parámetro a una variable
de entorno para, después, manipular la variable de entorno.
Aunque no es necesario memorizarlos sí que es importe saber que existen, ya que algunas veces nos
puede ser necesario usarlos para expansión sobre todo en nombres de archivos.
Muchas veces es necesario desde un script llamar a otro procedimiento, o llamar a una subrutina del
mismo procedimiento. Vemos ambos casos.
* Llamada a procedimiento externo. Vamos a crearnos dos script. El primero de ellos lo llamaremos
principal.cmd y con el contenido:
@echo off
echo estoy en el principal
call invocado.cmd
echo de vuelta en el principal
@echo off
echo estoy en el invocado
Si lo ejecutamos veremos los resultados. Recordemos que tanto el procedimiento principal como el
invocado pueden recibir parámetros y podemos jugar con ellos como queramos. Por ejemplo:
principal.cmd:
24
@echo off
echo estoy en el principal
echo mi primer parámetro: %1
echo mi segundo parámetro: %2
call invocado.cmd %2
echo de vuelta en el principal
invocado.cmd:
@echo off
echo estoy en el invocado
echo el parámetro del invocado es: %1
Nótese, que el ejemplo anterior, si en vez de llamar con CALL, hubiésemos omitido dicho CALL, es
decir la línea:
CALL invocado.cmd
sólo tuviese:
invocado.cmd
también se ejecutaría el invocado. Pero cuando este terminase, terminaría todo y no volvería al script
principal.
@echo off
echo Parámetro 1: %1
echo Parámetro 2: %2
call :rutina1 %2
echo estoy de nuevo en el principal
call :rutina1 %1
echo estoy de nuevo en el principal
call :rutina2 parametrosjuntos%1%2
goto final
:rutina1
echo en la rutina recibo parámetro: %1
goto:EOF
:final
echo estoy ya al final
25
Fijémonos que la subrutina la terminamos con goto :EOF. Esto indica que cuando se la invoca con
CALL volverá a la línea de continuación del invocante (realmente salta a fin de fichero, con lo que
provoca la condición de vuelta que hemos citado anteriormente).
(5ª parte)
Hemos comentado anteriormente que cada programa tiene su área de entorno la cual es una copia del
área de entorno del programa invocante, y que por tanto, si se modifican o añaden variables de entorno,
esto *sólo* afecta a dicho programa (y por supuesto a los que él invoque, ya que heredarán su entorno).
Si un script llama a otro script (mediante CALL) tendrá acceso a todas las variables modificadas en el
script principal.
No es conveniente modificar el entorno del cmd.exe (aunque sea el invocante del script), y lo ideal sería
que el script, al finalizar, dejase el entorno tal y como estaba antes de arrancarse.
@echo off
set a=%1
set computername=%a%_mio
....
Esto es válido y hemos usado una variable llamada "computername" simplemente porque se nos ha
ocurrido en el script. Pero, curiosamente, ese nombre de variable ya existía en el sistema (en el entorno
del cmd.exe). Por tanto, lo que sucede es que al finalizar el script, ese cmd.exe -y sólo ese- se quedará
en su entorno con una variable que ya existía modificada por el script, y además con otra variable "a"
que antes de empezar el script no tenía valor.
Lo ideal sería no usar nombres que ya existan en el entorno en el propio script, y además que el script
limpiase sus variables internas antes de finalizar. Esto es engorroso y normalmente difícil de controlar.
@echo off
setlocal
set a=%1
set computername=%a%_mio
......
endlocal
26
Es decir, le estamos diciendo desde la instrucción "setlocal" que todo lo que hagamos será local al
script. Y cuando se ejecuta "endlocal" (al final del script) le estamos diciendo que deshaga todas las
variables usadas y permanezca tal y como estaba desde que se ejecutó la anterior instrucción "setlocal".
Como norma es conveniente en los scripts (o al menos en el principal si usásemos llamadas CALL -esto
dependerá de la lógica-) el usar un "setlocal" al inicio del script y un "endlocal" como última instrucción
del script.
Algunas de ellas -las dos últimas- ya las hemos comentado: por defecto el intérprete de comandos tiene
desactivada la expansión diferida (a menos que se haya arrancado con /V:ON o bien se hayan
modificado ciertas claves de registro para cambiar el comportamiento general del intérprete de
comandos), y por ello hay que prestar especial atención a la expansión de las variables de entorno en la
línea de comando antes de que esta pase a ejecución (recordemos la sintaxis con "!" como delimitador
de la variable de entorno en vez de usar el delimitador "%" normal. Es conveniente revisar ese capítulo
ya que el concepto es extremadamente importante).
Debido a que es posible que el sistema en donde se vayan a ejecutar nuestros scripts pueda haberse
modificado el comportamiento global, siempre es conveniente forzar en nuestro script principal lo que
deseamos.
27
Mi consejo además es permitir la expansión diferida del entorno, por tanto, es conveniente siempre
poner al comienzo del script principal la instrucción:
Llegado a este punto ya podemos ver todos los comandos internos que tiene el intérprete de comandos.
Recordemos igualmente que hay una lista alfabética y detallada en el fichero de ayuda ntcmds.chm que
reside en \windows\help. Es conveniente para los que se vayan a dedicar a la escritura de scripts el tener
un acceso directo en Windows a dicho archivo. En esa lista alfabética figuran tanto los comandos
internos de Windows como los externos que nos da la propia versión del sistema operativo. Vamos a dar
un repaso de todas formas y un pequeño comentario a cada uno de ellos, excepto para el comando más
potente de Windows: el comando FOR, el cual merecerá todo un capítulo aparte. Es conveniente llegado
a este punto que se vaya consultando cada uno de los comandos que citaré a continuación en
ntcmds.chm.
Es el comando más importante y más potente que podemos usar en un script. Por tanto, vamos a verlo
en detalle y haciendo hincapié en ejemplos de su uso.
NOTA: Es el único comando que tiene sintaxis diferente ejecutado desde la línea de comandos y
ejecutado desde un script. Los subíndices del comando -tal y como se verán a continuación- se expresan
con un símbolo % desde la línea de comandos, pero en un script deben expresarse con un doble símbolo,
es decir con %%.
Sintaxis. Admite varias formas: (recordando que en script %variable debe ser %%variable)
%variable
Requerido. Representa un parámetro reemplazable. Utilice %variable para ejecutar for en el símbolo del
sistema. Utilice %%variable para ejecutar el comando for dentro de un archivo por lotes. Las variables
distinguen entre mayúsculas y minúsculas y se deben representar con un valor alfabético, como %A,
%B o %C.
(grupo)
Requerido. Especifica uno o varios archivos, directorios, intervalo de valores o cadenas de texto que se
desea procesar con el comando especificado. Los paréntesis son obligatorios.
El parámetro grupo puede representar un único grupo de archivos o varios. Puede utilizar caracteres
comodín (es decir, * y ?) para especificar un grupo de archivos. Los siguientes son grupos de archivos
válidos:
(*.doc)
(*.doc *.txt *.me)
(ene*.doc ene*.rpt feb*.doc feb*.rpt)
(ar??1991.* ap??1991.*)
nos mostrará los archivos que están dentro de la carpeta c:\windows (similar a un "dir" pero sólo nos
mostrará el nombre.
Ejercicio [for-1]: usando el comando for en la forma citada anteriormente obtener únicamente el nombre
del archivo. Si lo ejecutamos vemos que la salida es c:\windows\fichero.ccc, c:\windows\fichero2.xxx,
etc., y lo que queremos es únicamente el nombre y extensión del archivo.
29
Evidentemente una manera simple de hacerlo sería:
c:
cd \windows
for %f int (*.*) do @echo %f
Pero esto no es lo que se solicita, sino que, con una sola línea for y sin estar posicionado en la carpeta de
Windows, se obtenga el mismo resultado que con las tres instrucciones anteriores.
comando
Requerido. Especifica el comando que desea ejecutar en cada archivo, directorio, intervalo de valores o
cadena de texto incluido en el (grupo) especificado.
NOTA: Hay que prestar especial atención al posible uso del %variable dentro de la acción 'do' así como
al posible uso de variables dentro de él. Recordemos la parte vista anteriormente de expansión diferida o
no del comando a la hora de traducir la línea.
NOTA: Cuando están habilitadas las extensiones de comandos (es el valor por defecto del cmd.exe en
Windows) -ver capítulos anteriores- es posible también las sintaxis que veremos a continuación
Ejercicio [for-2]: Se desea obtener en un fichero llamado c:\datos.txt todos los archivos, un delimitador
como puede ser “;” y el nombre de la carpeta en la cual residan. Es decir un archivo de texto con el
formato:
notepad.exe;c:\Windows
... etc...
30
Ejercicio [for-3]: Obtener los mismos datos que en el ejercicio anterior pero recibiendo la carpeta origen
por parámetro.
/L -> Iteración de un intervalo de valores. Se utiliza una variable iterativa para establecer el valor inicial
(númeroInicial) y, después, recorrer un intervalo de valores especificado hasta que el valor sobrepase el
valor final (númeroFinal) especificado. /L ejecutará la iteración mediante la comparación de
númeroInicial con númeroFinal. Si númeroInicial es menor que númeroFinal, el comando se ejecutará.
Si la variable iterativa sobrepasa el valor de númeroFinal, el shell de comandos sale del bucle. También
se puede utilizar un valor númeroPaso negativo para recorrer un intervalo de valores decrecientes. Por
ejemplo, (1,1,5) genera la secuencia 1 2 3 4 5 y (5,-1,1) genera la secuencia (5 4 3 2 1)
Ejercicio [for-4]: Dar un barrido de ping a las primeras 30 ip's desde 172.16.0.1
5) Interacción y análisis de archivos. Es la parte más potente del FOR ya que permite ejecución interna
de comandos dentro del propio "do" -realiza o puede realizar "spawn" del posible comando dentro del
do-
Lo veremos después de entender la solución a los ejercicios.
[for-1]
Revisar el capítulo de "parámetros" para ver los prefijos ~n o bien ~x que pueden ponerse a parámetros
(o bien a variables explícitas o implícitas en un for -son equivalentes a parámetros-) para entender el
comando anterior.
[for-2]
@echo off
setlocal enabledelayedexpansion
set ruta=
del c:\datos.txt 2>&1 >nul
for /r c:\windows %%a in (.) do (
set ruta=%%a&@for %%f in ("!ruta:\.=!\*.*") do echo %%~nxf;!ruta:\.=! >>c:\datos.txt
)
endlocal
[for-3]
31
Vamos a realizarlo en un script.
@echo off
setlocal enabledelayedexpansion
if "%*" EQU "" goto error0
pushd
cd "%*" >nul
if errorlevel 1 goto error1
set ruta=
del c:\datos.txt 2>&1 >nul
for /r "%*" %%a in (.) do (
set ruta=%%a&set r=!ruta:\.=!&@for %%f in ("!r!\*.*") do echo %%~nxf;!r! >>c:\datos.txt
)
goto FIN
:error0
echo Falta parametro.
goto FIN
:error1
echo Carpeta recibida por parametro inexistente
goto FIN
:FIN
endlocal
popd
Razonar el script y cada detalle del uso del parámetro así como los controles realizados.
[for-4]
--
Hemos dejado aparte la interacción y análisis de archivos debido a que es la más compleja y a la vez la
que más potencia da a los scripts y que usaremos en muchos de nuestros procedimientos.
Es la parte más potente del FOR ya que permite ejecución interna de comandos dentro del propio "do"
-realiza o puede realizar "spawn" del posible comando dentro del in-. Vamos a ver la definición formal y
la sintaxis, pero sobre todo, a continuación, vamos a insistir en la comprensión real de su ejecución
mediante ejemplos.
Aconsejo una lectura rápida hasta la parte de "Ejemplos y comprensión del comando".
Definición formal:
32
Después, se llama al bucle for con la variable de iteración establecida al valor del testigo. De forma
predeterminada, /F pasa el primer testigo separado por espacios en blanco de cada línea de cada archivo.
Las líneas en blanco se omiten. Las distintas sintaxis son:
Sintaxis:
El argumento grupoNombreArchivos especifica uno o varios nombres de archivo. Cada archivo se abre,
lee y procesa antes de pasar al siguiente archivo en grupoNombreArchivos. Para suplantar el
comportamiento predeterminado del análisis, especifique "palabrasClaveDeAnálisis". Se trata de una
cadena incluida entre comillas que contiene una o varias palabras clave que especifican diferentes
opciones de análisis.
En la tabla siguiente se enumeran las palabras clave de análisis que se pueden utilizar para
palabrasClaveDeAnálisis.
skip=n Especifica el número de líneas que se omitirán al principio del archivo.
tokens=x,y,m-n Especifica los testigos de cada línea que se pasarán al cuerpo de for en cada
iteración. Como consecuencia, se asignarán nombres de variable adicionales. La forma m-n es un
intervalo que especifica los testigos m hasta n. Si el último carácter de la cadena tokens= es un asterisco
(*), se asigna una variable adicional que contendrá el texto que queda en la línea después del último
testigo analizado.
usebackq Especifica que se pueden utilizar comillas para incluir los nombres de los archivos de
grupoNombreArchivos, que una cadena incluida entre comillas se ejecutará como si fuera un comando y
que una cadena escrita entre comillas simples es un comando de cadena literal.
33
Reemplazo de variables:
Se han mejorado los modificadores de reemplazo de las referencias a variables de for. La tabla siguiente
muestra la sintaxis opcional (para cualquier variable I):
%~I Expande %I que elimina las comillas ("") delimitadoras.
%~fI Expande %I a un nombre de ruta de acceso completo.
%~dI Expande %I a una letra de unidad solamente.
%~pI Expande %I a una ruta de acceso solamente.
%~nI Expande %I a un nombre de archivo solamente.
%~xI Expande %I a una extensión de archivo solamente.
%~sI Expande la ruta de acceso para que solamente contenga nombres cortos.
%~aI Expande %I a los atributos del archivo.
%~tI Expande %I a la fecha y hora del archivo.
%~zI Expande %I al tamaño del archivo.
%~$PATH:I Busca los directorios enumerados en la variable de entorno PATH y expande
%I al nombre completo del primero encontrado. Si el nombre de la variable de entorno no está definido
o la búsqueda no encuentra el archivo, este modificador se expande a la cadena vacía.
En la tabla siguiente se enumeran las combinaciones de modificadores que se pueden utilizar para
obtener resultados compuestos.
En los ejemplos anteriores se puede reemplazar %I y PATH por otros valores válidos. Los nombres de
variable válidos de for terminan con la sintaxis %~.
Si utiliza nombres de variables en mayúsculas como %I, el código resultará más legible y evitará
confusiones con los modificadores, que no distinguen entre mayúsculas y minúsculas.
34
Hasta el momento, únicamente he copiado la definición formal que figura en la propia ayuda de
Windows. Voy a darle ahora una vuelta e intentar explicarlo bajo dos puntos de vista que son los básicos
de cara a la ejecución de un script:
Leer el contenido de un fichero, línea por línea, es tan sencillo como usar:
El comando anterior leerá todos los ficheros enumerados en la cláusula 'in' del directorio en curso y el
contenido de ellos, línea por línea lo pasará dentro de la variable i al do de ejecución.
Recordemos que en un script el %i debe sustituirse por %%i. Nótese igualmente que esta sintaxis del for
no admite caracteres "comodín" en los nombres de archivo, es decir, no podemos referenciarlos por
*.abc o bien por ???.abc, sino que el nombre debe estar totalmente cualificado.
¿Cómo podríamos entonces hacer para ver el contenido de todos los archivos con extensión abc? Bien,
usando un poco la imaginación y sabiendo que el for normal (sin /f) es capaz de enumerar los archivos
en un directorio, podremos endentar dos for para hacerlo.
(recordemos igualmente que las @ son para no ver la expansión del comando. Cuando estemos
depurando una instrucción lo normal es no ponerlas, para poder seguir lo que está haciendo).
Evidentemente este es un ejemplo poco agraciado, ya que mediante el comando type podríamos obtener
resultados similares, pero viéndolo con un for nos ayuda a comprender el propio for.
Compliquémoslo un poco. Queremos el resultado anterior pero ignorando la primera línea de cada
archivo.
Sigamos complicándolo un poco más con un ejemplo real: imaginemos que somos administradores de
una red y queremos que no se use el emule (o cualquier otro programa) en nuestra red. Una manera
sencilla sin entrar a modificar las políticas, seria tener un script en nuestra máquina que periódicamente
"matase" los procesos que queramos en las máquinas remotas.
La primera idea es que tenemos que tener un fichero con la lista de las máquinas en las cuales queremos
controlar los procesos. Además, deberíamos tener otro dichero con la lista de programas que queremos
"matar". De esta manera, simplemente añadiendo en uno de los ficheros una nueva máquina o en el
segundo fichero un nuevo programa, podremos tener siempre planificado un script único que
periódicamente analice en las máquinas especificadas en el fichero uno y mate los procesos
especificados en el fichero dos.
Para ello, creamos un fichero uno con el nombre de las máquinas. Llamemos a ese fichero
"máquinas.txt" en el cual escribiremos los nombre o direcciones IP de las máquinas:
35
\\KA0001
\\192.168.0.10
Igualmente crearemos un fichero dos, con el nombre de programas que queremos "matar". Sea este
fichero: programas.txt
emule.exe
bittorrent.exe
Debido a que vamos a acceder a máquinas remotas, se necesita un usuario / password administrador en
las máquinas remotas. Deseamos que el script reciba por parámetro el usuario y nos solicite por pantalla
la password de ese administrador.
Igualmente recordemos un par de comandos de XP Profesional o bien de W2003. Para ver los
programas que están en ejecución en una máquina, simplemente teclear en una consola de comandos
tasklist. Esto nos muestra la lista de procesos y su PID (número único que identifica un proceso).
Recordemos también que el comando taskkill es capaz de matar un proceso pasándole como parámetro
el PID del programa a "matar". Podemos verlo ejecutando en una consola taskkill /?
@echo off
setlocal enabledelayedexpansion
set user=%1
set /p pass="password del usuario %1 : "
cls
endlocal
goto :EOF
:proc00
for /f "skip=2 tokens=1,2,* delims= " %%g in ('tasklist /S %1 /U %user% /P %pass%') do (
call :proc01 %1 %%g %%h
)
goto :EOF
:proc01
for /f %%i in (programas.txt) do if {%2} EQU {%%i} taskkill /S %1 /U %user% /P %pass% /PID %3
goto:EOF
Podemos modificar el script para no solicitar la password y recibirla también por un segundo parámetro
y ponerlo en el planificador de tareas para que se ejecute por ejemplo cada 5 minutos. Las otras
máquinas no se enterarán de nada... pero los programas p2p se le "morirán" cada 5 minutos
misteriosamente. (Este ejemplo es valido sin cortafuegos. Con cortafuegos deberemos tener abiertos el
puerto netbios-ssn que es el que usan los comandos tasklist y taskkill).
36
En www.sysinternals.com existe una utilidad llamada pskill, pslist las cuales también funcionan en XP
Home (que no posee los comandos anteriores). Como ejercicio, os podéis bajar dichas utilidades,
ejecutarlas en consola para ver cómo son dichas salidas y preparar un script similar con ellas.
Fijémonos en la comilla simple que delimita al comando tasklist y sus parámetros. Cuando dentro de un
for /f en la parte del in, especificamos algo encerrado entre comillas simples lo que estamos haciendo es
ordenando que se ejecute ese comando en un cmd aparte y que cuando finalice, la salida que nos
mostraría en pantalla (STDIN) de la pase al for directamente. Esto lo veremos en detalle en la parte 2)
de este capítulo.
Por tanto es como si se ejecutase el tasklist y lo que se vería por pantalla se le pasa al for. Evidentemente
toda esa salida (probarla por pantalla) debe filtrarse para separar los campos que necesitamos, ya que
primero: nos molestan las líneas de cabecera y segundo: en las líneas de detalle tenemos que separar lo
que es el nombre del programa de lo que es el PID y el resto de datos (que nos sobran en este caso).
Es decir, si ejecutamos tasklist en nuestra máquina veremos algo así:
b) En tokens, le estamos indicando que vamos a coger el primer campo (que lo pondrá en %%g), el
segundo, (que lo pondrá en %%h) y el resto (que lo pondrá en %%i). Fijémonos que sólo hemos
definido %%g en la línea, pero al especificar más campos, usará las letras alfabéticamente a partir de %
%g como nombres de variables.
c) en delims= (nótese un espacio en blanco al final), le estamos diciendo que considere delimitadores
los caracteres que van a continuación del símbolo igual, por tanto en nuestro caso le estamos diciendo
que sólo hay un delimitador y que este delimitador son los espacios en blanco.
for /f "skip=2 tokens=1,2,* delims= " %%g in ('tasklist /S %1 /U %user% /P %pass%') do...
Está recuperando en %%g el nombre de cada proceso y en %%h el PID de dicho proceso.
37
Creo que con este pequeño resumen puede comprenderse perfectamente la potencia del for. Analizar
despacio ahora el resto del script.
Hemos introducido anteriormente en el ejemplo de la parte 1) que dentro del "in" puede ir
entrecomillado simple el nombre de un programa o comando y que el sistema lo ejecutará. Vamos a ver
cómo se realiza esto y cómo se recuperan los datos:
Lo existente entrecomillado simple dentro de un in en un comando for /f se ejecutará abriendo (en forma
oculta) otro "cmd" el cual ejecutará el comando. La salida a consola (STDOUT) de ese comando cuando
finalice, será la que se pase línea por línea al for invocante.
@echo off
dir
pause
Esto sacaría un "dir" y esperaría hasta que pulsemos una tecla. En vez de ejecutarlo directamente vamos
a ejecutarlo desde dentro de un for
Las variables de entorno son variables de caracteres (strings) por tanto, una asignación del tipo:
set i=1+2
38
contiene el string "1+2" no contiene un "3". Puede comprobarse ejecutando:
echo %i%
Si queremos que el resultado, sea el resultado de una operación matemática, deberemos hacer:
set /a i=1+2
set i=1+2+3
echo %i% nos devolverá "1+2+3"
set /a j=(%i%)*2 nos devolverá el resultado de (1+2+3)*2 = 12
set variable=+ABC+123
imaginemos que queremos cambiar TODOS los signos + por las letras "kf". En ese caso hacemos a
continuación:
set variable=%variable:+=kf%
En particular, nos puede servir para eliminar los caracteres de puntuación de una variable
sustituyéndolas por nulos. Es decir:
set variable=1.234.567
si hacemos ahora:
set variable=%variable:.=%
su contenido quedará como "1234567" por tanto, numérico puro y con el /a del punto 1) anterior
podríamos realizar operaciones matemáticas
Sea:
echo %variable:~,5%
39
podríamos asignárselo también a otra variable de la forma:
set nueva_var=%variable:~,5%
4) Cómo leer todo el contenido de un fichero en un script y poder procesar línea por
línea.
-----------------------------------------------------------------------------------------------------------------------------
---------------
posteriormente en la rutina :proceso de nuestro scrip deberemos eliminar las comillas que hemos
introducido a propósito para que no separen en varios parámetros y de esta manera recibir uno solo.
Puede hacerse con el tip 2) anterior.
NOTA: Los TIPS 2 y 3 no los he encontrado documentados en ningún sitio. Únicamente investigando
en otros scripts que han llegado a mi poder he deducido el cómo funcionan. Agradecería que si alguien
tiene información particular sobre el tratamiento de substrings de ese tipo, me lo comentase.
Respecto a la NOTA:
1.- El tema de los TIPS 2 y 3 se encuentra en un libro "buenísimo" de MacMillan llamado "Windows
NT Shell Scripting"
2.- Si tienes la licencia perpétua de http://www.jsiinc.com, al abrir la versión Offline existen 4-5 trucos
SÓLO para suscriptores donde se comentan precisamente esos trucos...
Nos creamos un archivo .reg, por ejemplo, con el nombre: ClrLogOn.reg con el contenido:
REGEDIT4
[HKEY_USERS\.DEFAULT\Control Panel\Desktop]
"FontSmoothing"="2"
40
"FontSmoothingOrientation"=dword:00000001
"FontSmoothingType"=dword:00000002
Una vez creado y salvado, lo pinchamos por una única vez para que se incorpore al registro, y ya está.
Puede ser de utilidad en caso de necesitar apagar una máquina y que existan usuarios con recursos
abiertos en ella que pueden dañarse ante un cierre no esperado.
Podéis bajaros de aquí el archivo open.cmd (debe ejecutarse en una consola de comandos -cmd.exe-)
que realiza dicha función.
@echo off
setlocal enabledelayedexpansion
endlocal
goto :EOF
Podemos hacer, por ejemplo, que un suceso en una máquina remota dispare una acción en nuestra
máquina. La que queramos. Esto viene perfecto para controlar una red de servidores en producción e
incluso para sincronizar procesos y tareas batch entre ellas.
En principio, esto es válido únicamente para Windows XP Profesional y superiores (W2003).
Vamos a ver primero un ejemplo sencillito: supongamos que queremos crear un fichero log en donde se
vaya guardando qué día y a qué hora, alguien ha hecho logon en nuestra máquina.
Existe un comando en XP Profesional, el comando eventtriggers, el cual nos permite lanzar un programa
o realizar una tarea automáticamente cuando ocurre algún suceso predeterminado que implica la
grabación de ese suceso en el Visor de Sucesos (en otro TIP posterior veremos cómo podemos, además,
crearnos sucesos "nuestros" predefinidos).
41
Igualmente supongo que, para este ejemplo, tenemos definida la política (en gpedit.msc) para que no
audite las conexiones de usuario -es decir, para que grabe un suceso cada vez que un usuario se conecte.
Asociado el suceso, lo que vamos a hacer es lanzar un script (un .bat) llamado logon-s.bat. Nuestro
deseo es que nos vaya grabando las conexiones de usuario en un archivo llamado conex.log y situado en
una carpeta en c:\logs .
Para ello, creamos un .BAT en c:\logs llamado logon-s.bat con el siguiente contenido:
@echo off
setlocal
for /f "tokens=1 delims= " %%u in ('date /t') do set $d1=%%u
for /f "tokens=1 delims= " %%u in ('time /t') do set $t1=%%u
echo "%$d1% %$t1% Conexión usuario" >> c:\logs\conex.log
endlocal
De esta manera ya tenemos una batería que, si se ejecuta, grabará una línea a continuación de la última
que exista en el fichero conex.log en esa carpeta, con el día, la hora y un texto que dice "Conexión
usuario". Ahora, para provocar el que se ejecute cada vez que alguien se conecta, simplemente,
ejecutamos en una ventana de comandos:
De esta manera, hemos creado un disparador que ejecuta la batería logon.s.bat cada vez que en el visor
de sucesos, en la parte de seguridad, se escriba un suceso con ID 680 (corresponde, tal y como podéis
comprobar viendo uno cualquiera de los mensajes, a la conexión con éxito de un usuario).
En cualquier momento, ejecutando eventtrigers sin parámetros, nos dará una lista de los triggers
(disparadores) definidos en nuestra máquina. En nuestro caso:
eventtriggers[.exe] /create [/s Computer [/u Domain\User [/p Password]]] /tr TriggerName [/l
[APPLICATION] [SYSTEM] [SECURITY] ["DNS Server"] [LOG] [DirectoryLogName] [*] ] {[/eid
ID]|[/t {ERROR|INFORMATION|WARNING|SUCCESSAUDIT|FAILUREAUDIT}]|[/so Source]} [/d
Description] /tk TaskName [/ru {[Domain\]User | "System"} [/rp Password]]
42
eventtriggers[.exe] /delete [/s Computer [/u Domain\User [/p Password]]] /tid {ID|*}
eventtriggers[.exe] /query [/s Computer [/u Domain\User [/p Password]]] [/fo {TABLE|LIST|CSV}]
[/nh] [/v]
Este TIP se me ha ocurrido viendo la pregunta de un compañero que quería matar ciertos procesos a una
determinada hora mediante el planificador de tareas.
La sintaxis es:
de esta manera, al ejecutarlo matará los programas cuyo nombre estén a continuacion del kill.
NOTA: El programa debe darse sin extensión. Por ejemplo, imaginemos que tenemos abierto el Notepad
y el Word y queremos matarlos. Se haría:
@echo off
setlocal ENABLEEXTENSIONS & set "i=0.0.0.0" & set "j="
for /f "tokens=4" %%a in ('route print^|findstr 0.0.0.0.*0.0.0.0') do (
if not defined j for %%b in (%%a) do set "i=%%b" & set "j=1")
endlocal & echo %i% & pause & goto :EOF
43
que se nos vaya el dedo y borremos una clave no deseada.
Vamos a ver 2 métodos de introducir claves en el registro y un método de borrar las claves:
PONER CLAVES
-------------------------
Metodo 1)
=======
Creamos un fichero .REG con una sintaxis predeterminada. Lo mejor es ver un ejemplo de una clave,
llamada JMT colgando de:
HKEY_LOCAL_MACHINE
Software
y aunque a su vez tiene los distintos posibles valores del registro, así como dos subclaves. Una con
"algo" de contenido y la otra totalmente vacía.
REGEDIT4
[HKEY_LOCAL_MACHINE\Software\JMT]
@="Con texto en el Default"
"CadenaCaracteres"="c:\\JMT\\jmt.txt"
"ValorBinario"=hex:00,01,02
"DoblePalabra"=dword:00001234
[HKEY_LOCAL_MACHINE\Software\JMT\Subclave]
@="Solo con el Defecto"
[HKEY_LOCAL_MACHINE\Software\JMT\SubclaveVacia]
Una vez creado el fichero, podéis ejecutarlo con el botón derecho del ratón, y luego "Combinar" (o
"Merge" si lo tenéis en Inglés).
* Es necesario hacer notar, que si tenemos que escribir la "\" en el fichero, debemos poner una doble
"\\". Igualmente si queremos escribir una comila ("), debemos poner una doble comilla ("").
Metodo 2)
=======
Creando un .INF. Aquí varía un poco la sintaxis. Además la ejecución, se hace igualmente con el botón
derecho, pero ahora hay que darle a "instalar".
[version]
44
signature="$CHICAGO$"
[DefaultInstall]
AddReg=Enable.ClaveJMT
[Enable.ClaveJMT]
HKLM,Software\JMT,,0,"Con Texto en el Default"
HKLM,Software\JMT,"CadenaCaracteres",0,"c:\JMT\jmt.txt"
HKLM,Software\JMT,"ValorBinario",1,00,01,02
HKLM,Software\JMT\SubclaveVacia,,0,""
* Fijarse que en este caso, no es necesario la doble contrabarra "\\". Aquí lo que pongamos se "graba" al
pie de la letra.
*** En ambos métodos, si insertamos una clave o valor que ya existe, lo que estamos realmente
haciendo es modificar su contenido.
ELIMINAR CLAVES
------------------------------
En este caso, es necesario hacerlo con un .INF (no sirve en este caso un .REG)
[version]
signature="$CHICAGO$"
[DefaultInstall]
DelReg = Del_JMT.Reg
[Del_JMT.Reg]
HKLM,Software\JMT,ValorBinario,,
Con lo anterior, eliminamos el campo "ValorBinario". Si quisiésemos eliminar TODA la clave JMT,
sería:
[Del_JMT.Reg]
HKLM,Software\JMT,,,
45
SHADOW COPY en Windows XP
-------------------------
Utilidad: por ejemplo, copiar ficheros que están en uso por otra aplicación. Con este ejemplo, podemos
copiarnos la carpeta de correo: outlook.pst por ejemplo, aunque esté abierto el correo. Su uso no se
limita únicamente al correo: es posible copiar cualquier archivo o carpeta completa aunque esté en uso.
Shadow Copy es un servicio nativo de W2003 cuya utilidad está dirigida a proteger carpetas
compartidas que están en uso por usuarios, y ante posibles errores de usuarios realiza copias periódicas
al objeto de no perder datos por manipulaciones incorrectas, como por ejemplo borrados accidentales.
XP también es capaz de usarlo indirectamente con NTBackup, pero en principio está restringido sólo a
un servicio y por desgracia, solo para que lo use NTBackup.
Una vez copiado, el resto del SDK no nos hace falta ya que son herramientas para desarrolladores.
-----------------------------------------
setlocal
set SOURCE_DRIVE_LETTER=%~d1
set SOURCE_RELATIVE_PATH=%~pnx1
set DESTINATION_PATH=%2
set CALLBACK_SCRIPT=%~s0
set TEMP_GENERATED_SCRIPT=GeneratedVarsTempScript.cmd
@goto :EOF
:IS_CALLBACK
setlocal
call %TEMP_GENERATED_SCRIPT%
siendo "origen" un archivo o carpeta y el mismo criterio para "destino". Si el origen o destino tuviese
caracteres especiales, como espacios en blanco, deberán encerrarse entre comillas. Por ejemplo:
en este caso, copiará todo el contenido de "carpeta origen 1" que cuelga de la carpeta en donde estemos
situados en ese momento a la carpeta "c:\pruebas de copia"
NOTAS:
------
El "origen" puede ser:
1) Un nombre de fichero, y en este caso el destino debe ser una carpeta en donde vayamos a dejarlo, o
bien una carpeta y el nombre del archivo. El nombre "origen" debe estar totalmente cualificado, o
supondrá que está en la carpeta actual.
2) Una carpeta. En este caso el destino debe ser otra carpeta y debe existir. Si no existiese, lo creará
como un único archivo y meterá dentro de él, todos los archivos origen uno a continuación del otro.
PROBLEMAS CONOCIDOS:
--------------------
La letra de unidad Z: debe estar libre en nuestra máquina y no mapeada a ningún recurso local o de red.
--------------------------------------------------------------------------------
http://blogs.msdn.com/adioltean/archive/2005/01/20/357836.aspx
http://technet.microsoft.com/en-us/library/cc780410(WS.10).aspx
http://www.petri.co.il/what's_shadow_copy_on_windows_server_2003.htm
47
y, fundamentalmente, a un usuario de los grupos de noticias de Microsoft: "jaime" que al hacer una
pregunta sobre las copias de ficheros ocultos... me ha dado la idea de resumir los artículos anteriores, en
castellano, y de una manera simple.
Existen algunas utilidades freeware que lo dan. Pero ¿alguna ocupa sólo 89 bytes?..... :-)
@for %%e in (%PATHEXT%) do @for %%i in (%1%%e) do @if {%%~$PATH:i} NEQ {} echo %
%~$PATH:i
findpath programa
por ejemplo:
findpath calc
@echo off
setlocal ENABLEEXTENSIONS
call :dias %1
set /a d=j
call :dias %2
set /a d=j-d
echo %d%&endlocal&goto :EOF
:dias
for /f "tokens=1,2,3 delims=-/." %%a in ("%1") do (
set dd=%%a&set mm=%%b&set yy=%%c)
set /a dd=100%dd%%%100,mm=100%mm%%%100
set /a z=14-mm,z/=12,y=yy+4800-z,m=mm+12*z-3,j=153*m+2
set /a j=j/5+dd+y*365+y/4-y/100+y/400-2472633
goto :EOF
48
Tanto fecha1 como fecha 2, admiten los formatos: dd.mm.aaaa dd-mm-aaaa dd/mm/aaaa
@echo off
setlocal ENABLEEXTENSIONS
call :dias %1
set /a j+=%2
call :inc %j%
echo %dd%/%mm%/%yy%
endlocal
goto :EOF
:dias
for /f "tokens=1,2,3 delims=-/." %%a in ("%1") do (
set dd=%%a&set mm=%%b&set yy=%%c)
set /a dd=100%dd%%%100,mm=100%mm%%%100
set /a z=14-mm,z/=12,y=yy+4800-z,m=mm+12*z-3,j=153*m+2
set /a j=j/5+dd+y*365+y/4-y/100+y/400-2472633
goto :EOF
:inc
set /a a=%1+2472632,b=4*a+3,b/=146097,c=-b*146097,c/=4,c+=a
set /a d=4*c+3,d/=1461,e=-1461*d,e/=4,e+=c,m=5*e+2,m/=153,dd=153*m+2,dd/=5
set /a dd=-dd+e+1,mm=-m/10,mm*=12,mm+=m+3,yy=b*100+d-4800+m/10
(if %mm% LSS 10 set mm=0%mm%)&(if %dd% LSS 10 set dd=0%dd%)
goto :EOF
El resultado sumar los "ndias" tecleados a la fecha. (admite signo para restar dias)
49
Este TIP lo he preparado a partir de la pregunta de un usuario (administrador en su red) en días pasados,
de cómo controlar y matar por red los programas P2P.
Únicamente se necesita estar definidio como usuario Administrador en las máquinas remotas.
1) Fichero de nombre: pgms.txt en donde pondremos los nombres de los programas que queremos matar
en las máquinas remotas. Por ejemplo, crearlo con:
notepad.exe
calc.exe
emule.exe
2) Fichero de nombre: máquinas.txt en donde pondremos los nombres o direcciones IP de las máquinas
en que queremos matar esos procesos. Por ejemplo:
\\KA0001
\\192.168.0.10
3) Una batería de comandos, podéis crearosla con el nombre kill_process.cmd y copiais íntegramente
este contenido:
setlocal enabledelayedexpansion
set user=%1
set /p pass="password del usuario %1 : "
cls
endlocal
goto :EOF
:proc00
for /f "skip=2 tokens=1,2,* delims= " %%g in ('tasklist /S %1 /U %user% /P %pass%') do call :proc01
%1 %%g %%h
goto :EOF
:proc01
for /f %%i in (pgms.txt) do if {%2} EQU {%%i} taskkill /S %1 /U %user% /P %pass% /PID %3
goto:EOF
--------------- hasta aquí sin esta línea---------------
(fijaos, que cada "for" debe estar en una linea, y que el goto es la siguiente linea. Por tanto, cuidado con
el cortar y pegar y respetad exactamente los espacios en blanco).
NOTA: La manera de usarlo sería (en una ventana de comandos -cmd.exe-, en la carpeta en donde
tengamos los tres ficheros):
50
kill_process administrador
siendo "administrador" el nombre de un usuario administrador en las máquinas remotas. Este comando
pedirá por pantalla la password de dicho usuario y a continuación matará los procesos.
Si en la máquina vamos cambiando de hardware, quedarán instalado viejos drivers y controladores que
de vez en cuando es conveniente borrar del registro. La pega es que los dispositivos viejos ya no
podemos verlos en el Administrador de dispositivos y por tanto no podemos desinstalarlos.
Una manera de hacer que aparezcan y por tanto poder borrarlos, es crearse, con el Block de Notas u otro
editor, un archivo bat, llamado por ejemplo: ShowDevices.bat con el contenido:
@echo off
set devmgr_show_nonpresent_devices=1
cd %SystemRoot%\system32
start devmgmt.msc
Si ejecutamos dicho archivo, y en el menú seleccionamos el Ver dispositivos "ocultos", nos aparecerán
(con un color más suave y difuminado) los dispositivos que teníamos y que ya no están instalados. Con
el botón derecho podremos desinstalarlos.
Bien, en la primera de ellas, el grupo de comandos se ejecuta si el comando anterior termina con un
errorlevel distinto de cero.
La segunda, se ejecuta el grupo de sentencia si el comando termina con errorlevel 0.
Veamos un ejemplo:
md; es una instrucción imposible, por tanto sacará un error. Direccionamos el error a nul (2>nul) para
no ver nada por pantalla. Pero como el comando termina con errorlevel distinto de cero, se ejecuta el
echo y nos mostrará "hay un error".
Sirve igualmente para analizar errores en cualquier instrucción que devuelva un código de retorno,
incluídas llamadas a procedimientos, por ejemplo:
51
TIP: Un pequeña optimización en XP
Bien, el defragmentador de XP es capaz (al contrario que el de W2000, y muy similar al de W98 y ME),
de "saber" cuales son los ficheros más utilizados ya que lleva una estadística de uso. El defragmentador,
siempre intentará colocar estos archivos lo más cercano a la FAT (si es un sistema FAT) o al MFT (si es
un sistema NTFS) al objeto de optimizar su tiempo de carga.
El problema suele surgir porque los ficheros más utilizados suelen estar precisamente "abiertos". Y los
ficheros abiertos, dependiendo de cómo hayan sido abiertos, no se pueden mover ya que precisamente,
si son de código, el propio fichero hace de archivo de paginación para ese código.
La pregunta es ¿cómo podemos entonces optimizar?. Bien, evidentemente, la optimización será mayor
cuantos menos archivos estén "pillados". Curiosamente, justo en el "shutdown" de la maquina, es
cuando la mayoría de los procesos han sido liberados. Por ello, la pregunta ahora es ¿podemos ejecutar
algo en el shutdown?.
Pues sí. Si ejecutamos gpedit.msc vemos que en "Computer Configuration" -> "Windows Settings" ->
Scripts (Startup/Shutdown) podemos incluir un script, por ejemplo, de Visual Basic. En particular, si en
el shutdown, le ponemos el nombre de un fichero, llamémosle por ejemplo, defrag.vbs, cuyo contenido
sea:
--defrag.vbs--
Set shell=createobject("WScript.Shell")
shell.run "defrag c:", 2, false
shell.run "defrag d:", 2, false
--cut---
Nos defragmentará, al cerrar windows, el disco C: y el D: (suprimid o añadid las lineas que queráis).
Evidentemente, esta defragmentacion llevará tiempo. Pero será bueno ejecutarla una vez al mes, por
ejemplo, para que las estadísticas de uso de programas tengan ya contenido válido para la
defragmentación.
Un saludo,
En algunas ocasiones puede ser necesario ocultar o remover el botón de inicio en una sesión de usuario,
sobre todo si queremos que un determinado usuario entre en Windows a una sola aplicación.
En ese caso, lo mejor es quitarle el botón de Inicio y establecer las políticas para que no pueda arrancar
el administrador de tareas. De esta manera quedará limitado a la ejecución de la aplicación que
queramos.
http://www.scriptlogic.com/products/scriptingtoolkit/
52
La sintaxis es:
Ejecutado desde el grupo de Inicio, podemos hacer que desaparezca por tanto el botón de Inicio.
Si además queremos que el usuario no pueda hacer determinadas tareas (usar el administrador de tareas,
por ejemplo, mediante CTRL+ALT+DEL), podemos limitárselo mediante una simple política
estableciéndola mediante el editor de políticas: gpedit.msc.
En este ultimo caso hay que prestar especial atención para que la política se aplique a los usuarios
únicamente y no a los Administradores de la máquina. Para ello, leed con atención este artículo:
CÓMO: Aplicar directivas locales a todos los usuarios, excepto los administradores
http://support.microsoft.com/default.aspx?scid=kb;es;293655
53