Kleiner Script-Sprachen-Lehrgang

Hier zur bsh  (ähnlich sh, ksh, zsh, bash)

Copyright © 1998-2001  Helmut Schellong

Zur bsh gibt es zwar eine mehrere 100KB umfassende Beschreibung, jedoch alles im Stil
von technischen Handbüchern (Manual) verfaßt, also kühl, knapp und sachlich geschrieben.

Dieser kleine Lehrgang ist mehr im Erklärstil geschrieben und wendet sich auch an Anfänger.

Siehe auch: 'Kurz-Beispiele'
Siehe auch: 'Für Fortgeschrittene'

Hauptseite


Register

Wie die Shell ihre Eingabe (z.B. Script-Dateien) liest
Kommando-Substitution
Zwischenraumzeichen (erweitert) und Wildcards
Arithmetische und logische Operationen:  Rechnen    bsh64
Der Exit-Kode von Kommandos
Das test-Kommando
Spezialitäten
Operationen mit Dateien und Geräten
Pipelines
Variablen:  Arrays, Indirekter Zugriff, Lokal, Lokal-Statisch, ...
Programmier-Kommandos (Steuerung des Ablaufs)
Shell-Funktionen
Reguläre Ausdrücke (Kommandos: grep, expr)
Hintergrund-Prozesse (fork, ...)
Interne Kommandos der bsh
Aufruf der bsh und Aufruf von Shell-Scripts
Installation der bsh und ihrem Dokumentationssytem
Beispielhafte Tips für Win NT
Besondere bsh-Vorteile

^

In vielen Lehrbüchern, die Programmiersprachen beschreiben, wird ein Einführungsbeispiel gegeben, 
das zeigt, wie man den Satz "Hallo Welt!" zum Bildschirm schreiben kann: 
    echo Hallo Welt!
Das war's. 
Dieses Beispiel funktioniert, genau so wie gezeigt, mit nahezu sämtlichen Shell-Programmen. 

Dieses echo-Kommando und andere können in die Kommandozeile einer interaktiven Shell (am Prompt) 
als auch in eine Datei geschrieben werden. 

Wenn es sich hier um das echo-Kommando von command.com handelte, wäre die Beschreibung hierzu 
schon fast komplett. Das echo-Kommando der bsh (bsh.exe) kann allerdings sehr viel mehr. 

Wie die Shell ihre Eingabe (z.B. Script-Dateien) liest - interpretiert:

Über Zwischenraumzeichen (Leerzeichen, Tab, Zeilenvorschub) liest die Shell hinweg, 
bis ein anderes Zeichen kommt. 
Dieses andere Zeichen ist dann der Beginn eines Kommandos, 
genauer: das erste Argument eines Kommandos, der Kommando-Name. 
Die Shell liest weiter, bis Zwischenraumzeichen oder andere Zeichen mit Spezialbedeutung kommen. 
Hierdurch wird das Ende des ersten Arguments erkannt. 
Solange die Zwischenraumzeichen nur Leerzeichen oder Tab sind, werden so die Argumente 
eines Kommandos erkannt und gesammelt: 
    kdoname  wort wort    wort ...
    kdo arg ; kdo;kdo arg  arg  ;   kdo
Ein Zeilenvorschub oder ein Semikolon (;) beenden das Sammeln von Kommandoargumenten,
das Kommando mit seinen Argumenten wird dann ausgeführt (execute).
Danach liest die Shell erneut wie oben beschrieben, auf der Suche nach einem weiteren Kommando. 

Es gibt noch andere Zeichen, die ebenfalls ein Kommando beenden. 
Diese Zeichen haben neben dieser Funktion noch eine zusätzliche Bedeutung.
(Beschreibung weiter unten.) 

Auf den Inhalt einer Shell-Variablen wird zugegriffen, indem man dem Variablennamen 
ein Dollar-Zeichen ($) direkt voranstellt: 

    echo aaa   bbb ++$shv--ccc
Falls der Variableninhalt »VVV« ist, werden die drei Argumente 
    aaa bbb ++VVV--ccc
zum Bildschirm geschrieben. 
Man erkennt hier folgendes: 
Für die Shell sind Zwischenraumzeichen nur ein Erkennungsmerkmal für die einzelnen Argumente, 
sie werden danach weggeworfen! 
Die Shell sammelt die Argumente, interpretiert die Argumente bei Spezialzeichen entsprechend, 
ersetzt die Ursprungsargumente beispielsweise (teilweise) durch Variableninhalte
und übergibt dann das Ganze an ein Kommando: 
    »echo«
    »aaa«
    »bbb«
    »++VVV--ccc«
waren hier die Argumente direkt vor Kommandoausführung. 
Variableninhalte und ähnliches werden schon beim Argumentesammeln -sofort- ersetzt. 
Das echo-Kommando fügt zwischen Argumenten jeweils ein Leerzeichen ein; 
desweiteren wird zum Abschluß ein Zeilenvorschub angefügt, den man auch abschalten kann. 

Zwischendurch:
Was ist denn, wenn man nicht  $shv--ccc  schreiben will, sondern  $shvccc  ? 
Der Variablenname ist ja  shv  , dann jedoch shvccc  !? 
Dafür gibt es die folgende Lösung: 

    ++${shv}ccc
wodurch der Variablenname vom nachfolgenden Text isoliert wird. 
Normalerweise ergibt sich diese Abgrenzung, wenn irgendwelche Zeichen folgen, die innerhalb eines 
Variablennamens nicht erlaubt sind! 
Erlaubt bei Namen sind:   a...zA...Z_0...9
0...9 nicht als erstes Zeichen!  Länge maximal 31 Zeichen.

Variablen werden gesetzt und dabei nötigenfalls erzeugt durch:

    shv=VVV
    a=aaa   b=BBB c=abc
    dv=  ev=''  fv=""
wobei kein Zwischenraum um das Gleichheitszeichen (=) erlaubt ist. 
Die letzten drei Variablen wurden 'leer' gesetzt. Sie existieren, aber ohne Inhalt. 
Ganz löschen kann man Variablen mit dem unset-Kommando: unset a b c
Nichtexistierende und leere Variablen werden logischerweise durch 'Nichts' ersetzt. 

Shell-Variablen sind keine Umgebungsvariablen, wie sie in der autoexec.bat gesetzt werden, 
sondern sie sind shell-intern! 
Die bsh hat drei Arten von Shell-Variablen. 
Globale Shell-Variablen können mit 'export' zu Umgebungsvariablen gemacht werden. 

Wieder Argumentbildung:
Oben hieß es ja, daß die Shell Zwischenraumzeichen nach Erkennung wegwirft. 
Das ergibt natürlich ein Problem, falls man solche Zeichen in bestimmter Menge irgendwie 
verarbeiten, ausgeben will. 
Diese Probleme werden durch gezielte Maskierung gelöst! 
Das heißt, alle Spezialzeichen können durch Maskierung zu gewöhnlichen Zeichen gemacht werden 
und verlieren dadurch ihre spezielle Bedeutung: 

    echo "  aaa   bbb${shv}ccc"
      aaa   bbbVVVccc
    echo " aaa   bbb%${shv}c%"cc"
     aaa   bbb${shv}c"cc
Das erste Beispiel zeigt eine Maskierung aller Leerzeichen innerhalb der Einfassung  "..."
Zwischen zwei Doppelapostroph haben nur noch die Zeichen  $ % "  spezielle Bedeutung, 
wobei ein  "  ohne Maskierung ja diese Einfassung beenden würde, und das Prozentzeichen (%) 
ist das General-Einzel-Maskierzeichen, das jedes nachfolgende Spezialzeichen maskiert. 
Das zweite Beispiel enthält zusätzlich  %$  und  %"  , die Wirkung sieht man an der Ausgabe.

Aus historischen Gründen hat auch  `...`  noch Spezialbedeutung (Backquote, linksgeneigtes Apostroph), 
aktuell ist jedoch stattdessen:  $(...)  . 

Die dritte Maskierungsmöglichkeit ist eine Einfassung durch zwei Einzelapostroph:  '...'
Dazwischen hat nur noch das Apostroph selbst Spezialbedeutung, die man dann auch 
wieder aufheben kann, indem man zwei solche hintereinander stellt: 

    echo ' aaa"bbb$ccc%ddd`eee''fff'
     aaa"bbb$ccc%ddd`eee'fff
Die drei Maskiermöglichkeiten sind also:  "..."  '...'  %
Erwähnt werden muß noch, daß das Einzel-Maskierzeichen  (%) 
nur entfernt wird, wenn danach ein Zeichen steht, das tatsächlich eine aktuelle Spezialbedeutung hat! 

Als Maskierzeichen kann auch der Backslash (\) gewählt werden, per Option -B oder  set -B. 
+B  wählt wieder '%'. 
Unter DOS kann '\' sehr stören:  echo "C:\\\\DOS\\\\XCOPY.EXE"

Die letzten drei Beispiele gaben dem echo-Kommando nur jeweils ein Argument:

    »echo«
    » aaa   bbb${shv}c"cc«
Man kann natürlich auch mehrere solche Einfassungen an Kommandos übergeben: 
    echo "..." "..."    "..." '...'
    echo '...'" $km Kilometer"'...'
Das letztere Beispiel zeigt ein (1) zusammengesetztes Argument mit Maskierungen verschiedenen Grades. 
Den Sinn erkennt man an dem Variablenzugriff, der ja innerhalb '...' nicht möglich ist. 
    echo % % % ab     cd
       ab cd
Obige Variante zeigt, daß  »   ab«  und  »cd«  gebildet werden. 

Das Prozentzeichen (%) hat ebenfalls spezielle Bedeutung für das echo-Kommando! 
Man beachte:  zuerst macht die Shell ihre Aufgabe (Argumentbildung), dann folgt 
ein Kommando mit seinen eigenen Interpretationen! 

