In diesem Tutorial bauen wir dbf, ein Konsolenwerkzeug in Visual FoxPro, das Tabellen ansieht und exportiert, ohne VFP zu öffnen:
dbf info customers.dbf
dbf campos customers.dbf --format json
dbf exportar customers.dbf clientes.csvEs hat drei Befehle, eine Hilfe, die sich von selbst schreibt, Fehler, die erklären, was los ist, und Exit-Codes, die ein Skript lesen kann. Weitergegeben wird es durch Kopieren eines Ordners, auf einen Rechner ohne Visual FoxPro. Und der gesamte Code ist eine VFP-.prg mit 76 Zeilen.
Ich habe es auf Spanisch gebaut, deshalb sind die Befehlsnamen (campos heißt „Felder“, exportar „exportieren“), die Meldungen und die Screenshots auf Spanisch. FoxForge selbst spricht Ihre Sprache: Die Menünamen gebe ich auf Deutsch an. Wie Ihre Befehle heißen, entscheiden Sie.
Wir arbeiten mit customers.dbf, der Kundentabelle aus den Northwind-Beispieldaten, die VFP mitbringt. Was Sie auf den Screenshots sehen, können Sie also genau so nachmachen. dbf liest nur: Es schreibt nie in die Tabelle.
Wenn Ihnen die Konsole fremd ist, gibt es vorher den Kurs CLIs für Fox-Entwickler: was ein Befehl ist, was Argumente und Optionen sind und was ein Exit-Code ist. Hier setze ich das alles voraus.
Was Sie brauchen
- Visual FoxPro 9 (oder VFP Advanced).
- FoxCli installiert. Es bringt FoxForge mit, die Werkstatt, die sich in die VFP-IDE einklinkt. Ohne Lizenz funktioniert alles genauso; den Unterschied sehen wir am Ende.
- Windows 10 oder 11.
Öffnen Sie VFP wie immer, ohne „Als Administrator ausführen“. Beim Kompilieren als Administrator registriert VFP die Komponente in HKLM, und diese Registrierung stört die folgenden Kompilierungen. Und benutzen Sie nicht die Schaltfläche Build im Projekt-Manager: Kompilieren Sie mit Strg+F7, denn nur so wird die CLI vollständig.
Schritt 1. FoxForge laden
Im Befehlsfenster von VFP:
DO "C:\Program Files (x86)\FoxForge\FoxForge.app"Passen Sie den Pfad an, wenn Sie FoxForge in einen anderen Ordner installiert haben. In der Menüleiste von VFP erscheint ein neues Menü, FoxForge, und von dort aus wird alles Weitere gemacht.
Schritt 2. Das Projekt anlegen
FoxForge > Neues Projekt > Konsolenanwendung (CLI). Im Assistenten:
- Name:
dbf. Das ist der Name des Programms, den man in der Konsole tippt. - Ordner: Ihr Projektordner. Ich habe
C:\a1\tutorial-bbenutzt. - Minimale Vorlage, ohne Kommentare: angehakt. Die andere Vorlage bringt den Code Zeile für Zeile erklärt mit, was man einmal gelesen haben sollte, aber heute schreiben wir unseren eigenen.

