In der heutigen "Look&Feel" Zeit scheinen Kommandozeilen-Programme schon ein wenig veraltet zu sein. Die nächste Generation wird mit den Begriffen Kommandozeile oder Konsole nichts mehr anzufangen wissen. C C++ C/C++ Kommandozeile getopt argc argv Zeiger Arrays Kommandozeile - getopt argc argv Kapitel 16: Kommandozeile

In der heutigen "Look&Feel"-Zeit scheinen Kommandozeilen-Programme schon ein wenig veraltet zu sein. Die nächste Generation wird mit Begriffen wie "Kommandozeile" oder "Konsole" wohl nichts mehr anzufangen wissen.

Wenn aber z.B. ältere Programme überholt werden müssen, wird der Umgang mit der Kommandozeile wieder wichtig. Bei Betriebssystemen wie Linux, UNIX oder FreeBSD ist es nach wie vor üblich, sehr viel mit einer Kommandozeile zu arbeiten.

Beim Schreiben eines Konsolen-Programms für Linux/UNIX oder MS-DOS (-Eingabeaufforderung) sind Kommandozeilenparameter immer noch eines der wichtigsten Konzepte. Da Konsolen-Programme keine grafische Oberfläche besitzen, stellt die Kommandozeile die wichtigste Schnittstelle zwischen dem Anwender und dem Programm dar.

Hinweis
 

Dieser Teil des Buchs ist nicht für Entwicklungsumgebungen (IDEs) geeignet. Dem Programm werden Argumente beim Aufruf übergeben. Unter MS-Windows muss dabei das alte MS-DOS herhalten. Die MS-DOS-Eingabeaufforderung von MS-Windows ist dafür aber auch geeignet. Unter Linux genügt eine einfache Konsole.

16.1. Argumente an die Hauptfunktion            zurück  Ein Kapitel tiefer  zum Inhaltsverzeichnis

Um einem Programm beim Start Argumente zu übergeben, wird eine parametrisierte Hauptfunktion benötigt. Hierzu die Syntax:

int main(int argc, char *argv[]) 

Diese Hauptfunktion main() besitzt zwei Parameter mit den Namen argc und argv. Die Namen dieser Parameter sind so nicht vorgeschrieben. Sie können genauso gut schreiben:

int main(int argumenten_zaehler, char *argumenten_vektor[])

Der erste Parameter beinhaltet die Anzahl von Argumenten, welche dem Programm beim Start übergeben wurden. Dabei handelt es sich um einen Integerwert. Im zweiten Parameter stehen die einzelnen Argumente. Diese werden als Strings in einer Stringtabelle gespeichert. Folgendes Beispiel demonstriert dies:

#include <stdio.h>

int main(int argc, char *argv[])
{
   int i;

   for(i=0; i < argc; i++)
      {
         printf("argv[%d] = %s ",i,argv[i]);
         printf("\n");
      }
   return 0;
}

Das Listing wurde z.B. unter dem Namen argument.c gespeichert und anschließend übersetzt. Starten Sie das Programm, wird auf dem Bildschirm der Programmname ausgegeben:

argv[0] = argument

Starten Sie das Programm jetzt nochmals mit folgender Eingabe ("argument" sei wieder der Programmname):

argument Hallo Welt 

Die jetzige Ausgabe lautet:

Abbildung 16.1: Argumente aus der Kommandozeile auswerten
Abbildung 16.1: Argumente aus der Kommandozeile auswerten

In argv[0], dem ersten Argument, befindet sich immer der Programmname selbst. Die einzelnen Argumente, die dem Programm übergeben werden, müssen immer mit mindestens einer Leerzeile getrennt sein. Zum Beispiel mit

argument HalloWelt

wäre die Ausgabe stattdessen

argv[0] = argument
argv[1] = HalloWelt

Der Parameter int argc zählt die Anzahl der Strings, die dem Programm beim Aufruf mitgegeben wurden. Dazu ein Beispiel:

#include <stdio.h>