echo  versteht unter anderen die Zeichenfolgen   %t  %n  %r  %e  %0 bis %0377  %%
die in  Tab, Zeilenvorschub, Wagenrücklauf, Escape, jedes beliebige Zeichen des Zeichensatzes 
und in  %  umgewandelt werden. 

    echo %0101%n%tB     # 65, ZV, TAB und 'B'
    A
            B
Die Zahl muß in Oktal-Schreibweise angegeben werden und muß mit '0' beginnen. 
Die Shell entfernt hier '%' nicht, weil '0', 'n' und 't' keine Spezialbedeutung für die Shell haben. 
ist das Kommentarzeichen der Shell, das nicht am Zeilenbeginn stehen muß, wie REM. 

Das Kommando 'print' ist identisch, bis auf die Abweichung, daß es auch übliche Optionsschreibweise 
versteht:  print -rnu ...
wobei -r die Spezialbedeutung des % abschaltet. 
Bei Ausgabe unbekannter Daten kann diese Option sehr wichtig sein, 
denn dabei will man keine zufälligen Umwandlungen haben.

^

Kommando-Substitution:

    echo aaa$( echo bbb )ccc
    aaabbbccc
    echo "aaa$(echo bbb )ccc"
    aaabbb
    ccc
Die Ausgaben von Kommandos, die zwischen  $(...)  stehen, ersetzen eben diesen Ausdruck. 
Es handelt sich um die Einsetzung der Ausgabe von Kommandos. 
Das, was ein Kommando normalerweise auf den Bildschirm ausgibt, wird also in ein Argument eingebaut 
oder bildet ein eigenes Argument! 
Man beachte die Ähnlichkeit mit der Einsetzung des Inhalts von Variablen!
Desweiteren sollte man hier nicht die Wirkung der Maskierung des Zeilenvorschubs übersehen. 

Kommando-Substitution (Ersetzung) ist ein sehr universelles und mächtiges Hilfsmittel. 
Diese Syntax ist übrigens beliebig verschachtelbar! 
Äußere Maskierungen sind innerhalb des Substitutionsausdrucks unwirksam. 
Erst für die eingesetzte Zeichenfolge wird eine äußere Maskierung dann wirksam. 

    newline="$(echo)"
    newline="
    "
speichern einen Zeilenvorschub in die Variable 'newline'. 
Ohne die Maskierung würde die Variable 'leer' gesetzt. 
    newline2="$(echo %n)"
    tab3="$(echo %t%t%t%c)"
    esc=$(echo %e)
Der abschließende Zeilenvorschub wird durch  %c  unterdrückt. 
Bei ESCape braucht man nicht maskieren, das ist kein Zwischenraumzeichen für die Shell, 
außerdem wird ohne Maskierung gleich der dort unerwünschte Zeilenvorschub weggeworfen. 
    echo aaa.bbb-%c-ccc.ddd ; echo EEE
    aaa.bbb-EEE
Wie man sieht, hat  %c   eine allgemeine Abbruchfunktion!
echo gibt nach dem Erkennen von  %c  überhaupt gar nichts mehr aus. 
Das kann man für weitere Steuerzwecke verwenden. 
    Verz="$( DIR )"
    echo "$Verz%c"
Die Dateiliste des aktuellen Verzeichnisses wird in die Variable 'Verz' gespeichert, 
mit sämtlichen Zeichen, auch den Zeilenvorschüben. 
Nachfolgend wird die Liste mit echo ausgegeben. 
Beides zusammen wirkt so wie einfach:  DIR

Komplett großgeschriebene Kommandos werden automatisch an command.com weitergereicht! 
Bei internen command.com-Kommandos ist das notwendig, bei xcopy z.B. nicht.
system dir /p  bewirkt das gleiche wie  DIR /p.

Bei solchen Aktionen muß man aber aufpassen, daß die Datenmenge nicht zu groß wird! 
Denn in beiden Zeilen wird nämlich die gesamte Zeichenmenge in die Kommandozeile der Shell 
hineinexpandiert, und die Kommandozeile der bsh-DOS-Version darf maximal 4KB lang sein, 
der Variablenspeicher faßt maximal 5KB. 
4 KB sind allerdings mehr als zwei komplett vollgeschriebene Bildschirme:  25x80=2000 ; 4KB=4096 

    echo "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%
          bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb%
    ______cccccccccccccccccccccccccccccccccc%
          dddddd${SHV}dddddddddddddddddddddd%
          eeeeeeeee$(DIR autoexec.bat)eeeeee%
          ffffffffffffffffffffffffffffffffff"
Das ist das echo-Kommando mit einem einzigen recht langen Argument,
das zwei Ersetzungsausdrücke enthält. 
Die Prozentzeichen direkt vor den Zeilenvorschüben bewirken, daß echo jeweils nachfolgend 
Tabs und Leerzeichen (____) ignoriert, wodurch eingerücktes Schreiben möglich wird. 

Dies war eine Funktionalität des echo-Kommandos. 
Aber die Shell kennt etwas ähnliches: 

    prints -ss24s-6s aaaaaaaaaaaa bbbbbbbbbbbbbbbbbbb %
                                  cccc ddddddddddddddd%
    ddddddddddddddddddddddd
Trotz der Zeilenvorschübe (direkt nach dem Prozentzeichen) ist dies ein einziges Kommando, 
denn die Shell ignoriert die unmaskierte Zeichenfolge  '%ZV'  komplett. 
Man beachte den Unterschied zwischen den beiden Anwendungsfällen!
Nochmal:  Es ist so als ob die % jeweils gefolgt vom ZV überhaupt nicht da sind. 

Dieses Hilfsmittel ermöglicht es, sehr lange Argumentketten und auch sehr lange Einzelargumente 
zu schreiben, denn sonst würden sich sehr lange Zeilen ergeben, die sehr unübersichtlich wären 
und auch von keinem Editor längenmäßig verkraftet würden.

^

Zwischenraumzeichen (erweitert) und Wildcards:

    Z="  aaa   bbb   "
    echo $Z ccc
    »echo«
    »aaa bbb«
    »ccc«
    aaa bbb ccc
    echo ${Z}ccc
    aaa bbbccc
    echo "$Z"ccc
      aaa   bbb   ccc
    echo "$Z"    ccc
      aaa   bbb    ccc
Die Shell entfernt sämtliche Zwischenraumzeichen, falls keine Maskierung vorliegt, 
fügt jedoch danach zwischen Worten ein Leerzeichen ein. 
    Verz=$(DIR)
    Verz=`DIR`
Mit dieser Verzeichnisliste kann man wenig anfangen, denn in der Variablen
steht jetzt eine Kette aus lauter Worten, die durch je ein Leerzeichen getrennt sind. 
Alle Zeilenvorschübe und viele Leerzeichen sind weg. 
(Letzteres ist die veraltete Alternativschreibweise. [Syntax==Schreibweise])

Wildcards:

    echo *txt
    FALL.TXT POPE.TXT RAST.TXT
