In the first part we sent the output of ver to a file with > and read it from VFP. Here is what lies behind that: where a console program writes and reads, how each thing is redirected, and how a program tells whoever launched it whether it went well. With this you can chain programs and use them in a .bat without looking at the screen.

Three channels

Every console program starts with three channels open:

ChannelNameWhat for
Inputstdinwhat the program reads (the keyboard, unless you say otherwise)
Outputstdoutthe result
Errorstderrwarnings and errors

Output and error go to the same screen, so at a glance you cannot tell them apart. They are two different channels, and they can be sent to different places.

VFP has something similar for output. What you write with ? goes to the screen, and with SET ALTERNATE you send it to a file as well:

The output of ? to a file, like the console's >
SET ALTERNATE TO echo.txt
SET ALTERNATE ON
? "Pending invoices:", 12
SET ALTERNATE OFF
SET ALTERNATE TO

echo.txt ends up holding Pending invoices: 12. What VFP lacks is a separate channel for errors: a VFP error opens a window or jumps to your ON ERROR.

Output to a file

> sends the output to a file instead of the screen. If the file exists, it replaces it:

ping -n 1 127.0.0.1 > output.txt

Nothing appears on screen, and output.txt holds the 9 lines you would have seen. With >> it appends instead of replacing: another ping -n 1 127.0.0.1 >> output.txt and the file grows to 18 lines.

When you do not care about the output, you send it to nul, a special Windows file that swallows everything:

ping -n 1 127.0.0.1 > nul

Errors go another way

Try sending the output of a git subcommand that does not exist to a file:

git estado > result.txt
git: 'estado' is not a git command. See 'git --help'.

The message still appears on screen, and result.txt stays empty, 0 bytes. git wrote the message on the error channel, and > only redirects the output. For the error you put a 2 in front:

git estado 2> errors.txt

Now the screen stays clean and the message is in errors.txt. If you want both in the same file, 2>&1 sends the error wherever the output goes:

git estado > all.txt 2>&1

This is what two channels are for: you save the result in a file and problems keep showing on screen, or in another file, without mixing with the data. If a program writes JSON on its output and its warnings on the error channel, the JSON comes out clean.

Not every program respects this. ping writes its own errors on the normal output: ping -n 127.0.0.1 > p.txt shows nothing, and the error message (“Debe especificar la dirección IP.” on mine) ends up inside p.txt. If an error does not show up where you expect, look in the output file too.

Input from a file

< does the opposite of >: the program reads from a file instead of the keyboard. sort sorts the lines that reach it on its input:

sort < cities.txt
Almeria
Madrid
Zaragoza

If you type sort on its own, it sits waiting for you to type lines, because its input is the keyboard. Many console programs work like that: they read their input, process it and write the result.

The pipe: one program’s output into another’s input

The vertical bar | connects two programs: what the first writes on its output reaches the second on its input, without any file in between. tasklist lists the running processes, and find keeps the lines that contain some text. With VFP open:

tasklist | find /i "vfp9.exe"
vfp9.exe                      6808 Console                    1    30.176 KB

Another one: find /c /v "" counts the lines it receives. Put after a dir /b, it counts what is in a folder:

dir /b C:\Windows | find /c /v ""
102

Neither program knows the other exists. Each does its part, and the pipe joins them. That is how the console works: many small programs that combine.

The exit code

When a console program ends, it hands a number back to whoever launched it. That is the exit code, and the rule is simple: 0 means it went well, any other number means it did not. What each non-zero number means is up to each program.

In cmd, the last code is kept in %ERRORLEVEL%:

ping -n 1 127.0.0.1 > nul
echo %ERRORLEVEL%

These are the codes returned by the commands we have used in the course:

CommandCode
ping -n 1 127.0.0.1 (answers)0
ping -n 1 -w 500 192.0.2.1 (nobody answers)1
git estado (subcommand that does not exist)1
git log --onelin (misspelt option)128

The code is what a script uses to decide what to do, without reading what the program wrote. && runs what follows only if the code was 0, and || only if it was not:

tasklist | find /i "vfp9.exe" > nul && echo VFP open || echo VFP closed

find returns 0 if it found the text and 1 if not, so that line tells you whether any VFP is running. With VFP open it answers “VFP open”, and with it closed, “VFP closed”.

In PowerShell the code is kept in $LASTEXITCODE instead of %ERRORLEVEL%.

The exit code of a VFP program

It is the same idea as a procedure’s RETURN, but between programs: the one that ends leaves a number for the one that launched it. A VFP .exe that ends with QUIT always exits with 0, however it went. QUIT does not take a code. To return another number you have to ask Windows:

Ending a VFP program with exit code 3
DECLARE ExitProcess IN kernel32 INTEGER nCode
ExitProcess(3)

I tried it by launching VFP with a program that does only that: it exits with 3, and the one next to it, which does QUIT, with 0. ExitProcess ends the process on the spot, without VFP’s normal shutdown, so call it once you have closed your tables and saved whatever needs saving.

With this, a .bat or the Task Scheduler can know whether your nightly process went well.

Accented letters that arrive changed

In the first part, reading the output of ver from VFP gave Versi¢n. That is because the console RUN opens writes in an MS-DOS code page (850 on mine), and VFP works with Windows’ 1252. chcp tells you which one the console uses (in Spanish on mine):

Página de códigos activa: 850

Accented letters have different numbers in each. CPCONVERT moves text from one code page to the other:

Reading a console program's output from VFP, accents included
RUN ver > version.txt
? CPCONVERT(850, 1252, FILETOSTR("version.txt"))

And now it reads Microsoft Windows [Versión 10.0.26200.9457]. On a US English Windows the console code page is usually 437 rather than 850: put the number chcp gives you.

If you use PowerShell

Redirection works the same, with two differences that cause trouble when you read the result from VFP:

  • > writes UTF-16. In the PowerShell 5.1 that ships with Windows, ping -n 1 127.0.0.1 > ps.txt leaves a file that starts with the bytes FF FE and has a zero byte between each letter: it is twice the size of the same file made from cmd. Read with FILETOSTR as it is, every letter comes with a CHR(0) after it. STRCONV turns it into normal text once you drop those first two bytes: STRCONV(SUBSTR(FILETOSTR("ps.txt"), 3), 6).
  • nul is written $null. git estado 2> nul fails in PowerShell; what works is git estado 2> $null.

Practice

In a cmd, inside your test folder:

ping -n 1 127.0.0.1 > output.txt
ping -n 1 127.0.0.1 >> output.txt
type output.txt
git estado > result.txt
git estado 2> errors.txt
type errors.txt
dir /b C:\Windows | find /c /v ""
ping -n 1 -w 500 192.0.2.1 > nul && echo Answers || echo No answer

And in the VFP Command window:

The output of dir, read from VFP with its accents
RUN dir C:\Windows > folder.txt
? CPCONVERT(850, 1252, FILETOSTR("folder.txt"))

In part 4, how to read a console program’s help, and what the help of one you write should contain.