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:

CanalNombre en inglésPara qué
Entradastdinlo que el programa lee (el teclado, si no dices otra cosa)
Salidastdoutel resultado
Errorstderrlos 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:

La salida de ? a un fichero, como el > de la consola
SET ALTERNATE TO eco.txt
SET ALTERNATE ON
? "Facturas pendientes:", 12
SET ALTERNATE OFF
SET ALTERNATE TO

En 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.txt

En 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 > nul

El error va por otro lado

Prueba a mandar a un fichero la salida de un comando de git que no existe:

git estado > resultado.txt
git: '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.txt

Ahora 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>&1

Tener 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.txt
Almeria
Madrid
Zaragoza

Si 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 KB

Otra: 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 ""
102

Ninguno 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:

OrdenCó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 cerrado

find 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:

Terminar un programa VFP con código de salida 3
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: 850

Las vocales con tilde tienen números distintos en una y en otra. CPCONVERT pasa el texto de una página a la otra:

Leer desde VFP la salida de un programa de consola, con sus tildes
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.txt deja un fichero que empieza por los bytes FF FE y lleva un byte a cero entre letra y letra: ocupa el doble que el mismo fichero hecho desde cmd. Leído con FILETOSTR tal cual, cada letra viene con un CHR(0) detrás. STRCONV lo pasa a texto normal quitando antes esos dos primeros bytes: STRCONV(SUBSTR(FILETOSTR("ps.txt"), 3), 6).
  • nul se escribe $null. git estado 2> nul da un error en PowerShell; lo que funciona es git 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 contesta

Y en la ventana de comandos de VFP:

La salida de dir, leída desde VFP con sus tildes
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ú.