Falls keine Maskierung vorliegt und die Option 'set -f' nicht gesetzt ist, findet Dateinamenexpansion statt. 
Dabei wird auf die Zeichen  * ? [  reagiert, die bei Unix-Wildcards Spezial sind. 
*        Steht für beliebig viele (>=0) beliebige Zeichen
?        Steht für ein beliebiges Zeichen
[...]    Zeichenklasse, steht für ein Zeichen, das darin vorkommt
[!..]    Zeichenklasse, invertiert, ... darin nicht vorkommt
Alle diese Spezials dürfen total beliebig in einem Suchmusterausdruck stehen, 
beliebig häufig und an beliebigen Stellen, und der Punkt gilt als normales Zeichen, 
das heißt, man kann  *  statt  *.*  geben. 
    [-+][0-9]*
    ??*[abf-i]?*
    \t_online\email20\[0-9]??*\adressen.dat
Wie man sieht, arbeitet dieser Mechanismus rekursiv. 
Das letzte Suchmuster liefert sämtliche viergliederigen Pfadnamen, die zu dem Ausdruck passen. 
    V=*txt
    »FALL.TXT POPE.TXT RAST.TXT«
    V="*txt"
    »*txt«
    echo $V
    FALL.TXT POPE.TXT RAST.TXT
    echo "$V"
    *txt
Man erkennt:  wo immer die Shell unmaskierte Wildcards 'sieht', expandiert sie diese! 
Dies ist übrigens ein zweiter Interpretationsschritt:  zuerst sieht die Shell ein Dollar-Zeichen, 
woraufhin sie den Variableninhalt holt, danach untersucht sie diesen Inhalt auf Wildcards, 
und expandiert gegebenenfalls. 
(»...«  markieren den jeweiligen Variableninhalt) 
Das Kommando  'set'  (ohne Argument) zeigt alle Variableninhalte.

Ausdrücke der folgenden Art -mit Wildcards- sollten vermieden werden:

COPY $pdir\bsh\man\'*.*' $zdir\man
Denn hier führt die unmaskierte Variable pdir dazu, daß die Expansion im gesamten Argument 
wirksam bleibt. 
Das verhält sich so, weil die Wildcards erst nach Fertigstellung des kompletten Arguments in diesem 
expandiert werden können - und in der unmaskierten Variablen 'pdir' könnten auch welche sein. 
COPY soll hier die *.* erhalten, nicht die bsh, 
und zwar weil command.com-COPY andernfalls ein zu langes Argument erhalten könnte. 
"$pdir\bsh\man\*.*" $zdir\man
"$pdir"'\bsh\man\*.*' $zdir\man
sind besser, oder set -f. 

Kein normales Kommando unter Unix beherrscht Wildcards, das machen die Shell-Programme 
als Dienstleistung für alle sonstigen Kommandos. 
Die gefundenen passenden Namen werden in die Kommandozeile hinein expandiert, weshalb man auch hier 
auf Limits achten muß: 
Es gibt heutzutage Verzeichnisse, die 20000 Dateien enthalten. Das sprengt bei  *  häufig das Limit 
der Kommandozeilenlänge. 
Aus diesem Grund hat manche Unix-Shell hier ein Limit von 1 MB !!! 

Es gibt aber viele andere Möglichkeiten, um diesen Limitproblemen aus dem Wege zu gehen.

^

Arithmetische und logische Operationen:  Rechnen

    echo aaa$(( 222+333 ))bbb
    aaa555bbb
Hier wurde eine Addition durchgeführt und das Ergebnis in das Argument eingefügt. 
Auch hier wird auf die Ähnlichkeit mit dem Einsetzen eines Variableninhalts hingewiesen. 
Ein arithmetischer Ausdruck  $((...))  wird durch sein Ergebnis ersetzt. 
    echo aaa(( a=222+333 ))bbb
    aaabbb
    echo $a
    555
    echo aaa(( a=222+333 ))${a}bbb
    aaa555bbb
Hier fehlt das Dollar-Zeichen vor der Doppelklammer. 
Ein solcher Ausdruck wird 'stumm' berechnet. Da das Ergebnis aber in der Variablen 'a' steht, 
kann man nachfolgend diese Variable beliebig verwenden. 

Die dritte Arithmetikvariante ist das let-Kommando: 

    let "a=(222*10)/4" a+=33 "b=a*zval, b-=$Fclt" ++a
Vier Argumente sind das, wovon zwei unmaskiert sein dürfen, da sie keine Shell-Spezialzeichen 
enthalten. 
Die Variable 'Fclt' wird von der Shell 'ausgepackt', bevor das let-Kommando zu arbeiten beginnt. 
let  sieht also sogleich eine Konstante. 
Diese Möglichkeit ist beispielsweise wichtig, wenn andere Darstellungsformen für die Zahlen 
vorliegen, also eine andere als die dezimale Schreibweise (Dual, Oktal, Hex, etc.). 

Wer die Programmiersprache 'C' kennt, wird hier Ähnlichkeiten feststellen.
Das ist kein Zufall, denn die bsh beherrscht die gesamte Arithmetik und Logik 
der Sprache C - und noch mehr ! 

Standardmäßig sind die Zahlen in den Variablen als Zeichenketten enthalten, nicht in binärer Form ! 
Deshalb kann man auch folgendes machen: 

    num=123
    num=${num}00
Nach der zweiten Zuweisung steht in der Variablen die Zahl:  12300
Das war also quasi eine Multiplikation mit 100 ! 
Daraus ergibt sich, daß man auch außerhalb von arithmetischen Ausdrücken 
mit Zahlen arbeiten kann: 
man kann zumindest Variablen setzen und zeichenkettenmäßig vergleichen und bearbeiten. 
    (( A=(a<<2)¦(b>>4),
       B=[(b<<4)&255]¦(c>>2),
       C=[(c<<6)&255]¦d
    ))
Arithmetische Ausdrücke dürfen beliebig lang sein, 10000 Zeilen - wenn man will ! 
    let "abk==abj && P&2#1010" && echo Trifft zu.
    ((   abk==abj&&P&2#1010 )) && echo Trifft zu.
Falls abk gleich abj ist und das zweite und/oder vierte Bit in P 1 ist, wird das 
echo-Kommando ausgeführt, andernfalls nicht. 
Diese beiden Arithmetikformen haben einen Exit-Kode für Steuerzwecke.

Die Ausgabe-Zahlenbasis kann temporär folgendermaßen eingestellt werden: 

    echo Hex-Zahl: $((16#, 1234+5678))H
Die Sprache C erkennt drei Zahlenbasen: 
    Dezimal(999), Oktal(0777), Hexadezimal(0xffff)
Die bsh kann sechsunddreißig verschiedene Basen verarbeiten !:
    2# bis 36# und 256#
Sie kann quasi mit Buchstaben rechnen. 

Das interne Kommando 'base' ist in diesem Zusammenhang von großem zusätzlichen Nutzen! 
(siehe Manual bsh(K)) 

Die bsh rechnet intern mit 32/64 Bit breiten, vorzeichenbehafteten Integer-Zahlen.
Für Berechnungen mit Gleitkommazahlen gibt es das externe Kommando 'calc/calc.exe'.


^

bsh64

Die bsh64-Varianten sind 32Bit-Programme, die intern mit 64Bit-Zahlen rechnen und diese auch anderweitig verwenden.
Der Arithmetik/Logik-Modul ist 64-bittig, auch die Kommandos let, sum, base, test, catv, seek, fstat .
(Bei bsh64.exe die letzten drei Kommandos jedoch nicht.)

Die Variante für SCO-UnixWare ist praktisch vollständig 64-bittig.

1#  echo $(( ~0>>1 ))
9223372036854775807
2#  echo $(( ~(~0>>1) ))
-9223372036854775808
3#  echo $((36#, ~0>>1 ))
1y2p0ij32e8e7
4#  echo $(( 36#zzzzzzzzzzzz ))
4738381338321616895
5#  echo $(( 256#zzzzzzzz ))
8825501086245354106
6#  sum -l < /home2/media/ms/sp6i386.exe
19237147082760996
Das sum-Kommando braucht nur etwa 1/4 Sekunde, um alle longs des 35MB-Servicepacks aufzuaddieren.
Man beachte, daß hier noch lange kein Überlauf passieren kann - das erst bei 16GB(!) Dateigröße.
Das überlauflose Summieren von 32Bit-Werten in einer unsigned-64Bit-Variablen ist als Prüfsumme erheblich
sicherer als modulo 256 !
Jedenfalls bedeutend schneller als CRC-Summen zu bilden.

Mit solch einem Integer-Zahlenbereich hat man wirklich 'ausgesorgt'.
Mit der ersten obenstehenden Zahl könnte man knapp 10 Millionen Tera-Byte adressieren !
.

^

Der Exit-Kode von Kommandos:

Jedes Kommando innerhalb der bsh, die bsh selbst und alle zugehörigen externen Kommandos 
geben beim Beenden einen definierten Exit-Kode (auch Status genannt) an den Aufrufer zurück, 
den man ignorieren aber auch verwerten kann. 
Das ist eine Zahl zwischen 0 und 127. 
Die vordefinierte Shell-Variable  $?  enthält stets den Exit-Kode des zuletzt ausgeführten Kommandos: 
    echo $?
    1
    echo $?
    0
Hier stammt die 1 von irgendeinem vorherigen Kommando 
und die 0 vom ersten echo-Kommando. 

Traditionell hat eine Rückgabezahl von 0 den Status TRUE (Wahr).
Das ist fast immer so, wenn ein Kommando seine Arbeit ordnungsgemäß verrichten konnte, 
ohne daß irgendwelche Fehler auftraten. 
Alle Rückgabezahlen ungleich 0 haben den Status FALSE (Falsch/Unwahr).

Die Shell benutzt dieses TRUE/FALSE für Steuerzwecke. 
Weiter oben stand stand schon mal  &&  vor einem echo-Kommando. 
Das war ein solcher Steuerzweck. 

Ein Kommando hinter  &&  wird nur ausgeführt, falls der Status des davor stehenden Kommandos 
TRUE ist. 
Ein Kommando hinter  ¦¦  wird nur ausgeführt, falls der Status des davor stehenden Kommandos 
FALSE ist. 

Diese beiden Zeichenfolgen beenden -wie Semikolon und Zeilenvorschub- das Sammeln von 
Kommandoargumenten, haben aber eben diese zusätzliche Steuerfunktion.

Es können beliebig lange Status-Kommando-Ketten gebildet werden:

    test $k -gt 123 && kdoA ¦¦ kdoB && kdoC &&
                       kdoD && kdoE ¦¦ echo Fehler:kdoE
Die Kommandos werden der Reihe nach nur ausgeführt, solange der Exit-Kode zu dem jeweiligen 
&&  oder  ¦¦  paßt.
Sobald es einmal nicht paßt, wird die gesamte restliche Kette abgebrochen.
Falls der Zahlenwert in der Variablen 'k' nicht größer als 123 ist, wird nach dem test-Kommando 
kein weiteres Kommando mehr ausgeführt. 

Man beachte, daß nach solch einem Zeichendoppel die Status-Kette in einer neuen Zeile 
fortgesetzt werden kann. 
Dies war nur eine einzige, über zwei Zeilen verteilte Status-Kette ! 

Es gibt auch eine ODER-Status-Kette mit 
Siehe auch Kommando 'inv'.

^

Das test-Kommando:

Dieses Kommando hat drei verschiedene Schreibweisen: 
    test ...
    [ ... ]
    [[ ... ]]
Das Kommando ist mittels der drei Kommandonamen  test, [ und [[  aufrufbar. 
Bei der Klammer-Syntax müssen die letzten Argumente  ]  bzw.  ]]  lauten. 

Dieses Kommando kann eine sehr große Anzahl von verschiedenartigsten Gegebenheiten testen. 
(siehe Manual test(K)) 
Je nachdem, ob eine getestete Bedingung zutrifft oder nicht zutrifft, wird ein Exit-Kode 
TRUE (0) oder FALSE (1) zurückgeliefert. 
Das test-Kommando wird fast immer von einer Zeichenfolge  &&  oder  ¦¦  begleitet: 

[ -e $name ] && echo $name existiert
[ -f $name ] && echo $name existiert und ist Datei
[ -d $name ] && echo $name exist. und ist Verzeichnis
[ -s $name ] && echo $name exist. Datei, nicht leer
[ -w $name ] && echo $name exist. Datei, beschreibbar
[ -x $name ] && echo $name exist. Datei, ausführbar
[ ! -x $name ] ¦¦ echo $name exist. Datei, ausführbar
Die beiden letzten Beispiele sind wegen doppelter Invertierung identisch.
Falls es vorkommen kann, daß eine Variable leer ist oder Wildcards enthält, sollte sie 
vorbeugend maskiert werden: 
    [ -f "$name" ] ...
Denn, eine leere Variable ergibt bei  $var  'Nichts', "$var"  jedoch ergibt wenigstens  "",
ein leeres Argument: 
    [ "$str" == packed ] && unzip ...
    [[ "$flags" != ??1? ]] && continue
Bei der Doppelklammerschreibweise werden bei Zeichenkettenvergleichen
rechts Wildcards berücksichtigt. 
Bei dieser Aufrufschreibweise gilt auch in anderer Hinsicht eine andere Syntax. 
    [ $# -gt 3 ] ...
    [ ${#sbk} -gt 6 ] ...
    [ $sbk -le 64 ] ...
    [  sbk -le 64 ] ...
Das waren zahlenmäßige Vergleiche. 
Es gelten die Operatoren:  -lt  -le  -eq  -ne  -ge  -gt
Beim zweiten Beispiel wurde die Inhaltslänge der Variablen sbk verglichen.
Variablen werden hierbei vom test-Kommando selbst ausgepackt, 
ein Dollar-Zeichen zum Zugriff durch die Shell ist nicht nötig. 
(Das ist nur möglich, weil test ein internes Kommando ist.) 
Das modernere let-Kommando mit der C-Syntax ist hier natürlich überlegen.

Die einzelnen Bedingungen lassen sich mit logisch UND und ODER verknüpfen,
wobei UND Vorrang hat, weshalb es auch Klammerung gibt: 

    [ -s $name -a %( "$k" == ab -o "$k" == AB ')' ] ...
Die Klammern müssen maskiert werden, da sie auch ohne $ davor für die Shell 
Spezialbedeutung haben (Subshell).

^

Spezialitäten

Der Inhalt einer (unmaskierten) Variablen kann als Kommando oder Teil davon aufgerufen werden: 
    Echo="echo abc ABC"
    $Echo
    abc ABC
Das ist mit der alias-Einrichtung fast gleich: 
    alias Echo="echo abc ABC"
    Echo
    abc ABC
Bei Aliases wird das Dollar-Zeichen weggelassen und es dürfen im Aliasnamen auch andere Zeichen 
verwendet werden als für Variablennamen erlaubt sind. 
Die alias-Funktionalität ist relativ einfach gehalten, für stark erweiterte Möglichkeiten sind die 
frei definierbaren Shell-Funktionen vorgesehen. 
    $( echo echo eeeee)
    eeeee
    $( echo eeeee )
    bsh: Kommando nicht gefunden: 'eeeee'
Dieser indirekte Kommandoaufruf per Kommando-Substitution dürfte klar sein. 
    $( grep "#@xyz" kdo_datei )
Man stelle sich vor, eine Kommandodatei enthalte Zeilen mit speziellen Kennungen 
am Zeilenende und mit (einem) Kommando(s) davor. 
grep sucht die entsprechende(n) Zeile(n) und schreibt sie aus. 
Durch die Kommando-Substitution wird das Kommando ausgeführt, die Kennung jedoch 
nicht, da sie dasKommentarzeichen (#) zu Beginn enthält. 
xyz kann natürlich durch $1 ersetzt werden - innerhalb einer Shell-Funktion.
Mit dieser Konstruktion könnte man beliebig viele Quasi-Aliases definieren.

^

Operationen mit Dateien und Geräten:

Wenn ein externes Kommando/Programm gestartet wird, sind (intern) bereits standardmäßig 
3 oder 5 Geräte (Unix/Dos) geöffnet, denen die Erkennungsnummern  0, 1, 2, 3, 4  zugeordnet sind. 
Diese Nummern sind die sogenannten Handles/file descriptors. 
Innerhalb der bsh können die Handles  0 bis 9  beliebig verwendet und umgelenkt/verknüpft werden. 
Desweiteren gibt es als Novität bei Unix-Shells globale/dauerhafte Verknüpfungen. 

Die vorverknüpften Handles: 

0   Standardeingabe;       ohne Umlenkung: Tastatur
1   Standardausgabe;       ohne Umlenkung: Bildschirm
2   Standardfehlerausgabe; ohne Umlenkung: Bildschirm
Das echo-Kommando beispielsweise schreibt auf Handle 1, 
read liest von Handle 0, und Fehlermeldungen gehen auf Handle 2. 
Manche Kommandos (z.B. tr) lesen von Handle 0 und schreiben auf 1 - gleichzeitig.
    echo aaa > PRN
    > PRN
    echo aaa
    ><
In beiden Fällen wird Handle 1 zum Drucker umverknüpft. 
Beim ersten Beispiel wird nach Kommandoende automatisch zum Bildschirm zurückverknüpft, 
beim zweiten Beispiel muß man das durch  ><  machen, was Sinn ergibt, wenn mit mehreren 
Kommandoaufrufen zum gleichen Ziel geschrieben werden soll. (globale Umlenkung)
    echo ddd > datei
    echo eee >> datei
    tr ab ba < eingabedatei > ausgabedatei
Zuerst wird 'datei' erzeugt oder aber auf Größe 0 gebracht, dann gelangt 'dddZV' in sie hinein. 
Zum Zweiten werden Daten an 'datei' angehängt. 
Mit  >>  wird stets an das Dateiende geschrieben, nichtexistierende Dateien würden zuvor erzeugt. 
Ohne Umlenkungen würde 'tr' eine Eingabe von der Tastatur erwarten und zum Bildschirm schreiben. 
'tr' als Filterkommando wird auch häufig in einer sogenannten Pipeline (¦) eingesetzt. 
    > datei1
    6> datei2
    < idatei
    while readl izeile
    do
       echo 11111
       print -ru6 222:$izeile

       < iidatei
       while readl iizeile
       do
          echo 111:$iizeile
       done
       ><
    done
    ><<<
Hier ist eine while-Schleife zu sehen, mit einer weiteren darin verschachtelten.
Zu Anfang werden drei globale Umlenkungen etabliert, die zum Schluß rückverknüpft werden. 
Man kann die Handles  0-9  vor die Umlenkungszeichen stellen.
Impliziter Standard ist:  0<  1>  0<>
Das echo-Kommando schreibt zu datei1, print mittels Option '-u6' zu datei2.
readl liest stets von Handle 0, aus idatei, jedoch bei der inneren Schleife wird Handle 0 zeitweise 
mit einer neuen Verknüpfung belegt, die die alte -zeitweise- überdeckt.

Allerdings überdecken ja bereits die drei ersten globalen Umlenkungen die Standardverknüpfungen 
mit Tastatur und Bildschirm. (Handle 6 war aber unverknüpft.) 
Diese Konstruktion liest jeweils eine Zeile aus idatei und jedesmal alle Zeilen aus iidatei. 
Die while-Schleife wird an anderer Stelle erklärt.

<>  öffnet eine Datei zum Lesen und Schreiben.  Beim Öffnen wird die Dateigröße nicht verändert.


    3<&0
    3>&1
    print -u3 abc
    ><
    print -u3 abc 3>&1

    catv $Fpos,,0 <$idatei ¦ {
       seek + 0 pos
       while readl zeile
       do
          expr "$zeile" :: '^#@F ' ¦¦ continue
          let "++n%20==0" && {
             read -u3 "inp?e¦<Enter> "
             # ...
             seek $pos 0 ; continue
          }
       done
    }
    ><
Hier wird Handle 3 mit dem Gerät verknüpft, mit dem Handle 0 verknüpft ist - mit der Tastatur.
Direkt danach mit dem Gerät, mit dem 1 verknüpft ist.
Beide 'print' schreiben per 3 zum Bildschirm, einmal mit globaler Verknüpfung, danach mit temporärer
Verknüpfung.
Von 'catv' bis '}' gilt trotzdem '3<&0', diese erste Verknüpfung wurde nur 2mal zeitweise überdeckt.

catv  liest per 0 aus $idatei, bei  '¦'  ist diese Verknüpfung wieder weg.
catv  schreibt per 1 in die interne Pipe-Datei, danach ist 1 wieder mit dem Bildschirm verknüpft,
und dann ist Handle 0 zum Lesen mit den Pipe-Daten verbunden, und zwar von '{' bis '}'.
Die Start-Position der Pipe-Daten wird mit 'seek' in 'pos' abgesichert.
Dann liest 'readl' per Handle 0 Zeile für Zeile aus der Pipe-Datei und bewegt jedesmal den Dateizeiger
auf den Anfang der jeweils nächsten Zeile.

'read -u3 ...' will nun eine Antwort haben und liest per 3 von der Tastatur!
 Das geht wegen '3<&0', und weil bei '3<&0' Handle 0 noch mit der Tastatur verknüpft war.
'read' ohne '-u3' würde ja per 0 aus der Pipe-Datei lesen!
Je nach Antwort wird mit 'seek $pos 0' auf den Start der Pipe-Daten zurückgestellt und alles erneut gelesen.
Ohne '3<&0' hätte man 'read inp ... t<CON' oder unter Unix 'read inp ... </dev/console' verwenden müssen,
was ja offenbar nicht portabel ist.

Bei  '4<datei.txt ; 5<&4'  teilen sich die Handles 4 und 5 einen Dateizeiger (seek).
Bei  '4<datei.txt ; 5<datei.txt'  hat jeder Handle seinen eigenen, unabhängigen Dateizeiger.

Diese Beschreibung wirkt kompliziert, aber dennoch ist die Angelegenheit einfach.
An der Syntax selbst kann man die vorliegenden Verknüpfungen besser erkennen als per Beschreibung.

Man muß sich jeweils nur fragen:  Was ist jetzt gerade mit Handle X verknüpft?
Und man muß wissen, daß die Operatoren  < > |  Verknüpfungen vornehmen,
und mit welchen Handles die verschiedenen Kommandos lesen und schreiben (können).


    echo %c > datei
    : > datei
Beide Beispiele erzeugen eine leere Datei. 
Der Doppelpunkt (:) ist ein Kommandoname! 
':' ist ein Leerkommando, das nur einen TRUE-Exit erzeugt -sonst nichts. Argumente werden ignoriert. 
Achtung, ohne Kommando vor den Umlenkungszeichen ergeben sich dauerhafte Verknüpfungen! 
Siehe auch Kommando  'mktemp'. 
    kdo 2>NUL
    kdo 2>/dev/null
Das ist eine Möglichkeit, Fehlerausgaben, die man nicht sehen will, 'in die Wüste' zu schicken. 
Es verbleibt dann noch der Exit-Kode zur Auswertung - beispielsweise für eigene Fehlermeldungen. 

Auch die DOS-Versionen der bsh akzeptieren die unter Unix üblichen Schrägstriche '/', 
um die Namen in einem Pfadnamen voneinander zu trennen: 

    < c:/bsh/doc/bsh.mnk
Das geht aber nur in Verbindung mit bsh-internen Einrichtungen! 
Argumente zu externen Kommandos beispielsweise werden nicht umgewandelt,
da die Shell dort nicht wissen kann, ob ein Pfad als Zeichenkette gemeint ist oder ob damit 
tatsächlich eine Datei geöffnet werden soll.

^

Pipelines

Die Funktion einer Pipeline läßt sich folgendermaßen nachbilden: 
    echo aaxua > datei
    tr xu ux < datei
    echo aaxua ¦ tr xu ux
Das zweite Beispiel zeigt die 'richtige' Pipeline, bei der manuell erzeugte Dateien nicht nötig sind. 
Das, was echo ausgibt (Handle 1), gelangt in die Pipeline, und tr liest es mit Handle 0 aus der Pipeline. 
    echo axaua ¦ tr xu ux ¦ wc
Dieses ist eine mehrstufige Pipeline, bei der ein Kommando (tr) gleichzeitig
aus einer Pipeline liest und in eine schreibt. 
Um dies nachzubilden, müßte man schon mit zwei Dateien arbeiten.

Es können Pipelines mit beliebig vielen Stufen gebildet werden.

    [ ax -gt 0 ] && echo axaua ¦ tr xu ux ¦ wc
Pipelines können fast beliebig mit der anderen Syntax kombiniert werden. 
In diesem Sinne ist eine Pipeline(kette) als ein einzelnes Kommando anzusehen.

Auch Shell-Funktionen können Bestandteil einer Pipe-Kette sein.

    wc datei ¦ read l w c
Hier wurden die Variablen  l, w, c  mit der Anzahl Zeilen, Worte und Zeichen in 'datei' 
auf einen Schlag gesetzt. 

Das Pipe-Zeichen  ¦  beendet -wie Semikolon und Zeilenvorschub- das Sammeln von 
Kommandoargumenten, hat aber eben diese zusätzliche Pipe-Funktion.

^

Variablen:  Arrays, Indirekter Zugriff, Lokal, Lokal-Statisch, ...

Arrays können erzeugt werden mit den Kommandos  set -A , array  und direkt durch Zuweisung: 
    i=77
    a[22]=AAA
    let "b[33]=333, c[ 44+6]=5050, d[i]=777"
Mit set und array können mehrere Elemente auf einmal behandelt werden. 
Achtung, es werden 'normale' Shell-Variablen namens  a22, b33, c50, d77  erzeugt! 
Das ist nicht absolut ideal, aber wenn man's weiß, nicht tragisch - auch wegen lokaler und 
vieler anderer Möglichkeiten, die die bsh bietet. 
Arrays werden in der Shell-Programmierung recht selten gebraucht, auch deshalb diese Vereinfachung. 

Das Kommando 'set' listet alle existenten Shell-Variablen mit ihren Inhalten. 
Das Kommando 'env' listet alle Umgebungsvariablen (Environment). 

    echo $a22  ${a[22]}  "$a[22] : $d[i]"
    let "y+= b[ k+1 ]-d[2]"
Hier wurde auf Array-Inhalte zugegriffen, auch mit indexierenden Ausdrücken.
Achtung,  $a[22]  ist etwas anderes als  ${a}[22] !
Für Details siehe Manual bsh(K). 
    shv="aaa bbb ccc ddd"
Die Variable 'shv' wird dadurch zu einem Quasi-Array! 
Auf die abgetrennten Zeichenfolgen kann einzeln zugegriffen werden, auf vielfältige 
Art und Weise - nur eben nicht direkt mittels  shv[index]

Indirekter Zugriff:

    a=b b=c c=ddd
    echo ${{{a}
    ddd
Hier wurde zweifach indirekt über 'a' und 'b' auf den Inhalt von 'c' zugegriffen. 

Lokale Variablen:

    local a=aaa b=bbb c d
    static s t u=UU
Innerhalb von Shell-Funktionen -und in fast allen anderen Umgebungen- können 
mit dem local-Kommando lokale Variablen angelegt werden, die nach Verlassen der Funktion 
nicht mehr existieren. 
Ebenso können static-Variablen erzeugt werden, die vom (internen) Namen her lokal sind, jedoch 
bei einem Wiederanlaufen einer Funktion mit dem alten Inhalt noch da sind.

In der FreeLizenz-Version der bsh können relativ wenige globale (ca.20) und jeweils 'nur' 6 lokale 
Variablen bzw. 'Namen' angelegt werden. 

Vordefinierte Shell-Variablen:

Wie innerhalb von DOS-BAT-Dateien  %0 bis %9  gibt es in einer unix-mäßigen Shell wie der bsh 
selbstverständlich ebenfalls 'positionale Parameter', mit denen auf
Shell-, Script- und Funktionsargumente zugegriffen werden kann: 
    echo $0 $# $3 $9 ${100} $* $@
Wie man sieht, liegt hier die Grenze nicht bei '9'. 
Die Anzahl der Argumente (außer Arg0) liefert  $# .
Die beiden letzten liefern alle Argumente ab Arg1. 
Weitere vordefinierte Variablen sind unter anderen:  PWD, OLDPWD, SECONDS, RANDOM 
Siehe auch Kommando 'shift'.

^

Programmier-Kommandos (Steuerung des Ablaufs)

Variablen-if:

    ifdef shv && echo Variable %'shv%' existiert
    ifset a b c && echo a b und c exist. und sind nicht leer
    unset shv b
Obiges bedarf wohl keiner weiteren Erklärung. 

Kommando-Gruppierung:

    ifset k && { kdo1; kdo2; kdo3; } &&
               echo "Alles wegen 'k' korrekt ausgeführt!"
    ifset j ¦¦ {
       print -u2 "Fehler: 'j' nicht gesetzt!"
       kdo ; kdo
       kdo && { kdo &¦ kdo;}
       echo So allerlei gemacht. ; exit 2
    }
Hier wurden Kommandos zusammengefaßt, jeweils von einer Bedingung abhängig gemacht. 
Die geschweiften Klammern  { ...; }  sind quasi Kommandos, weshalb sie auch 
wie ein Kommandoname gesetzt werden müssen. 
Der Exit-Kode nur von kdo3 ist oben ausschlaggebend für 'echo'. 
Hier ist auch eine Exit-ODER-Kette mit    zu sehen. 
Siehe auch Kommando 'inv', zur Invertierung des Exit-Kodes. 

if-Anweisung:

    if [ $a == Kern ]
    then
       echo Der Kern ist da.
    elif [ $a == Schale ]
    then
       echo Die Schale ist da.
    else
       while let "++xx<20"
       do  echo Weder Kern noch Schale!; done
    fi
Es sind beliebig viele 'elif' möglich. 
»if [ $a == Kern ] ; then«  ist auch erlaubt. 
In den Abteilungen darf jeweils praktisch alles in beliebiger Menge und Verschachtelung stehen! 
Das gilt auch für die Bedingungen zwischen if und then und while und do, etc.! 
Generell gilt das für alle solche Syntax-Konstruktionen der bsh! 

Fallunterscheidung:

    case "$a" in
            [kK]ern)  echo Kern oder kern ist da. ;;
      Schale¦schale)  echo Schale oder schale ist da. ;;
                  *)  echo Leider nichts da!
                      echo "Aber vielleicht kommt's"
                      echo "ja noch ?" ;;
    esac
Der Inhalt von 'a' wird mit den Fall-Mustern) der Reihe nach verglichen.
Wie zu sehen ist, werden hier Wildcards als auch ODER (¦) verarbeitet.
Listenabschluß  ;;  nicht vergessen! 

while/until-Schleife:

    while :
    do
       echo Wann ist denn endlich Schluß ?!
       [ nloop -lt 100 ] && echo Noch nicht.
       [ nloop -ge 100 ] && { echo Jetzt ist Schluß!; break;}
       let ++nloop
    done
Die Schleifenbedingung steht zwischen while und do. 
Hier ist es ein Kommando, das immerfort TRUE-Exit gibt, weshalb die Schleife ewig laufen würde, 
wenn nicht im Schleifenkörper ein Abbruch programmiert worden wäre.
Die Schleife geht in den Schleifenkörper -zwischen do und done- hinein,
solange ihre Bedingung TRUE liefert. 
Bei der until-Schleife ist es genau umgekehrt:  solange FALSE geliefert wird. 

Die verschiedenen Syntax-Konstruktionen verarbeiten die Kommandos: 

    break n
    continue n
    goend n
    return e
zum Abbruch, Fortsetzen bei Bedingung, Abbruch und Verlassen einer Shell-Funktion.
Für 'n' ist 1 voreingestellt.  Bei größeren Werten werden entsprechend viele Verschachtelungen 
auf einen Schlag überbrückt. 

for-Schleife1:

    echo      aaa bbb ccc $b ddd $( kdo) eee *.txt $*
    for a in  aaa bbb ccc $b ddd $( kdo) eee *.txt $*
    do
       echo $a
    done
Das echo-Kommando soll deutlich machen, wie die Schleife funktioniert:
Alle Argumente, die die Shell hier bildet, werden nacheinander in die Laufvariable 'a' gespeichert 
und der Schleifenkörper wird mit dem jeweiligen Inhalt durchlaufen - bis kein Arg mehr da ist. 

for-Schleife2:

    for 3 n m M  in  1 Jan Januar 2 Feb Februar ...
    do
       echo "$n: kurz=$m lang=$M"
    done
    for 3 n j k  in  $( funktion )
    do  ...;  done
Hier werden für jeden Durchlauf die drei angegebenen Variablen auf einmal gesetzt. 
Das ist für Tabellen und ähnliches sehr nützlich! 
Man kann das auch mit den sogenannten 'Strukturen' von Compiler-Sprachen vergleichen. 
    < datei
    while read n m M -
    do
       echo $n:$m:$M
    done
    ><
Zum zeilenweisen Lesen aus einer tabellarischen Datei bietet sich obige Variante an. 
read  erledigt die Zerlegung in Worte und die Variablenzuweisung.
Es können auch Auslassungszeichen '-' gegeben werden, um keine Variable zu 'verschwenden'. 
Ohne '-' würden zwei Worte in 'M' gelangen, weil hier vier Worte pro Zeile angenommen werden. 

for-Schleife3:

    for lv from 2 by 2 to 64 repeat
    do
       echo $lv
    done
    to 10 repeat; do  echo 10mal mach ich es.; done
    repeat; do  echo ewig mach ich es.; done
Dieses ist eine Zahlen-Schleife, die mit einer Zahl durchläuft, von einem Anfangswert bis zu 
einem Endwert einschließlich, deren jeweilige Erhöhung oder Verminderung angebbar ist. 
Man kann alles -beliebig gemischt- weglassen, bis auf das Argument 'repeat'.

^

Shell-Funktionen

Nachdem eine Shell-Funktion definiert worden ist, kann sie genau so wie ein internes 
oder externes Kommando eingesetzt werden: 

Also, Aufruf mit dem Funktionsnamen, mit oder ohne Argumente, Umlenkungen dazu, 
Kommando-Substitution, in eine Pipeline einfügen, etc. 
Funktionen haben auch einen Exit-Kode. (return e) 

Shell-Funktionen sind bis Shell-exit bekannt und ausführbar, sobald sie von der Shell gesehen 
und 'gelernt' wurden. 
Funktionen können selbstverständlich bei jedem Shell-Start automatisch geladen werden. 

Definitionen:

    kurz_funktion() { kdo; ...; }
    lang_funktion() {
       Beliebig viele
       beliebige
       Kommandos
       ifset vf && verschachtelt()) { ...; )}
       return 0
    }
Verschachtelung mit der zusätzlichen Klammer ) bedeutet, daß man innerhalb einer Funktion 
eine andere definieren kann. 
Desweiteren kann damit bei nicht verschachtelten Funktionen ein unbeabsichtigter
Funktionsabschluß  »ZV}«  maskiert werden.
Eine verschachtelte Funktion kann gezielt 'gelernt' werden, und auch wieder mit 'unset -f' 
auf 'unbekannt' gesetzt werden. 
Zur Unterscheidung sind beliebig viele zusätzliche Klammern ) angebbar.
    stop() { read "-?<Enter> "; }
    stop
Dies ist eine kleine Stop-Funktion:  man muß Enter drücken, damit's weiter geht. 
Der Aufruf wurde in der zweiten Zeile gezeigt. 
Eine solch kleine Funktionalität könnte man noch mit einem alias alternativ erzeugen, 
die nachfolgende Funktion aber nicht mehr: 
    frage()  {
       local inp
       while echo "%n$1  [jn]:  %c"
       do
         read inp
         case "$inp" in
           [jJ])  return 0 ;;
           [nN])  return 1 ;;
              *)  continue ;;
         esac
       done
       return 2
    }
    frage "Abbruch gewünscht?" && exit
