*** BSH PROGRAMMING CONSIDERED HARMFUL ***

> GEGENDARSTELLUNG / ENTGEGENHALTUNGEN des bsh-Autors
> (Farblich und per > abgesetzt)


Seit einiger Zeit hat die Newsgroup de.comp.os.unix.shell mit Postings eines gewissen Helmut Schellong zu tun, in denen er sein Programm »bsh« ungebeten anpreist. Da sich herausstellte, daß dieses Programm nach Ansicht vieler Teilnehmer fragwürdige, aber laut Autor gewollte Eigenheiten hat, habe ich sie hier zusammengestellt.

> Sehr übertriebene Darstellung zu Postings über eine 100%-OnTopic-Shell.
> In 5-10% aller Postings gibt es möglicherweise einen werbenden Tonfall von mir - mehr steckt nicht dahinter.
> In Antworten (innerhalb von Diskussionen) kann man die Nennung positiver Merkmale meist nicht vermeiden;
> das kann nicht einfach als "anpreisen" dargestellt werden.


1. »bsh« hat eine proprietäre Syntax

Seit dem Ende der siebziger Jahre ist die Bourne-Shell die dominierende Unix-Shell. Sie bietet eine für einen Kommandoprozessor sehr ausgefeilte und mächtige Sprache. Anfang der neunziger Jahre wurde sie zusammen mit mehreren Erweiterungen der Korn-Shell in POSIX.2 standardisiert; SUSv2 als der Unix-Marke zugrundeliegende Spezifikation hat diese Definition übernommen. Nahezu alle Shell-Skripte auf Unix-Systemen sind in dieser derart historisch gewachsenen Sprache geschrieben.

> Verfolgt man die NG-Postings, stellt man ein riesiges Tohuwabohu fest, was hinter einem tradionellen Pfad
> wie beispw. /bin/sh an Unterschiedlichkeit steckt - je nach Unix-System. Man könnte offenbar Bücher darüber füllen...
> Auch von vielen Shell-Bugs ist die Rede.

> Alle Shells (csh,ksh,bash,tcsh,ash,zsh,pdksh) haben ihre eigene, proprietäre Syntax bzw. proprietäre
> Erweiterungen gegenüber der 'sh', und das sogar untereinander gemischt und auch noch plattformabhängig.
> Für bsh gilt das ebenso, jedoch ist sie bei weitem plattformunabhängiger als die anderen Shells.

> bsh enthält weitgehend die Syntax der ksh, mit gleicher oder nahezu gleicher Semantik, wobei die ksh wiederum
> die Syntax der Bourne-Shell (sh) enthält.

Der Autor der »bsh« behauptet, seine Shell verfüge über eine ähnliche Syntax. Bei oberflächlicher Betrachtung mag man das hinnehmen, es werden z.B. dieselben Namen für Kontrollkonstrukte (for - do - done, if - then - fi) und Befehle (break, expr) verwendet. Sieht man jedoch genauer hin, ergeben sich gravierende Unterschiede.

  >      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
> Unterschiede sind gar nicht oder kaum vorhanden.
> Unterschiede und Besonderheiten sind bei bsh erheblich vollständiger und auffälliger dokumentiert als bei den anderen Shells.

So haben viele Kommandos zwar denselben Namen, aber andere Optionen oder sonstige Änderungen. expr z.B. wird unter Unix vornehmlich für simple arithmetische Operationen genutzt. Das expr-Kommando der »bsh« kennt sie nicht, stattdessen aber eine Reihe von Erweiterungen im Umfeld regulärer Ausdrücke. Hier weist das Unix-expr lediglich den Operator : auf, der einen String mit einem regulären Ausdruck vergleicht. Letzterer ist auf den Anfang des Strings fixiert, auch das ist bei »bsh« nicht der Fall. Die äußere Ähnlichkeit der Syntax täuscht also, tatsächlich weist das expr-Kommando der »bsh« massive Unterschiede zum Unix-Kommando auf.
 

> Original:           expr "string" :  RE
>      bsh:           expr "string" : ^RE
>      bsh:      /bin/expr "string" :  RE
>      bsh:    extern expr "string" :  RE
> Einziger Unterschied:  bsh-expr gestattet eine Wahl zwischen mit ^ und ohne ^.