int main(int argc, char *argv[])
{
   printf("Insgesamt %d Argumente\n",argc-1);
   printf("Letztes Argument: %s\n",argv[argc-1]);
   return 0;
}

Bei diesem Beispiel werden die Anzahl der Argumente und das letzte Argument ausgegeben. Als Programmaufruf dient etwa:

argument abc xyz 

In der Stringtabelle char *argv[] befinden sich folgende Strings:

Abbildung 16.2: Inhalt der Stringtabelle argv[]
Abbildung 16.2: Inhalt der Stringtabelle argv[]

Falls dezimale Werte anstatt Strings als Argumente übergeben werden, handelt es sich dabei weiterhin um Strings. Wird der dezimale Wert benötigt, so muss dieser erst in einen solchen konvertiert werden. Hierzu das Beispiel einer solchen Konvertierung:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TRUE 0
#define FALSE -1

int main(int argc, char *argv[])
{
   int i,j,y,erg;
   if(argc < 4)
      {
         printf("Benötige mindestens 4 Argumente!\n");
         printf("Aufruf:%s <zahl><op><zahl>...\n",*argv);
         exit(1);
      }
   /* 1.Zahl in einen Integer konvertieren*/
   erg = atoi(argv[1]);

   for(i=1; i<argc-1; i+=2)
      {
         for(j=i+1;j<i+2;j++)
            {
               y = atoi(argv[i+2]);
               if(strcmp(argv[j],"+") == TRUE)
                  erg+=y;
               else if(strcmp(argv[j],"-") == TRUE)
                  erg-=y;

               /* Vergleich für Linux mit dem Zeichen * in der
                  Kommandozeile nicht möglich, wegen dessen
                  besonderer Bedeutung. Stattdessen das Zeichen m
                  für Multiplikation verwenden.*/

               /* else if(strcmp(argv[j],"m") == TRUE) */
               else if(strcmp(argv[j],"*") == TRUE)
                  erg*=y;
               else if(strcmp(argv[j],"/") == TRUE)
                  erg/=y;
               else
                  {
                     printf("Ungültiger Operand: %s\n",argv[j]);
                     exit(1);
                  }
            }/*for*/
      }/*for*/
   printf("%d\n",erg);
   return 0;
}

Damit lassen sich einfache Rechenoperationen ausführen. Im Folgenden soll eine solche Eingabe demonstriert werden (der Programmname sei hierfür calc):

calc 5 + 5 - 9 * 10 / 2 

Intern sieht diese Eingabe so aus:

Abbildung 16.3: Einzelne Argumente für Rechenoperationen auswerten
Abbildung 16.3: Einzelne Argumente für Rechenoperationen auswerten

Hier wurden zehn Argumente eingegeben, wobei jedes dieser zehn Argumente ein Stringende-Zeichen (\0) besitzt. Somit besitzt der Parameter int argc in der Funktion main() den Wert neun. Beachten Sie bei der Eingabe, dass nach jedem Zeichen ein Leerzeichen folgt. Folgendes würde nicht funktionieren:

calc 5+5-9*10/2   /* falsch, nur ein Argument*/

Mit

if(argc < 4)
   {
      printf("Benötige mindestens 4 Argumente!\n");
      printf("Aufruf: %s <zahl><op><zahl>...\n",*argv);
      exit(1);
   }

wird getestet, ob weniger als vier Argumente eingegeben wurden. Falls dies zutrifft, wird eine entsprechende Fehlermeldung auf dem Bildschirm ausgegeben und das Programm beendet.

Wenn die Mindestanzahl von Argumenten gegeben ist, folgt als Nächstes die Konvertierung der Argumente:

erg = atoi(argv[1]); 

Hierbei wird mit der Funktion atoi() der String in argv[1] in eine dezimale Zahl konvertiert. Im ersten Fall wird der String "5" in den Integerwert 5 umgewandelt. Die Funktion atoi() ist in der Headerdatei <stdlib.h> deklariert. Außerdem befindet sich darin u.a. noch die Funktion atof() und atol(). atof() konvertiert einen String in eine Gleitkommazahl, und atol() macht aus einem String einen long-Wert. Mehr zu diesen Funktionen in Kapitel 23, Weitere Headerdateien und ihre Funktionen.