Dies ist eine praktische Abfrage-Funktion. 
Trotz der geringen Größe zeigt diese Funktion viele verschiedene Programmiermerkmale. 
$1  setzt den Fragetext ein. 
return 2  wird nur erreicht bei Systemfehler innerhalb von echo. 

Shell-Funktionen dürfen beliebig groß sein und 'alles' enthalten.
Funktionen dürfen sich auch selbst -rekursiv- aufrufen. 
Unter DOS ist aber nach etwa 15 verschachtelten Aufrufen der Stack-Speicher erschöpft. 

Die bsh-FreeLizenz-Version kann nur maximal 8 Funktionen berücksichtigen.
Dieses Limit kann man umgehen durch Verwendung eines 'case'.

^

Reguläre Ausdrücke (Kommandos: grep, expr)

Reguläre Ausdrücke (regular expressions) können als Super-Wildcards bezeichnet werden. 
Siehe auch  c't  10/1993  S.218   (Der beste Fachz.schr.-Artikel, den ich dazu je sah.)
Zwei (standardähnliche) Ausführungen gibt es:  Basic-RAs und Extended-RAs 
Zum Suchen sind die ERAs besser, für Universalzwecke sind die BRAs besser. 

Die bsh hat die BRAs eingebaut:

    .        steht für ein beliebiges Zeichen
    .*       beliebig viele beliebige Zeichen
    ..*      mindestens ein beliebiges Zeichen
    a        ein a
    a*       beliebig viele a   (auch gar keins!)
    aa*      mindestens ein a
    a%{1,%}  mindestens ein a
    [xyz]    eins der enthaltenen Zeichen (Zeichenklasse)
    [^xyz]   eins der nicht enth. Zeichen (Z.klasse)
    [a-z]    eins der enthaltenen Zeichen (von a bis z)
    %<       Paßt zum Anfang eines Wortes
    %>       Paßt zum Ende eines Wortes
             Wort-Zeichenmenge:  [a-zA-Z_0-9]

    %A       Paßt zu  A-Za-z
    %L       Paßt zu  a-z
    %U       Paßt zu  A-Z
    %D       Paßt zu  0-9
    %W       Paßt zu  A-Za-z_0-9
    %!       Invertiert -vorangestellt- die vorstehenden Zeichenmengen.
             Die Häufigkeit kann mit  *  und  %{a,b%}  (nachfolgend)
             festgelegt werden.

    ^        Justiert auf den Anfang, am Anfang stehend
    $        Justiert auf das Ende, am Ende stehend
    %(   %)  Stanzt Zeichenbereiche aus (Bezugsbereiche)
    %1...%9  Beziehen sich auf die Bezugsklammerpaare
    %{a,b%}  Steht hinter einem Zeichen oder Z.klasse
             und gibt Min- und Max-Anzahl an.
    %{n%}    Genau n Zeichen      (n=0...255)
    %{n,%}   Mindestens n Zeichen
    %        Maskierzeichen, wie bei der Shell
    %t%r%n%z Setzen die Zeichen TAB, CR, NL, ^Z ein
    Achtung, die Zeichenpaare %( %) %{ %} %< %> sind speziell,
    sie wurden nicht maskiert,
    damit ( ) { } < > ganz gewöhnliche Zeichen sind!
    %( %)  können auch verschachtelt werden.
    Bei []]    oder [^]]   ist ']' ein gewöhnliches Zeichen!
    Bei [-...] oder [...-] ist '-' ein gewöhnliches Zeichen!
    ^ und $ sind nur am Anfang bzw. am Ende speziell!
    (Maskierung durch Position)