> Daß per  ::  Erweiterungen aktiviert werden, ist gewiß kein Nachteil.

Im Widerspruch zur allgemeinen Praxis auf Unix-Systemen wird von »bsh« nicht der Backslash \, sondern das Prozentzeichen % zum Quoten einzelner Zeichen benutzt. Das heißt, daß zunächst fast jeder String einschließlich regulärer Ausdrücke auf diese Zeichen untersucht und ggf. geändert werden muß; ferner wird % auf Unix-Systemen an so vielen Stellen verwendet, daß es zu Problemen mit dem echo-Kommando der »bsh« kommen kann, das dieses Zeichen zum Einleiten von Escapesequenzen nutzt.

> Das stimmt nicht.  bsh gestattet eine Wahl zwischen  %  und  \  :

> bsh -B
> set -B
> #!/u/bin/bsh -B
> aktivieren \ .

Die Kontrollkonstrukte der »bsh« weisen gegenüber denen der Unix-Shell funktionale Beschränkungen auf. Insbesondere ist es nicht möglich, die Ausgabe einer Schleife

         while get-some-input
         do
                 process-one-line
         done | postprocess
oder einer Kommandogruppe gemeinsam unzulenken:
        { echo foo; echo bar; } > output-file
> Das stimmt zwar -und ist dokumentiert-, aber als flexibleren Ersatz gibt es hierfür globale Umlenkungen,
> bei denen eine explizite Rücknahme an beliebiger Stelle vorgesehen ist:
<file
>>file2
...
>file3
...
><<
...
><
> Dies folgt dem Konzept  open() ... close()  in der Sprache C.

Schleifen können nicht asynchron ausgeführt werden, das &-Zeichen bleibt in diesem Fall schlicht wirkungslos:

        while some-condition
        do
                background-processing
        done &
> Letztlich stimmt das nicht.  Kommando fork "..." "..." ...  gestattet das prinzipiell, mit anderer Syntax
> und etwas anderer Semantik.

Operationen, für die die Shell gemeinhin eingesetzt wird, lassen sich also mit »bsh« nicht oder nur über Umwege durchführen. Aber selbst wenn die Funktionalität im Prinzip vorhanden ist, ergeben sich Probleme, denn gängige Konstrukte der Unix-Shell werden von »bsh« nicht akzeptiert. So darf break nicht im ersten Kommandoabschnitt einer Schleife stehen:

        while { check-this && break; } || check-that
        do
                work
        done
> Ab Version 3.40 dürfen  break,continue,goend  bei  while,if,elif  stehen.
> bsh kennt zusätzlich auch  goto.
> Diese Einschränkung war dokumentiert und absichtlich in Anlehnung an die Sprache C implementiert,
> um 'unsaubere', unübersichtliche Syntax zu verhindern.
> Diese Konstruktionen sind zudem eher selten als "gängig".

Funktionsdefinitionen dürfen nicht allein mit ; enden:

        foo() { bar; }; foo
> Das stimmt, ist dokumentiert und ist Absicht.  Das ist IMO grauselige Syntax.

Das Dollarzeichen darf nicht unmaskiert am Ende eines Wortes stehen:

        grep foo$ bar
> Das stimmt und ist Absicht.  $ ist unmaskiert nun mal ein Spezialzeichen, hinter dem Bestimmungsgemäßes folgen muß.

Wenn eine Einflußnahme auf Variablen, Arbeitsverzeichnis, offene Dateien usw. der ausführenden Shell nicht erwünscht ist, kann der Programmierer einen Skriptabschnitt in einer exakten Kopie der Shell (Subshell) ausführen, indem er ihn in Klammern ( ... ) setzt. Auch »bsh« bietet ein solches Konstrukt, jedoch sind Syntax wie Semantik verschieden. So ist es nicht möglich, innerhalb von Subshell-Listen mit Anführungszeichen zu quoten;

        ( echo '$name' )
müßte mit »bsh«
        ( echo %%%$name )