Weiter mit dem Programmablauf bei den for-Schleifen:

for(i=1; i<argc-1; i+=2)
   {
      for(j=i+1;j<i+2;j++)
         {
            y = atoi(argv[i+2]);

Die erste for-Schleife durchläuft die ungeraden Zahlen des Feldindex, in dem sich (bei richtiger Anwendung des Programms) dezimale Zahlen befinden: [1]="5", [3]="5", [5]="9", [7]="10", [9]="2". Die zweite for-Schleife durchläuft die geraden Zahlen und dient den Operatoren +, -, * und / ([2]="+", [4]="-", [6]="*", [8]="/"). Danach bekommt die Variable y den dezimalen Wert der Zeichenkette argv[3], der wiederum mit atoi() konvertiert wird. Jetzt erfolgt die Auswertung des Operators:

if(strcmp(argv[j],"+") ==0)
   erg+=y;
…

Entspricht argv[2] dem Additionszeichen "+"? In dem Fall ist das zweite Argument tatsächlich das Zeichen "+". Daher kann jetzt der erste Wert (zweites Argument) mit dem zweiten Wert (viertes Argument) addiert (drittes Argument) werden. Der Wert wird in der Variablen erg zwischengespeichert. Genauso läuft dies mit den nächsten Zahlen und Operatoren ab, bis keine Argumente (Zahlen oder Operatoren) mehr vorhanden sind.

16.2. Optionen (Schalter )- aus der Kommandozeile auswerten            zurück  Ein Kapitel höher  zum Inhaltsverzeichnis

Der Taschenrechner, wie Sie ihn eben programmiert haben, macht in der heutigen Zeit wenig Sinn. Sinnvoller und (immer noch) sehr häufig verwendet werden Kommandozeilen-Argumente, um Schalter auszuwerten. Etwa:

rm --help

Damit wird eine kurze Hilfsanleitung des Programms rm angezeigt. Meistens handelt es sich dabei um eine kurze Beschreibung, mit welchen Optionen (Schaltern) das Programm aufgerufen werden kann und was diese bewirken. Das Programm rm ist in der UNIX-Welt beheimatet und dient dem Löschen von Dateien und Verzeichnissen. Beispielsweise wird mit den Schaltern -rf

rm -rf verzeichnis 

ein ganzes Verzeichnis rekursiv ohne Nachfrage gelöscht. Dazu folgt jetzt ein Beispiel. Auf (fast) jedem System gibt es das Programm echo, womit Sie einen Text auf die Standardausgabe schicken können. Beispielsweise mit

echo Hallo Welt

wird "Hallo Welt" auf dem Bildschirm ausgegeben. Das folgende Programm ist ein eigenes Echo, myecho, womit der Text mithilfe von Schaltern spiegelverkehrt, in Groß- oder Kleinbuchstaben ausgegeben werden kann. Folgende Schalter werden verwendet:

void show_help(void)
{
   printf("\nProgrammaufruf: myecho [OPTION] STRING\n"\
          "Programm gibt den Text in gewünschter Form auf"\
          "dem Bildschirm aus\n\n nFolgende Optionen stehen"\
          "Ihnen zur Verfügung:\n\n"\
          "\t-r  Text wird spiegelverkehrt ausgegeben\n"\
          "\t-g  Text wird in Grossbuchstaben ausgegeben\n"\
          "\t-s  Text wird in Kleinbuchstaben ausgegeben\n "\
          "\t-h  Dieser Text\n"
          "\t-v  Versionsnummer\n\n");
}

Hier der vollständige Quellcode:

#include <stdio.h>
#include <string.h>
#include <ctype.h> /* tolower(), toupper(), isalpha() */
#include <stdlib.h>
#define FALSE 0
#define TRUE  1
#define BUF 4096

void show_help(void)
{
   printf("\nProgrammaufruf: myecho [OPTION] STRING\n"\
          "Programm gibt den Text in gewünschter Form auf"\
          "dem Bildschirm aus\n\nFolgende Optionen stehen"\
          "Ihnen zur Verfügung:\n\n"\
          "\t-r  Text wird spiegelverkehrt ausgegeben\n"\
          "\t-g  Text wird in Grossbuchstaben ausgegeben\n"\
          "\t-s  Text wird in Kleinbuchstaben ausgegeben\n "\
          "\t-h  Dieser Text\n"
          "\t-v  Versionsnummer\n\n");
}

int getopt(char *argument,char *option)
{
   if( argument[0]=='-' && argument[1]==option[0] )
      return TRUE;
   return FALSE;
}

void spiegeln(char *string)
{
   char *reverse=string;
   while(*reverse++);
   while(*reverse-- != *string)
      printf("%c",*reverse);
   printf("\n");
}

void larger(char *string)
{
   char *large=string;
   while(*large)
      printf("%c",(isalpha(*large))?toupper(*large++):*large++);
   printf("\n");
}

void smaller(char *string)
{
   char *small=string;
   while(*small)
      printf("%c",(isalpha(*small))?tolower(*small++):*small++);
   printf("\n");
}

int main(int argc, char *argv[])
{
   int counter=3;
   char buffer[BUF];
   size_t len=0;
   if(argc == 1 || getopt(argv[1],"h") == TRUE )
      {
         show_help();
         exit(0);
      }
   else if(getopt(argv[1],"v") == TRUE)
      {
         printf("Version 1.0\n");
         exit(0);
      }
   else if(argc < 3)
      {
         show_help();
         exit(0);
      }

   len=strlen(argv[2])+1;
   /*Ab argv[2] bis argv[n] alle Elemente in buffer*/
   if(len > BUF)
     {
        printf("Der String enthält zu viele Zeichen\n");
        exit(0);
     }
   strcpy(buffer,argv[2]);
   while(argv[counter]!=NULL)
      {
         len += strlen(argv[counter])+2;
         if(len > BUF)
            {
               printf("Der Puffer ist bereits voll\n");
               break;
            }
         strcat(buffer, " ");
         strcat(buffer, argv[counter++]);
      }

   if(getopt(argv[1],"r") == TRUE)
      spiegeln(buffer);
   else if(getopt(argv[1],"g") == TRUE)
      larger(buffer);
   else if(getopt(argv[1],"s") == TRUE)
      smaller(buffer);
   else
      show_help();
   return 0;
}

Als Kernstück in diesem Programm dient die Funktion getopt():

int getopt(char *argument, char *option)
{
   if( argument[0]=='-' && argument[1]==option[0] )
      return TRUE;
   return FALSE;
}

Damit wird überprüft, ob der String mit dem Schalterzeichen (-) beginnt und ob für option eine Option vorhanden ist:

if(getopt(argv[1],"s") == TRUE) 

Hier wird beispielsweise überprüft, ob der Schalter s verwendet wurde. Falls dies zutrifft, wird TRUE zurückgegeben, andernfalls FALSE. Alle anderen Funktionen dienen nur zur Verzierung des Programms, welches Sie natürlich weiter ausbauen können.

Hier wurden außerdem Funktionen der Standard-Headerdatei <ctype.h> verwendet, die der Überprüfung und Konvertierung von einzelnen Zeichen dienen. Die Funktion isalpha() überprüft, ob es sich um ein Zeichen des Alphabets handelt. Ist dies der Fall, liefert diese Funktion 1 zurück. Die Funktion tolower() macht aus einem großen Buchstaben einen kleinen und toupper() aus einem kleinen Buchstaben wieder einen großen. Mehr zu dieser Headerdatei und den darin enthaltenen Funktionen erfahren Sie später.

Weiter mit Kapitel 17: Dynamische Speicherverwaltung            zum Inhaltsverzeichnis