Reguläre Ausdrücke wirken sehr kompliziert, wenn man sich fertige Ausdrücke anschaut. 
Eigentlich sind sie aber recht einfach, wenn man die Einzelregeln kennt und 
einen solchen Ausdruck schrittweise, Zug um Zug aufbaut. 

Eine ganz wichtige Verhaltensweise des Algorithmus ist, daß er von links nach rechts 
so viele passende Zeichen 'einsammelt' wie es möglich ist. 
Der Algorithmus wird manchmal schon weiter gelesen haben als man denkt,
beim Schreiben eines RA. 
Die Ausstanzklammern haben auf den Suchvorgang keinen Einfluß. 
Eine Zeichenklasse steht für genau ein dazu passendes Zeichen 
und ist ein Mittelding zwischen 'beliebiges Zeichen' und 'ganz bestimmtes Zeichen'. 

Beispiel:  eMail-Header:

Nachfolgend eine typische Zeile aus einem eMail-Kopf, dann zweimal 
das expr-Kommando, das eMail-Adresse und Name aus der Zeile herausfischt,
und abschließend die Ausgabe von Name und Adresse: 
    Anselm Katastroff <AnseKat@t-online.de>
    expr "$zeile" :mail '<%([^<>]%{5,%}%)>'
    expr "$zeile" :name '^%([^<>]%{1,%}[^<> ]%)  *<'
    echo $name%n$mail
    Anselm Katastroff
    AnseKat@t-online.de