> Hier wird die Variable weder von der aufrufenden Shell noch von der SubShell ausgewertet.
> Das ist ein exotischer Fall.  Meist gibt man  $name  oder  %$name  für Auswertung durch aufrufende
> bzw. SubShell.  Dafür wird '^abc[u-x]%{2,6}'  original übergeben, ohne Maskierorgien.
> Die Entscheidung, hier nur den Zeichen % $ `  bzw.  \ $ `  Spezialbedeutung zu geben, ist für die reale Praxis richtig.

geschrieben werden, um die Auswertung als Variable zu verhindern. Die Subshell der »bsh« ist keine exakte Kopie ihres Vorgängers, denn der Einsatz ihrer Shell-Funktionen ist darin nicht möglich:

        foo() { bar; }
        ( foo )
schlägt also fehl.

> Hier kann man sich trefflich streiten:  Variablen, CWD, etc. soll die SubShell nicht übernehmen, Funktionen aber doch.
> Weder die Unix-Shell-SubShell noch die bsh-SubShell ist eine exakte Kopie!
> Die Funktion fork() auf C-Ebene erzeugt hingegen eine fast perfekte Prozeß-Kopie...

> Desweiteren besitzt bsh datei-lokale, funktions-lokale und -lokal-statische Variablen, Aliase und goto,
> weshalb die SubShell-Einrichtung in der bsh weitgehend ihre Bedeutung verliert.
> Diese ausgebaute Lokalität der bsh ist einer ihrer vielen großen Vorteile.

Die Erkennung von Schlüsselworten ist in der Unix-Shell unterbunden, wenn sie mit Quotingzeichen versehen sind oder der entsprechende String aus einer Variablensubstitution entstanden ist. Nicht so in »bsh«:

        #  "if" true; then echo foo; fi
        foo
        #  foo=if; $foo true; then echo bar; fi
        bar
In der Unix-Shell läßt sich das, falls gewünscht, mit eval erreichen; das Verhalten der »bsh« kann hingegen dazu führen, daß eine Stelle in einem Skript abhängig von externen Einflüssen als Syntaxfehler gewertet wird.

> Das ist (pauschale) Absicht, um einfache und schnelle Indirektionen zu ermöglichen.
> bsh gestattet es mittels  ifdef -seEf, ifset -seEf, ifenv  genaue Prüfungen vorzunehmen.
> Auch diese 'Ortungsfähigkeit' ist ein Vorteil der bsh.

Die Verwendung der »bsh« wird von arbiträren Limits eingeschränkt, so gibt es eine feste Obergrenze für Länge und Anzahl von Variablen wie Funktionen. Selbst bei internen Kommandos sind sowohl die Zahl als auch der gesamte Zeichenumfang der Argumente begrenzt.

> Das stimmt primär für die kostenlosen bsh-Varianten.
> Die Limits sind aber hoch genug gesteckt, um vieles zu ermöglichen, was durch die öffentlichen Beispiel-Skripte
> bewiesen ist.

Die regulären Ausdrücke in den Kommandos der »bsh« sind ebenfalls proprietär. So gibt es %A für Buchstaben, %U für Großbuchstaben usw. POSIX.2 schreibt dafür nicht nur eine andere Syntax ([:alpha:], [:upper:]) vor, sondern insbesondere auch, daß Umlaute und ähnliche sprachgebundene Zeichen abhängig von der Locale-Einstellung des Systems einbezogen werden; in »bsh« fehlt diese Eigenschaft.

> Das gilt genauso und noch 'viel schlimmer' für fast alle Programme, die RAs anbieten:  z.B.
> perl, vim, JavaScript, diverse GNU-Proprietarismen, usw.
> Da ist fast das ganze \Alphabet in dieser Weise besetzt.

Folglich ist es unmöglich, mit »bsh« ein nicht vollkommen triviales Unix-Shellskript auszuführen, ohne in größerem Rahmen Anpassungen vorzunehmen. Die Subtilität der Differenzen macht diesen Prozeß zudem schwer durchschaubar. Wer sowohl Unix-Shells als auch »bsh« einsetzt, wird ständig durch falsche Freunde irritiert.