Klicken Sie auf Anlegen. FoxForge legt das Projekt an und öffnet es im Projekt-Manager:
dbf\
├── dbf.pjx das VFP-Projekt
├── foxforge.json kennzeichnet es als CLI-Projekt
├── pack.ps1 packt, was weitergegeben wird
├── src\
│ ├── main.prg Ihre Befehle: die einzige Datei, die Sie bearbeiten
│ ├── foxcli.h benannte Exit-Codes
│ └── foxcli.prg die Basisklasse, die zu FoxCli gehört
└── dist\ hier landet die .exe beim KompilierenSchritt 3. Was die Vorlage mitbringt
Öffnen Sie src\main.prg:
#INCLUDE foxcli.h
*!* dbf: describe aquí lo que hace tu CLI
DEFINE CLASS DbfCommand AS FoxCliCommand OF foxcli.prg OLEPUBLIC
*!* Saluda a alguien
*!* @tcNombre -n A quién saludar
PROCEDURE Saludar(tcNombre AS String)
Console.WriteLine("Hola " + tcNombre + "!")
ENDPROC
ENDDEFINEDaran sieht man schon, wie alles funktioniert:
- Jede öffentliche Methode der Klasse ist ein Befehl.
Saludar(„grüßen“) wird alsdbf saludarbenutzt. - Jeder Parameter ist eine Option, benannt ohne sein Präfix:
tcNombreist--nombre. Der Typ kommt aus demAS. - Der
*!*-Block darüber ist die Dokumentation. Die erste Zeile ist die Beschreibung des Befehls, und jede@-Zeile beschreibt einen Parameter:-ngibt ihm einen kurzen Alias, der Rest ist seine Beschreibung. Daraus entsteht die Hilfe. Consoleist die Konsole.Console.WriteLine()schreibt auf die Ausgabe undConsole.Error()auf den Fehlerkanal.
Schritt 4. Die Befehle schreiben
Ersetzen Sie den Inhalt von main.prg durch diesen Code:
#INCLUDE foxcli.h
*!* dbf: mira y exporta tablas de VFP desde la consola, sin abrir Visual FoxPro
*!* @exit 10 La tabla no existe
DEFINE CLASS DbfCommand AS FoxCliCommand OF foxcli.prg OLEPUBLIC
*!* Cuántos registros y campos tiene una tabla
*!* @tcTabla <> La tabla (.dbf)
*!* @example dbf info customers.dbf
PROCEDURE Info(tcTabla AS String)
IF !THIS.Abrir(tcTabla)
RETURN EXIT_NO_TABLE
ENDIF
Console.WriteLine("Registros: " + TRANSFORM(RECCOUNT()))
Console.WriteLine("Campos: " + TRANSFORM(FCOUNT()))
Console.Write("Modificada: ")
Console.WriteLine(LUPDATE())
USE
ENDPROC
*!* La estructura de una tabla: nombre, tipo y ancho de cada campo
*!* @tcTabla <> La tabla (.dbf)
*!* @example dbf campos customers.dbf --format json
PROCEDURE Campos(tcTabla AS String)
LOCAL laCampos[1], lnCampos, lnI, lcJson
IF !THIS.Abrir(tcTabla)
RETURN EXIT_NO_TABLE
ENDIF
lnCampos = AFIELDS(laCampos)
USE
IF Console.IsJson()
lcJson = ""
FOR lnI = 1 TO lnCampos
lcJson = lcJson + IIF(lnI > 1, ",", "") + ;
'{"nombre":"' + LOWER(laCampos[lnI, 1]) + '","tipo":"' + laCampos[lnI, 2] + ;
'","ancho":' + TRANSFORM(laCampos[lnI, 3]) + '}'
ENDFOR
Console.WriteLine("[" + lcJson + "]")
ELSE
FOR lnI = 1 TO lnCampos
Console.WriteLine(PADR(LOWER(laCampos[lnI, 1]), 14) + laCampos[lnI, 2] + ;
STR(laCampos[lnI, 3], 5))
ENDFOR
ENDIF
ENDPROC
*!* Copia una tabla a un fichero CSV o XLS
*!* @tcTabla <> La tabla (.dbf)
*!* @tcDestino <> El fichero que se crea
*!* @tcFormato :csv|xls =csv Formato del fichero
*!* @example dbf exportar customers.dbf clientes.csv
*!* @example dbf exportar customers.dbf clientes.xls --formato xls
PROCEDURE Exportar(tcTabla AS String, tcDestino AS String, tcFormato AS String)
IF !THIS.Abrir(tcTabla)
RETURN EXIT_NO_TABLE
ENDIF
IF tcFormato == "xls"
COPY TO (tcDestino) TYPE XL5
ELSE
COPY TO (tcDestino) TYPE CSV
ENDIF
Console.Error(TRANSFORM(RECCOUNT()) + " registros copiados a " + tcDestino)
USE
ENDPROC
*-- Abre la tabla solo para leer. Si no existe, lo dice por el canal de error.
PROTECTED FUNCTION Abrir(tcTabla)
IF !FILE(FORCEEXT(tcTabla, "dbf"))
Console.Error("dbf: no encuentro la tabla " + tcTabla)
RETURN .F.
ENDIF
USE (FORCEEXT(tcTabla, "dbf")) AGAIN SHARED NOUPDATE ALIAS tabla
RETURN .T.
ENDFUNC
ENDDEFINEUnd fügen Sie am Ende von src\foxcli.h den eigenen Exit-Code hinzu:
#DEFINE EXIT_NO_TABLE 10 && la tabla no existeWorauf es in diesem Code ankommt:
<>macht ein Positionsargument.@tcTabla <>sagt, dass die Tabelle ohne Namen davor geschrieben wird:dbf info customers.dbfund nichtdbf info --tabla customers.dbf.:csv|xls =csvist eine feste Liste mit Standardwert.--formatoakzeptiert nurcsvoderxls, und ohne Angabe giltcsv. Das prüft FoxCli, bevor Ihr Code überhaupt erreicht wird.- Was die Methode zurückgibt, ist der Exit-Code. Ohne
RETURNendet der Befehl mit 0.RETURN EXIT_NO_TABLEendet mit 10, und das@exit 10oben dokumentiert es. Ihre eigenen reichen von 10 bis 125: Die niedrigen Zahlen gehören FoxCli. Console.IsJson()sagt, ob der Aufrufer--format jsonverlangt hat. Diese Option deklarieren Sie nicht selbst: FoxCli bringt sie in jedem Befehl mit.- Das Ergebnis geht auf die Ausgabe, Meldungen auf den Fehlerkanal.
exportarmeldet mitConsole.Error(), wie viele Datensätze kopiert wurden. Leitet jemand die Ausgabe in eine Datei um, mischt sich die Meldung also nicht hinein. Abrir(„öffnen“) istPROTECTED: eine Hilfsmethode wie jede andere, die nicht in der Befehlsliste erscheint. Deshalb hat ihr Kommentar das übliche*--:*!*ist für das, was in der Hilfe der CLI erscheint, und eine Hilfsmethode wird nie aus der Konsole aufgerufen.Console.WriteLine(LUPDATE())schreibt das Datum auf jedem Rechner im ISO-Format (2004-12-09), unabhängig vonSET DATE. Die Ausgabe einer CLI lesen andere Programme, und sie muss überall gleich sein.
Schritt 5. Kompilieren
FoxForge > Aktuelles Projekt > CLI kompilieren oder Strg+F7. Ohne FoxCli-Lizenz erscheint dieser Hinweis:

