En la primera entrega mandamos la salida de ver a un fichero con > y la leímos desde VFP. Aquí va lo que hay detrás: por dónde escribe y lee un programa de consola, cómo se desvía cada cosa, y cómo le dice un programa a quien lo lanzó si ha ido bien. Con esto ya puedes encadenar programas y usarlos en un .bat sin mirar la pantalla.
Tres canales
Todo programa de consola arranca con tres canales abiertos:
| Canal | Nombre en inglés | Para qué |
|---|---|---|
| Entrada | stdin | lo que el programa lee (el teclado, si no dices otra cosa) |
| Salida | stdout | el resultado |
| Error | stderr | los avisos y los errores |
La salida y el error van a la misma pantalla, así que a simple vista no se distinguen. Son dos canales distintos, y se pueden mandar a sitios distintos.
VFP tiene algo parecido para la salida. Lo que escribes con ? va a la pantalla, y con SET ALTERNATE lo mandas también a un fichero:
SET ALTERNATE TO eco.txt
SET ALTERNATE ON
? "Facturas pendientes:", 12
SET ALTERNATE OFF
SET ALTERNATE TOEn eco.txt queda Facturas pendientes: 12. Lo que VFP no tiene es un canal aparte para los errores: un error de VFP abre una ventana o salta a tu ON ERROR.
La salida, a un fichero
> manda la salida a un fichero en lugar de a la pantalla. Si el fichero existe, lo sustituye:
ping -n 1 127.0.0.1 > salida.txtEn pantalla no sale nada, y salida.txt tiene las 9 líneas que habrías visto. Con >> se añade al final en lugar de sustituir: otro ping -n 1 127.0.0.1 >> salida.txt y el fichero pasa a 18 líneas.
Cuando la salida no te interesa, la mandas a nul, que es un fichero especial de Windows que se lo traga todo:
ping -n 1 127.0.0.1 > nulEl error va por otro lado
Prueba a mandar a un fichero la salida de un comando de git que no existe:
git estado > resultado.txtgit: 'estado' is not a git command. See 'git --help'.El mensaje sale en pantalla igualmente, y resultado.txt se queda vacío, con 0 bytes. git ha escrito el mensaje por el canal de error, y > solo desvía la salida. Para el error se pone un 2 delante:
git estado 2> errores.txtAhora la pantalla queda limpia y el mensaje está en errores.txt. Si quieres las dos cosas en el mismo fichero, 2>&1 manda el error a donde vaya la salida:
git estado > todo.txt 2>&1Tener dos canales sirve para esto: guardas el resultado en un fichero y los problemas siguen apareciendo en la pantalla, o en otro fichero, sin mezclarse con los datos. Si un programa escribe un JSON por la salida y sus avisos por el error, el JSON sale limpio.
No todos los programas lo respetan. ping escribe sus propios errores por la salida normal: ping -n 127.0.0.1 > p.txt no enseña nada, y el «Debe especificar la dirección IP.» acaba dentro de p.txt. Si un error no aparece donde esperas, mira también en el fichero de la salida.
La entrada, desde un fichero
< hace lo contrario que >: el programa lee de un fichero en lugar del teclado. sort ordena las líneas que le llegan por la entrada:
sort < ciudades.txtAlmeria
Madrid
ZaragozaSi escribes sort sin más, se queda esperando a que teclees líneas, porque su entrada es el teclado. Muchos programas de consola funcionan así: leen su entrada, la procesan y escriben el resultado.
La tubería: la salida de uno, a la entrada de otro
La barra vertical | conecta dos programas: lo que el primero escribe por su salida le llega al segundo por su entrada, sin pasar por ningún fichero. tasklist lista los procesos que hay en marcha, y find se queda con las líneas que contienen un texto. Con VFP abierto:
tasklist | find /i "vfp9.exe"vfp9.exe 6808 Console 1 30.176 KBOtra: find /c /v "" cuenta las líneas que le llegan. Pegado detrás de un dir /b, cuenta lo que hay en una carpeta:
dir /b C:\Windows | find /c /v ""102Ninguno de los dos programas sabe que el otro existe. Cada uno hace su parte, y la tubería los une. Es la forma de trabajar de la consola: muchos programas pequeños que se combinan.
El código de salida
Cuando un programa de consola termina, le devuelve un número a quien lo lanzó. Es el código de salida (en inglés, exit code), y la regla es sencilla: 0 es que ha ido bien, cualquier otro número es que no. Qué significa cada número distinto de 0 lo decide cada programa.
En cmd, el último código queda en %ERRORLEVEL%:
ping -n 1 127.0.0.1 > nul
echo %ERRORLEVEL%Estos son los que devuelven las órdenes que hemos usado en el curso:
| Orden | Código |
|---|---|
ping -n 1 127.0.0.1 (contesta) | 0 |
ping -n 1 -w 500 192.0.2.1 (no contesta nadie) | 1 |
git estado (comando que no existe) | 1 |
git log --onelin (opción mal escrita) | 128 |
El código es lo que usa un script para decidir qué hacer, sin leer lo que el programa ha escrito. && ejecuta lo siguiente solo si el código fue 0, y || solo si no lo fue:
tasklist | find /i "vfp9.exe" > nul && echo VFP abierto || echo VFP cerradofind devuelve 0 si ha encontrado el texto y 1 si no, así que esa línea dice si hay algún VFP en marcha. Con VFP abierto responde «VFP abierto», y cerrado, «VFP cerrado».
En PowerShell el código queda en $LASTEXITCODE en lugar de %ERRORLEVEL%.
El código de salida de un programa VFP
Es la misma idea que el RETURN de un procedimiento, pero entre programas: el que termina le deja un número al que lo lanzó. Un .exe de VFP que acaba con QUIT sale siempre con 0, haya ido como haya ido. QUIT no acepta un código. Para devolver otro número hay que pedírselo a Windows:
DECLARE ExitProcess IN kernel32 INTEGER nCodigo
ExitProcess(3)Lo he probado lanzando VFP con un programa que hace solo eso: sale con 3, y el de al lado, que hace QUIT, con 0. ExitProcess termina el proceso en el acto, sin pasar por el cierre normal de VFP, así que llámalo cuando ya hayas cerrado tus tablas y guardado lo que tengas que guardar.
Con esto, un .bat o el Programador de tareas pueden saber si tu proceso nocturno ha ido bien.
Las tildes que llegan cambiadas
En la primera entrega salió Versi¢n al leer desde VFP la salida de ver. Es porque la consola que abre RUN escribe con la página de códigos 850, la del MS-DOS, y VFP trabaja con la 1252 de Windows. chcp te dice cuál usa la consola:
Página de códigos activa: 850Las vocales con tilde tienen números distintos en una y en otra. CPCONVERT pasa el texto de una página a la otra:
RUN ver > version.txt
? CPCONVERT(850, 1252, FILETOSTR("version.txt"))Y ahora sale Microsoft Windows [Versión 10.0.26200.9457].
Si usas PowerShell
Las redirecciones son las mismas, con dos diferencias que dan problemas al leer el resultado desde VFP:
>escribe en UTF-16. En el PowerShell 5.1 que trae Windows,ping -n 1 127.0.0.1 > ps.txtdeja un fichero que empieza por los bytesFF FEy lleva un byte a cero entre letra y letra: ocupa el doble que el mismo fichero hecho desdecmd. Leído conFILETOSTRtal cual, cada letra viene con unCHR(0)detrás.STRCONVlo pasa a texto normal quitando antes esos dos primeros bytes:STRCONV(SUBSTR(FILETOSTR("ps.txt"), 3), 6).nulse escribe$null.git estado 2> nulda un error en PowerShell; lo que funciona esgit estado 2> $null.
Para practicar
En un cmd, dentro de tu carpeta de pruebas:
ping -n 1 127.0.0.1 > salida.txt
ping -n 1 127.0.0.1 >> salida.txt
type salida.txt
git estado > resultado.txt
git estado 2> errores.txt
type errores.txt
dir /b C:\Windows | find /c /v ""
ping -n 1 -w 500 192.0.2.1 > nul && echo Contesta || echo No contestaY en la ventana de comandos de VFP:
RUN dir C:\Windows > carpeta.txt
? CPCONVERT(850, 1252, FILETOSTR("carpeta.txt"))En la entrega 4, cómo se lee la ayuda de un programa de consola, y qué tiene que llevar la de uno que escribas tú.