> bsh ist keine POSIX.2-Shell.  bsh ist nun mal proprietär wie z.B. auch zsh.  bsh ist dokumentiert und funktioniert auch
> entsprechend ihrer Dokumentation einwandfrei.


2. Der Parser der »bsh« ist unzuverlässig

Die ohnehin vom Standard abweichende Syntax der »bsh« wird vom Parser des Programmes nicht immer sauber behandelt. Das zeigt sich bereits an den häufig irreführenden Fehlermeldungen, wie
        #  if { then; }; fi
        bsh: Syntaxfehler: 'vermisse  ;} '

        #  while break; do true; done              > beseitigt; war und ist dokumentiert.
        bsh: Syntaxfehler: 'vermisse do'

        #  echo $/
        bsh: Parametername zu lang: ''
Der Umgang mit der Syntax wird dadurch noch zusätzlich erschwert.

> Auch wenn der Wortlaut nicht immer perfekt paßt, sind es dennoch hinreichende Hinweise.
> Zumal in Scripten Zeilennummern und ggf. Funktionsnamen mit angegeben werden.

Fehlerhafte Konstrukte werden mitunter ohne jede Fehlermeldung oder Warnung ausgeführt:

        while true && do break | done

        while true; echo | do; false && done
> Bei solchen abenteuerlich 'abgebrochenen' Syntax-Konstruktionen kann das passieren - na und.
> Hauptsache, es gibt keine echten Bugs wie z.B.:  "Unbemerkt 1 Loop zuwenig bei bestimmten Situationen."

Wird eine Datei in ein unvollständiges Kontrollkonstrukt geleitet, führt »bsh« u.U. den Inhalt der Datei aus:

        1#  echo uname -s | if
        1>  Linux
        1>  bsh: Syntaxfehler: 'vermisse then'
        bsh: Umlenkungsr ckf hrung!

        1#  echo 'rm -ri /' > foo
        2#  while < foo
        2>  rm: examine files in directory / (yes/no)? 
        2>  bsh: Syntaxfehler: 'vermisse do'
        bsh: Umlenkungsr ck hrung!
Die Gefährlichkeit dieses Vorgangs liegt auf der Hand. Gängige Konstrukte der Bourne-Shell sind nicht nur nicht implementiert, ihre Verwendung kann zur Ausführung von Dateiinhalten führen, die niemals als Shellskript vorgesehen waren.

> Diese potentielle Gefahr ist beseitigt ab Version 3.40, obwohl deren Wahrscheinlichkeit extrem gering ist.
> Außerdem: die Doku benennt und benannte solche Konstruktionen im interaktiven Modus explizit als problematisch.
> Eine Bearbeitung dieser Multizeilen-Konstruktionen bei Eingabe im interaktiven Modus ist schlicht nicht implementiert.


3. »bsh« bietet Pipes, die keine sind

> Die Verwendung von bsh-Pipe-Dateien war und ist mehrfach dokumentiert.

Eines der wichtigsten Mittel der Shellprogrammierung ist die Pipe, ein Kommunikationskanal, der sich an beiden Enden als Datei präsentiert. Was in das eine Ende der Pipe geschrieben wird, wird am anderen Ende gelesen, wobei das Lesen und u.U. auch das Schreiben blockiert wird, bis die entsprechende Aktion auf der anderen Seite stattfindet. Eine Pipe wird laut POSIX-Standards mit dem Systemaufruf pipe() erstellt, sie kann in der Shell mittels write_cmd | read_cmd zum Einsatz kommen. Das syntaktische Konstrukt heißt Pipeline.

Die »bsh« belegt dieselbe Syntax mit einer anderen Semantik. Statt der Pipe werden reguläre Dateien verwendet, deren Inhalt von einem Programm an das nächste weitergegeben wird; das entspricht in etwa

        write_cmd > /tmp/$$; read_cmd < /tmp/$$
in der Unix-Shell. Da Zugriffe auf eine solche Datei nicht blockieren, muß »bsh« mit dem Starten jeden Gliedes der Pipeline so lange warten, bis der vorhergehende Prozeß beendet ist. Das führt zunächst dazu, daß der Vorgang durch den Verlust der Parallelität langsamer abläuft; ferner müssen die Daten, die durch eine echte Pipe transparent laufen, hier im Dateisystem abgelegt werden, was leicht zu Platzproblemen führen kann.