Jetzt haben Sie dist\dbf.exe. Nebenbei hat FoxForge die Dokumentation in docs\dbf.md erzeugt, mit der Beschreibung jedes Befehls aus den *!*-Blöcken.
Schritt 6. Aus der IDE ausprobieren
Bevor Sie eine Konsole öffnen, können Sie es ausprobieren, ohne VFP zu verlassen. FoxForge > Aktuelles Projekt > Argumente… oder Umschalt+F8, und tippen Sie die Zeile so, wie Sie sie in einer Konsole tippen würden, ohne den Programmnamen:

Er läuft in dist\, deshalb steht hier der vollständige Pfad der Tabelle. Ausführen (oder beim nächsten Mal Strg+F8, weil es sich die Argumente merkt) öffnet eine Konsole mit dem Ergebnis und darunter dem Exit-Code:

Wenn etwas schiefgeht, führt FoxForge > Aktuelles Projekt > Prüfstand einen Befehl direkt aus Ihrem Quelltext aus, mit dem VFP-Debugger zur Hand und ohne zu kompilieren.
Schritt 7. Weitergeben und aus der Konsole benutzen
Im Projektordner packt pack.ps1 ein Zip mit dem, was zum Ausführen der CLI nötig ist, und mit nichts sonst. Aus einer Konsole in diesem Ordner:
powershell -ExecutionPolicy Bypass -File pack.ps19 ficheros -> C:\a1\tutorial-b\dbf\dbf-dist.zip (3.634.677 bytes)
Se despliega descomprimiendo: no hace falta registrar nada ni ser administrador.Diese 9 Dateien sind Ihre CLI und die Laufzeit von VFP 9, damit sie auf einem Rechner ohne Visual FoxPro läuft. Entpacken Sie sie, wohin Sie wollen (ich in einen Werkzeugordner), und schon ist sie installiert. Es wird nichts registriert, also muss auch nichts deinstalliert werden: Man löscht den Ordner.
Um den VFP-Ordner nicht anzufassen, kopieren Sie die Northwind-Daten in Ihren Testordner. Achtung: den ganzen Ordner, denn customers.dbf gehört zur Datenbank northwind.dbc:

Wechseln Sie in die Kopie und fügen Sie den Ordner von dbf zum PATH dieser Konsole hinzu, damit Sie dbf ohne Pfad tippen können:
cd C:\pruebas-cli\northwind
set PATH=%PATH%;C:\herramientas\dbf(Ersetzen Sie C:\herramientas\dbf durch den Ordner, in den Sie entpackt haben. Dieses set gilt nur für dieses Fenster.)
Die Hilfe haben Sie nebenbei geschrieben, in den *!*-Blöcken:

Auch describe bringt FoxCli mit: Es gibt alle Befehle mit ihren Optionen als JSON aus, damit ein KI-Agent die CLI entdecken kann, ohne sonst etwas zu lesen.
Fehlt ein Argument, erreicht dbf Ihren Code gar nicht erst: Es sagt, was fehlt, zeigt die Hilfe des Befehls und endet mit 2. Mit der Tabelle liefert es das Ergebnis:

campos zeigt die Struktur als Tabelle zum Lesen am Bildschirm:

Und mit --format json als eine Zeile, die ein anderes Programm lesen kann:
dbf campos customers.dbf --format json[{"nombre":"customerid","tipo":"C","ancho":5},{"nombre":"companyname","tipo":"C","ancho":40},{"nombre":"contactname","tipo":"C","ancho":30},...,{"nombre":"fax","tipo":"C","ancho":24}]exportar legt die Datei an und meldet sich auf dem Fehlerkanal. Mit einer Tabelle, die es nicht gibt, endet es mit der 10, die wir deklariert haben:

Im CSV stehen die Akzente an ihrem Platz (Avda. de la Constitución 2222, México D.F.), weil VFP es mit der Codepage der Tabelle schreibt.
Und ein Format, das nicht in der Liste steht, erreicht Ihren Code nie: FoxCli sagt, welche gültig sind, zeigt die Hilfe von exportar und endet mit 2.

Eine Tabelle fern ihrer Datenbank. Kopieren Sie nur customers.dbf in einen anderen Ordner, ohne northwind.dbc, will VFP Sie fragen, wo die Datenbank ist. In einer CLI ist niemand da, den man fragen könnte, also endet dbf mit Code 1 und dem VFP-Fehler auf dem Fehlerkanal (Fehler 2031, eine Operation der Benutzeroberfläche ist gerade nicht erlaubt). Es bleibt nicht hängen und wartet auf eine Antwort: FoxCli lässt nie einen Dialog offen.
Schritt 8. Die Lizenz
Ohne Lizenz funktioniert die CLI genauso und ohne Ablaufdatum. Der einzige Unterschied ist die Zeile, die Sie am Ende jeder Ausgabe gesehen haben (bei mir auf Spanisch):
Hecho con FoxCli (evaluación) - irwinrodriguez.dev/foxcliSie geht auf den Fehlerkanal, landet also in keinem CSV und keinem JSON, auch wenn Sie die Ausgabe umleiten. Mit einer FoxCli-Lizenz, geladen unter FoxForge > FoxCli-Lizenz…, siegelt Strg+F7 die CLI mit Ihrem Namen: Die Zeile verschwindet, und --version sagt, an wen sie lizenziert ist.
dbf --versiondbf (FoxCli 1.3.0, con licencia de Irwin Rodríguez)Die CLIs, die Sie kompilieren, geben Sie kostenlos weiter, an wen Sie wollen: Die Lizenz gehört dem Entwickler, der kompiliert, nicht jeder Kopie.
Wie es weitergeht
- Die FoxCli-Anleitung finden Sie unter FoxForge > Hilfe > FoxCli-Anleitung: Eingaben lesen, Datumsangaben, das Ausgabeformat, die
HELP-Klausel und alles, was heute nicht vorkam. - Die Dokumentation Ihrer CLI liegt in
docs\dbf.mdund wird bei jeder Kompilierung neu erzeugt: Sie taugt so, wie sie ist, als README. - Ein großes Beispiel: FoxPack, der Bibliotheksmanager für VFP-Projekte, ist eine mit FoxCli gebaute CLI, und sein Quellcode ist veröffentlicht.
- Ideen, wie es mit
dbfweitergeht: ein Befehlcontar(„zählen“) mit Filter (--donde "country = 'Spain'"), oder dassexportarein--camposannimmt, um die Spalten auszuwählen.