Der Teilausdruck  [^<> ]  ist wichtig, falls vor < mal mehrere Leerzeichen stehen sollten.
Ohne ihn wären sonst am Namen  n-1  Leerzeichen hinten dran.
In die Ausdrücke ist also eine gewisse Fehlertoleranz eingebaut, auch werden mindestens 
5 Zeichen und mindestens 2 Zeichen für Adresse bzw. Name gefordert.

Andererseits ist  [^<>]  nicht ganz genau.
[a-zA-Z0-9_.@-]  wäre für professionelle Ansprüche besser geeignet, 
weil das diejenigen Zeichen sind, aus denen eine eMail-Adresse bestehen darf. 

Suchen und Ersetzen:

Folgende Shell-Funktion ersetzt einen Teil einer Zeichenkette durch einen Ersatzteil: 
    Ersetze() {
       expr "$1" :: "$2" ¦¦ return
       local l=${#1} n=000 e

       expr "$1" :n "$2"
       expr "$1" :e "$2"'%(.*%)'

       let "l-=${#e}+n"
       catv ."$1" =$l,1
       catv ."$3" e /%j
       return 0
    }

    Ersetze aaabbbccc 'cc$' CC
    aaabbbcCC
Zuerst wird geprüft, ob das Suchmuster überhaupt vorhanden ist ($2 in $1). 
Dann werden lokale Variablen angelegt:  l=9 
expr:  n=2, e="" 
let:  l=7 
catv  schreibt die ersten 7 Zeichen aus $1 zur Standardausgabe:  aaabbbc 
catv  schreibt  CC ($3)  und  "" (e)  und Zeilenvorschub (/%j) :  CC 
Funktion wird mit Exit=0=TRUE verlassen. 
Ohne das Dollar-Zeichen beim Suchmuster wäre das Ergebnis:  aaabbbCCc
    Ersetze aaabbbccc "cc" CC
    aaabbbCCc
    Ersetze aaabbbccc "aa*" A
    Abbbccc
    Ersetze aaabbbccc "bbc" BBc
    aaabBBccc
Falls mehrere Vorkommnisse eines Suchmusters ersetzt werden sollen, kann die Funktion 
mit dem jeweils vorherigen Ergebnisrest so oft aufgerufen werden bis sie einen FALSE-Exit gibt. 
Oder man vereinbart eine Option und integriert eine Schleife. 

Zur Lösung dieser Ersetzungsaufgabe gibt es natürlich viele verschiedene Möglichkeiten, 
diese ist eher zur Demonstration, zum Probieren gedacht. 
Beispielsweise kann das grep-Kommando zu Hilfe genommen werden, um Dateien schnell 
auf Suchmuster zu prüfen... 

Nichtinteraktives unkontrolliertes Ersetzen in ganzen Dateien birgt natürlich Gefahren! 
So können Suchmuster ersetzt werden in einem Zusammenhang, wo man gar keine Ersetzung will. 
Mit einem Editor, der RAs kann, ist das unproblematischer, weil man da vorher 
kontrollierend suchen und nötigenfalls 'undo' geben kann. 

Bei Dateien mit einfacher Tabellenstruktur ist die Angelegenheit meist unproblematisch. 
Da jedoch kommt man auch anders zum Ziel, wenn beispielsweise komplette Worte, die durch 
Zwischenraumzeichen getrennt sind, ersetzt werden sollen. 

Das expr-Kommando kann auch selbst Ersetzungen vornehmen:

    expr zeichenkette  :         'regexp' [+=] 'ersatztext'
    expr zeichenkette  :ergebnis 'regexp' [+=] 'ersatztext'
    expr zeichenkette =:ergebnis 'regexp' [+=] 'ersatztext'
    expr abcde :  bcd  ''
    ae
    expr abcde : '[cC]' '-&-'
    ab-c-de
    expr abCde : '[cC]' '-&-'
    ab-C-de
    expr abCdec : '[cC]' + '-&-'
    ab-C-de-c-
    expr 'Ai[23][i+1]' : 'Ai%[%([^]]*%)]%[%([^]]*%)]' %
                         'Aik[%2][%1][k]'
    Aik[i+1][23][k]
Es ist schon 'ein Hammer', was man mit echten(!) RAs machen kann. 
Manche Aufgaben schrumpfen von 10 Stunden auf 10 Minuten Zeitbedarf! 
Man beachte, daß oben irgendwas beliebiges zwischen den eckigen Klammern stehen darf, 
es wird alles umgewechselt! 
(nur eckige Klammern dürfen hier nicht zwischen den eckigen Klammern stehen.) 
    expr 'A[k[i]][sec]' : 'A%[%(..*%)]%[%(..*%)]' 'A[%2][%1]'
    A[sec][k[i]]
Diese Variante wirkt unsicherer eingegrenzt, funktioniert aber ebenfalls und ist kürzer. 
Außerdem werden hier auch  []  mit umgewechselt.
Wenn jedoch die [] mehrfach eingesetzt werden, kommt es zu anderen Ergebnissen 
als man vorher dachte.  Unsicherer ist diese Variante also doch. 
    '%(..*%)%1'
    abcabc
    aaaa
    k3rk3r
Obiger RA paßt zu allen nichtleeren Zeichenketten, die aus zwei genau gleichen 
irgendwelchen Hälften bestehen! 

'+'  bewirkt Ersetzen aller Vorkommnisse, sonst nur ein erstes.
'='  macht spezielle Zeichen im Ersatztext zu gewöhnlichen. 
Speziell sind im Ersatztext:  &  %  %1 bis %9   und   %U %L %E
'&'  steht im Ersatztext für denjenigen Teil der Eingabe-Zeichenkette, der jeweils zum RA paßt.
%ULE   sind für Uppercase- und Lowercase-Wandlung und deren Abschaltung.
Bei  '=:'  (2.Arg) wird der Case außerhalb von Zeichenklassen ignoriert.

grep sucht nach Dateizeilen mittels Regulärer Ausdrücke. 
expr kann beliebige Zeichenketten (z.B. Zeilen) vergleichen, ausschneiden und verändern. 
Beide Kommandos setzen stets einen Exit-Kode. 
Eine Zielvariable wird von expr nicht verändert, sofern der RA nicht paßt. 

siehe Manuals expr(K), grep(K), regexp(R).

^

Hintergrund-Prozesse

Die bsh hat hierzu 3 Kommandos und den Operator '&'.
Der Parameter  $!  enthält den jeweils letzten Prozeß-ID (PID).
$$  enthält den PPID.
    # fork '/bin/sleep 30' '/bin/sleep 40'
    1222
    1224
    # wait 1222
    1222
    # wait
    1224
    # _
Hintergrund-Prozesse und  wait  gehören zusammen.
wait  ist auch sinnvoll, wenn CHILDs bereits beendet haben,
es erfolgt dann kein Warten, sondern eine sofortige Rückkehr.
Spätestens bis zur nächsten Tastatur-Eingabe sollten alle CHILDs ein  wait  bekommen haben.
Ohne  wait  gibt es beispw. unter Linux und FreeBSD eine Enter-Notwendigkeit nach Kommando-Ausgaben.
Unter SCO-Unix nicht, aber es werden Zombie-Prozesse erzeugt, die durch Beenden
des PARENT verschwinden - oder eben durch  wait .
Wenn der PARENT vor CHILDs endet, werden die CHILDs dem init-Prozeß zugeordnet (PID=1).

Wenn der CHILD auf Handle 0 verzichten kann, sollte man ihn schließen.
Das ist auch ein Weg, um manche Probleme zu lösen.

    # /bin/sleep 30 & /bin/sleep 40 &
Dies hat oberflächlich den gleichen Effekt wie oben  fork .
fork  startet allerdings für jedes Argument: bsh -c 'arg'
&  startet nur das angegebene externeKommando im Hintergrund, beherrscht also nicht die bsh-Syntax.
Dafür können die Prozesse kleiner sein.

Hintergrund-Prozesse sind sinnvoll bei langsamen Speichervorgängen von/zu Geräten,
oder bei Daemons, die etwas überwachen sollen.

    kill [-signo] pid ...
    # echo $$
    1314
    # kill 1314
    bsh: *** Signal 15 *** ...
    $ _
Mit  kill  sendet man Signale zu Prozessen.
Siehe:  man signal

Die letzten fünf Zeilen zeigen, wie man die bsh auch beenden kann.


Ein Abfangen des Signals SIGCHLD mit automatischem 'wait' funktioniert einerseits gut,
aber wenn zum Zeitpunkt dieses Signals eine System-Funktion arbeitet, kehren viele davon
mit Error-return zurück!
Deshalb ist ein SIGCHLD-Catch letztlich keine gute Idee.


Mit Win95/98/me arbeitet die BG-Einrichtung nicht so gut zusammen.
Setzen Sie daher die Umgebungsvariable 'BSH_W98' .

.

^

Interne Kommandos der bsh

Die bsh ist mit Abstand das leistungsfähigste aller Shell-Programme!
Und die einzige unix-typische Shell für alle Betriebssysteme - auch für DOS. 
Der Interpreter 'perl' ist möglicherweise ähnlich leistungsfähig, er ist aber keine Shell 
und er wurde von vornherein konzeptionell auf Unix zugeschnitten. 

Kommandonamen:

    .        :        alias    array    autor
    base     basename break    cat      catv
    cd       chdir    cmpv     continue conv
    copy     crc      ctime    cut      dirname
    echo     env      eval     exec     exit
    export   expr     extern   false    fmode
    fprint   fsize    fstat    global   goend
    goto     grep     ifdef    ifset    ifenv
    inv      let      line     list     local
    localset mkdirs   mktemp   move     mtime
    print    prints   prompt   pwd      read
    readc    readl    readonly rel      remove
    return   sane     seek     set      shift
    sleep    sortl    static   stime    sum
    system   systime  tee      test     times
    tr       trap     true     type     typeset
    tz       umask    unalias  unexport unset
    unsetenv ver      wc       whence   fullname
    fork     wait     kill     link     nop
    readv

Schlüsselworte:

    if then else elif fi  case esac    Schlüsselwort-Kommandos
      for while until do done  time    Schlüsselwort-Kommandos
              in  from by to repeat    Schlüsselwort-Argumente
                          { } {{ }}    Schlüsselwort-Kdo-Paare
                          [ ] [[ ]]    Kommandos mit Schlußarg
              break continue  goend    Programmier-Kommandos
                       return  goto    Programmier-Kommandos

Liste nach Aufgaben:

    .           Skript-Aufruf (. datei arg ...)

    :           Exit-Code
    true        Exit-Code
    false       Exit-Code
    inv         Exit-Code
    nop         Echtes Leerkommando - tut gar nichts

    typeset     Variablen-Typ (binär,basis)
    readonly    Variablen
    export      Variablen
    unexport    Variablen
    unset       Variablen
    unsetenv    Variablen
    local       Variablen
    localset    Variablen
    static      Variablen
    global      Variablen
    env         Variablen
    array       Variablen, Arrays
    set         Variablen, Arrays, Optionen
    shift       Parameterkette verschieben
    conv        Variablen-Bearbeitung
    cmpv        Variablen-Vergleich
    ifdef       Variablen-Test
    ifset       Variablen-Test
    ifenv       Variablen-Test

    alias       Aliases
    unalias     Aliases

    echo        Ausgabe
    print       Ausgabe
    prints      Ausgabe, universell, formatiert
    fprint      Ausgabe, Funktionskörper
    catv        Extrem universelle, binäre Eingabe/Ausgabe
    seek        Dateizeiger
    read        Eingabe
    readc       Eingabe
    readl       Eingabe
    readv Eingabe aus Variablen -> Variablen, binär/text
    line        Eingabe/Ausgabe

    test        Universal-Bedingungsprüfer

    fstat       Datei-Attribute
    fmode       Datei-Mode (fstat)
    fsize       Datei-Größe (fstat)
    mtime       Datei-Zeit (fstat)
    stime       Datei-Zeit (fstat)
    umask       Datei-ModeMaske
    crc         Prüfsummen
    sum         Byte/Word/Long-Summen
    wc          Zählt Zeilen, Worte, Zeichen
    copy        Kopiert Dateien und Verzeichnisse
    move        Bewegt  Dateien und Verzeichnisse
    remove      Löscht  Dateien und Verzeichnisse
    link        Erzeugt Links (Verweiseinträge)
    cat         Dateien-Verkettung
    sortl       Sortiert Dateizeilen
    rel         Entfernt bestimmte Zeilen
    grep        Zeilensuche mittels regulärer Ausdrücke
    expr        Operationen mittels regulärer Ausdrücke
    tr          Universelle Zeichen-Veränderungen
    cut         Datei-Ausschnitte
    mktemp      Temporäre Dateien
    tee         Umlenkungs-Zusatzverzweigung
    basename    Datei-Namen
    dirname     Datei-Namen
    fullname    Datei-Namen

    cd          Directory
    chdir       Directory
    pwd         Directory
    list        Verzeichnis-Liste, rekursiv, Filter
    mkdirs      Erzeugt Verzeichnisse

    sleep       Zeitdauer
    times       Zeitdauer
    systime     Systemzeit
    ctime       Zeitformate
    tz          Zeitzone

    let         Arithmetik
    base        Zahlen-Darstellungsbasis, multi-funktional

    eval        Mehrfache Shell-Interpretation

    prompt      Shell rekursiv interaktiv aufrufen

    type        Bedeutung von Namen herausfinden
    whence      Bedeutung von Namen herausfinden
    extern      Externes Programm ausführen
    system      Kommandoausführung durch Standard-Shell
    trap        Signal-Reaktionen
    exec        Programm ausführen und damit Shell beenden
    fork        Background-Prozesse
    wait        Background-Prozesse
    kill        Beendet Prozesse
    exit        Shell beenden
    sane        Dateinummern rückverknüpfen (sanieren)

    ver         Programmversion
    autor       Programmautor
Siehe bsh(K), xyz(K).

^

Aufruf der bsh und Aufruf von Shell-Scripts

Interaktiv - man bekommt ein Prompt: 
    bsh
    bsh -E
Option -E startet gleich den Kommandozeilen-Editor; innerhalb:  set -E ; set +E 
Man kann damit sehr gut herumprobieren:  kdo ...; echo $? 

Startet ein Shell-Script von 'außen' und am bsh-Prompt: 

    bsh script.bsh [arg...]
    #  script.bsh [arg...]
    #  . script.bsh [arg...]
Startet für eine Kommandoausführung: 
    bsh -c "kdo; ...;"
In der DOS-Kommandozeile kann hierbei die Formulierung mitunter problematisch sein. 

Beim Start verarbeitet die bsh automatisch ein Script  .bshrc  bzw.  autoexec.bsh
in demjenigen Verzeichnis, das in der Umgebungsvariablen HOME angegeben ist. 
Dort können auch PATH und CDPATH (nochmals) gesetzt werden und 
weitere Initialisierungen vorgenommen werden. 
'bsh -p'  schaltet das ab.

^

Installation der bsh und ihrem Dokumentationssytem

Nachfolgend ein Ausschnitt aus der Datei 'zuerst.txt', die in 'bsh302.zip' enthalten ist: 
In Ihrer \autoexec.bat sollten Sie mindestens
folgende Variablen auf gültige Inhalte setzen:
(- allerdings in Abhängigkeit von der vorhandenen Software):
     set TEMP=r:\ram_temp
     set BSHTEMP=r:\ram_temp
     set MANFILES=c:\bsh\man
     PATH=%PATH%;c:\bsh\bin
     set BSHAUTOR=H.Schellong,BadSalzuflen
     set XXXAUTOR=H.Schellong,BadSalzuflen
(Die angegebenen Verzeichnisse sind natürlich Beispiele.)
Wichtig ist, daß die Verzeichnisse, die Sie angeben, auch
tatsächlich existieren und ggf. den erwarteten Inhalt haben.
In Ihrer \config.sys sollten Sie setzen:
     FILES=80       (mindestens)
     SHELL=c:\command.com c:\ /p /e:2048
     devicehigh=c:\dos\ansi.sys /x
Wichtig ist, daß die Programme  bsh.exe, man.exe, pg.exe, upat.exe  'im Pfad' (PATH) liegen 
und MANFILES gesetzt ist. 
Das Kommando 'man' zeigt die Manualtexte mit Hilfe von 'pg'. 
'man man' zeigt das Manual zu 'man' selbst. 

HOME und CDPATH können ebenfalls bereits hier gesetzt werden. 

Die Dokudateien (Manuals) in \bsh\doc liegen natürlich im IBM437-Zeichensatz vor. 
Sie können in ein \bsh\dociso kopiert werden und dann nach ISO8859 konvertiert werden, 
damit sie auch mit einem Browser korrekt aussehen. 
Zur Umwandlung gibt es ein Script in  bshbeisp.htm . 

^

^

Beispielhafte Tips für Windows NT (4)

WinNT hat ebenfalls einen ansi.sys im Verzeichnis  C:\WINNT\system32\ .
Dort sind auch  Config.nt  und  Autoexec.nt .
In  Config.nt  braucht man in der Regel nur die Zeile mit  ...himem.sys  zu kopieren
und dann auf den Namen  ansi.sys  zu ändern.
Dadurch kann der Kommandozeilen-Editor der bsh benutzt werden.

Den Pfad kann man folgendermaßen erweitern:  path %path%;d:\bin;d:\usr\bin

Innerhalb der bsh zeigt das Kommando env: COMSPEC=...\COMMAND.COM
Das kann geändert werden:

#  COMSPEC=...\CMD.EXE
#  export COMSPEC
Danach wird z.B. bei
#  DIR
cmd.exe benutzt, der NT-Interpreter, der mit seinen Kommandos ja lange Dateinamen
unterstützt.
Auf diese Weise kann die DOS16-bsh indirekt mit langen Namen umgehen (z.B. grep, expr, ...).

Unter Win95/98 kann wohl ähnlich vorgegangen werden.

Eine bsh32.exe und eine Linux-bsh wird es vielleicht auch bald geben.
Die jetzige bsh ist aber am universellsten, da sie in absolut jeder DOS-Umgebung läuft,
z.B. in allen DOS-Emulatoren unter Unix und in allen Win-DOS-Boxen sowieso.

^

^

Besondere bsh-Vorteile

  • Sehr viele (etwa 100) interne Kommandos.

  • Darunter auch mehrere große, wie z.B. grep und expr.
  • Es ist nahezu alles intern vorhanden, um das ganze System zu beherrschen,

  • bis hin zur direkten Manipulation eines einzelnen Bytes auf der Festplatte.
  • Von DOS über Windows bis hin zu Unix sind bsh-Skripte portabel, oft ohne

  • irgendein Zeichen darin verändern zu müssen.
  • An vielen Stellen brauchen nur die Namen von Variablen angegeben zu werden,

  • also ohne $-Operator, gerade weil alles intern ist.
    Das gestattet viele indirekte Operationen auf ganz einfache Weise.
  • Anstelle von viel kryptischer Syntax wurden viele Kommandos eingebaut,

  • um Zusatzfunktionalitäten zu realisieren.  Das ist klarer, übersichtlicher und
    leichter zu merken.
  • Das Verhalten bei Quoting und Maskierung, einschließlich Kommandosubstitution,

  • ist einheitlich, praxisgerecht einfach und präzise vorhersagbar.
  • bsh ist ClosedSource-Software, was durchaus auch ein Vorteil ist.

  • Ein einziger Autor und immer derselbe arbeitet daran.  Das verhindert Wildwuchs.
    Bis in's Detail bleibt alles für immer gleich; es kommen nur neue Einrichtungen hinzu.
    Die (undokumentierten) Detailunterschiede bei anderen Shells sind insgesamt immens,
    auch bei ein und demselben Shell-Namen - trotz POSIX-Standard.
  • Die Dokumentation ist vergleichsweise sehr viel ausführlicher und mit Beispielen garniert.
  • Die Kern-Geschwindigkeit ist deutlich bis vielfach höher als die aller anderen Shells:

  • 1.5-, 7-, 15-, 45-fach, wobei die ksh (1.5) als einzige in die Nähe der bsh kommt.
    Die Brutto-Geschwindigkeit bei spezifischen Problemlösungen ist nicht selten 20..30-fach
    höher als eine ksh-Lösung.  Selbst 'perl' wird mitunter von der bsh erreicht.
  • Die statische Exe-Größe ist mit etwa 130 KB sehr gering.  Falls das höher liegt, sind die

  • hinzugelinkten Libraries der Grund, die nur selten optimal konzeptioniert sind.
  • Gute Eignung als CGI-Interpreter ist vorhanden.

  • Mein WebSpace einschließlich CounterService arbeitet seit Jahren mit bsh.
    Neben bsh kann hier eigentlich nur 'perl' eingesetzt werden.
  • Gute Eignung als Interpreter unter Embedded-Systemen (auch DOS) ist vorhanden.

  • In den meisten Fällen kann hier nur bsh eingesetzt werden.

    ^

    Zur Hauptseite