Anders als bei einer echten Pipe muß zudem jeder Prozeß von selbst terminieren, damit es nicht zu einer Blockadesituation kommt. Das heißt, daß eine Befehlsfolge wie

        tail -f /var/log/messages | egrep 'warning|error|fatal|panic'
zum überwachen einer Logdatei mit der »bsh« nicht verwendbar ist.

>Flexibles und ausbaubares Monitoring mit bsh:

>size=0 offs=0 st=100
>while sleep -m $st; true
>do
>   fstat -sv size $file
>   [ size -eq offs ] && st=500 continue
>   catv $offs,$((size-offs)),0 =Data: < $file
>   offs=$size
>   print -rn "$Data"
>done


Viele Unix-Utilities prüfen, ob ihre Ein- oder Ausgabe mit einer Pipe verbunden ist und passen sich daran an. Das ist erwünscht, da sich eine Pipe in Details anders verhält als eine reguläre Datei. So ist es nicht möglich, die Position des nächsten Zugriffs zu ändern; Daten werden beim Schreiben ans Ende angehängt und stets von vorne gelesen.

Zudem sind Schreibzugriffe in eine Pipe bis zu einer gewissen Größe atomic, das System garantiert also, daß eine bestimmte Datenmenge in einem Zug geschrieben wird. Das ist z.B. relevant, wenn mehrere Prozesse zeilenweise in eine Pipe schreiben, weil so garantiert werden kann, daß eine einzelne Zeile immer aus einer einzigen Quelle stammt. Bei der Simulation einer Pipe mittels regulärer Dateien ist das nicht der Fall.

> Doch, zumindest ist das herstellbar, weil ein einzelner write()-Aufruf in diesem SInne atomic ist.

Wenn eine Pipe aus irgendwelchen Gründen nicht in Frage kommt, kann man in der Unix-Shell von Hand reguläre Dateien anlegen. Daher ist die Simulation von Pipes in der »bsh« ein minderwertiges Verfahren, das die Shellprogrammierung einer ihrer wesentlichen Möglichkeiten beraubt.

  >      Die bsh-Pipelines arbeiten mit temporären Dateien, genau: mit seek-baren,
  >      verschachtelten Dateiendabschnitten, nicht mit der Funktion pipe().
  >      Das hat mehr Vorteile als pipe() Vorteile hat:
  >           o  Funktioniert mit allen Betriebssystemen.
  >           o  Man erhält eine temporäre Datei 'on the fly'.
  >           o  Diese Datei kann mittels 'seek' mehrfach gelesen werden.
  >           o  Es gibt keine (Lese-)Probleme mit Kommandos, die bei
  >              'echten' pipe()s versagen.
  >              (Kommandos, die keine ausdrücklichen Filterkommandos sind.)
  >           o  Die Prozesse werden nacheinander gestartet,
  >           o  sie können sich nicht gegenseitig stören,
  >           o  die momentane Prozeßlast ist geringer,
  >           o  der Exit ist von jedem in der Kette eindeutig erhältlich.
  >      Vorteile von pipe():
  >           o  Der Datenstrom darf beliebig groß sein.
  >           o  Müßte prinzipiell (meist) schneller arbeiten.
  >           o  Permanentes Monitoring ist möglich.
  >      Es ist angedacht, per  -U/+U und set -/+U  'echte' pipe()-Pipelines
  >      schaltbar zu machen - natürlich nicht in der DOS-Version.
  >      Grundsätzlich kann man in der bsh jede andere vorhande Shell
  >      aufrufen, falls man deren Spezifika mal benötigt.
> Die bsh-Datei-Pipes sind nicht minderwertig - sie sind lediglich anders.
> Der bsh-Pipe-Mechanismus ist technisch anders (und portabler) realisiert.

4. »bsh« ist keine freie Software

Obwohl es eine kostenlose Version der »bsh« gibt, dient sie letztlich als Anreiz zum Kauf einer Vollversion, wie man an einer Reihe von künstlichen Limitierungen erkennen kann. Außerdem ist der Quellcode zur »bsh« nicht verfügbar. Daher ist es unmöglich, selbst Bugs zu fixen oder Features hinzuzufügen. Ferner wird verhindert, daß ein Nutzer »bsh« auf ein System portiert, für das das Programm vom Hersteller nicht zur Verfügung gestellt wird. Insbesondere aber kann man sich von der Sicherheit der Software nicht ohne Quelleneinblick überzeugen. Wer »bsh« ernsthaft einsetzen wollte, wäre damit hochgradig vom Autor der Software abhängig.

> Demnach dürfte ab sofort weltweit keinerlei Software mehr verkauft werden und es keine ClosedSource geben, denn sowas
> ist automatisch 'brandgefährlich'  und niemals ernsthaft verwendbar ... ?!

> Die weitaus meiste Software ist proprietär, nicht kostenlos und ClosedSource!
> Freie OpenSource-Software ist mengenmäßig die absolute Ausnahme.


5. Der Verwendungsbereich der »bsh« ist stark eingeschränkt

Aufgrund der proprietären Syntax ist ein »bsh«-Skript letztlich nur lauffähig, wenn eine »bsh« zur Verfügung steht. Dies ist aber selten der Fall, den »bsh« wird von keinem Hersteller eines Unix-artigen Systems mitgeliefert. Sie ist zudem nur für wenige Systemumgebungen überhaupt verfügbar, Versionen für Unix-Systeme auf RISC-Rechnern fehlen fast völlig. Eine weitere Einschränkung ist, daß Fehlermeldungen und Dokumentation den Einsatz effektiv auf den deutschen Sprachraum festlegen. Wer Skripte für »bsh« schreibt, muß sich also darüber im klaren sein, daß deren Verbreitung über das persönliche Umfeld hinaus praktisch nicht herzustellen ist.

> Das stimmt nur halbwegs:
> Jedes beliebige Shell-Script ist nur lauffähig, wenn eine dazu passende Shell installiert ist.
> Keine neue Shell war sofort nach Erscheinen auf allen Plattformen mitgeliefert oder vorinstalliert.
> Bei der modernsten mittlerweile verbreiteten Shell, der ksh, hatte es bis zu 10 Jahre gedauert, bis sie weitgehend
> vorinstalliert war. Unter Linux ist sie bis heute nicht vorinstalliert, ja ksh93 noch nicht einmal verfügbar.
> bsh gibt es kostenlos für alle wichtigen Intel-x86-Plattformen, deren Exemplaranzahl bekanntlich immens ist.
> Die deutschen Texte erschweren den Einsatz außerhalb des deutschen Sprachraumes, machen ihn aber keineswegs unmöglich.
> Eine Verbreitung über das persönliche Umfeld hinaus ist zwar nicht wahrscheinlich, jedoch durchaus möglich.
> Dem persönlichen Nutzen tut das keinen Abbruch.


Fazit

Die proprietären Eigenheiten der »bsh« sind im Vergleich zur Unix-Shell nicht nur funktional minderwertig; seine Fehler und die Gefahr, die Konstrukte mit gängiger Syntax zu verwechseln, machen dieses Programm zu einem Risiko für das ganze System. Von einer Verwendung ist deshalb dringend abzuraten.

> Das stimmt überhaupt gar nicht.
> bsh ist mit ihren etwa 100 internen Kommandos die mit Abstand leistungsfähigste Shell und funktioniert
> einwandfrei gemäß ihrer Dokumentation und unübertroffen schnell zur Laufzeit.

In diesen Text flossen Beobachtungen von Jürgen Ilse, Jürgen P. Meier und Matthias Andree ein. Das Konzept lehnt sich an "Csh Programming Considered Harmful" von Tom Christiansen an. Jürgen Ilse stellt unter <http://members.pop-hannover.de/~ilse/bsh-programming-harmful.html> eine aktuelle HTML-Fassung dieses Dokumentes zur Verfügung.
Hier steht dieser Text auch Textdatei zur Verfügung:
bsh-programming-harmful.txt

Gunnar Ritter Version 1.3 01/08/18

> Gegendarstellung: Helmut Schellong, 22./23.08.2001