Kleiner Script-Sprachen-LehrgangHier 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
|
echo Hallo Welt!Das war's.
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.
kdoname wort wort wort ...
kdo arg ; kdo;kdo arg arg ; kdoEin Zeilenvorschub oder ein Semikolon (;) beenden das Sammeln von Kommandoargumenten,
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--cccFalls der Variableninhalt »VVV« ist, werden die drei Argumente
aaa bbb ++VVV--ccczum Bildschirm geschrieben.
»echo«waren hier die Argumente direkt vor Kommandoausführung.
»aaa«
»bbb«
»++VVV--ccc«
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}cccwodurch der Variablenname vom nachfolgenden Text isoliert wird.
Variablen werden gesetzt und dabei nötigenfalls erzeugt durch:
shv=VVVwobei kein Zwischenraum um das Gleichheitszeichen (=) erlaubt ist.
a=aaa b=BBB c=abc
dv= ev='' fv=""
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"Das erste Beispiel zeigt eine Maskierung aller Leerzeichen innerhalb der Einfassung "..." .
aaa bbb${shv}c"cc
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'Die drei Maskiermöglichkeiten sind also: "..." '...' %
aaa"bbb$ccc%ddd`eee'fff
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«Man kann natürlich auch mehrere solche Einfassungen an Kommandos übergeben:
» aaa bbb${shv}c"cc«
echo "..." "..." "..." '...'Das letztere Beispiel zeigt ein (1) zusammengesetztes Argument mit Maskierungen verschiedenen Grades.
echo '...'" $km Kilometer"'...'
echo % % % ab cdObige Variante zeigt, daß » ab« und »cd« gebildet werden.
ab cd
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'Die Zahl muß in Oktal-Schreibweise angegeben werden und muß mit '0' beginnen.
A
B
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.
echo aaa$( echo bbb )ccc
aaabbbccc
echo "aaa$(echo bbb )ccc"Die Ausgaben von Kommandos, die zwischen $(...) stehen, ersetzen eben diesen Ausdruck.
aaabbb
ccc
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)"speichern einen Zeilenvorschub in die Variable 'newline'.
newline="
"
newline2="$(echo %n)"Der abschließende Zeilenvorschub wird durch %c unterdrückt.
tab3="$(echo %t%t%t%c)"
esc=$(echo %e)
echo aaa.bbb-%c-ccc.ddd ; echo EEEWie man sieht, hat %c eine allgemeine Abbruchfunktion!
aaa.bbb-EEE
Verz="$( DIR )"
echo "$Verz%c"Die Dateiliste des aktuellen Verzeichnisses wird in die Variable 'Verz' gespeichert,
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%Das ist das echo-Kommando mit einem einzigen recht langen Argument,
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb%
______cccccccccccccccccccccccccccccccccc%
dddddd${SHV}dddddddddddddddddddddd%
eeeeeeeee$(DIR autoexec.bat)eeeeee%
ffffffffffffffffffffffffffffffffff"
Dies war eine Funktionalität des echo-Kommandos.
Aber die Shell kennt etwas ähnliches:
prints -ss24s-6s aaaaaaaaaaaa bbbbbbbbbbbbbbbbbbb %Trotz der Zeilenvorschübe (direkt nach dem Prozentzeichen) ist dies ein einziges Kommando,
cccc ddddddddddddddd%
ddddddddddddddddddddddd
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.
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" cccDie Shell entfernt sämtliche Zwischenraumzeichen, falls keine Maskierung vorliegt,
aaa bbb ccc
Verz=$(DIR)Mit dieser Verzeichnisliste kann man wenig anfangen, denn in der Variablen
Verz=`DIR`
echo *txtFalls keine Maskierung vorliegt und die Option 'set -f' nicht gesetzt ist, findet Dateinamenexpansion statt.
FALL.TXT POPE.TXT RAST.TXT
* Steht für beliebig viele (>=0) beliebige ZeichenAlle diese Spezials dürfen total beliebig in einem Suchmusterausdruck stehen,
? Steht für ein beliebiges Zeichen
[...] Zeichenklasse, steht für ein Zeichen, das darin vorkommt
[!..] Zeichenklasse, invertiert, ... darin nicht vorkommt
[-+][0-9]*Wie man sieht, arbeitet dieser Mechanismus rekursiv.
??*[abf-i]?*
\t_online\email20\[0-9]??*\adressen.dat
V=*txt
»FALL.TXT POPE.TXT RAST.TXT«
V="*txt"Man erkennt: wo immer die Shell unmaskierte Wildcards 'sieht', expandiert sie diese!
»*txt«
echo $V
FALL.TXT POPE.TXT RAST.TXT
echo "$V"
*txt
Ausdrücke der folgenden Art -mit Wildcards- sollten vermieden werden:
Denn hier führt die unmaskierte Variable pdir dazu, daß die Expansion im gesamten ArgumentCOPY $pdir\bsh\man\'*.*' $zdir\man
sind besser, oder set -f."$pdir\bsh\man\*.*" $zdir\man"$pdir"'\bsh\man\*.*' $zdir\man
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.
echo aaa$(( 222+333 ))bbbHier wurde eine Addition durchgeführt und das Ergebnis in das Argument eingefügt.
aaa555bbb
echo aaa(( a=222+333 ))bbb
aaabbb
echo $a
555
echo aaa(( a=222+333 ))${a}bbbHier fehlt das Dollar-Zeichen vor der Doppelklammer.
aaa555bbb
Die dritte Arithmetikvariante ist das let-Kommando:
let "a=(222*10)/4" a+=33 "b=a*zval, b-=$Fclt" ++aVier Argumente sind das, wovon zwei unmaskiert sein dürfen, da sie keine Shell-Spezialzeichen
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=123Nach der zweiten Zuweisung steht in der Variablen die Zahl: 12300
num=${num}00
(( A=(a<<2)¦(b>>4),Arithmetische Ausdrücke dürfen beliebig lang sein, 10000 Zeilen - wenn man will !
B=[(b<<4)&255]¦(c>>2),
C=[(c<<6)&255]¦d
))
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
Die Ausgabe-Zahlenbasis kann temporär folgendermaßen eingestellt werden:
echo Hex-Zahl: $((16#, 1234+5678))HDie Sprache C erkennt drei Zahlenbasen:
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'.
Die Variante für SCO-UnixWare ist praktisch vollständig 64-bittig.
Das sum-Kommando braucht nur etwa 1/4 Sekunde, um alle longs des 35MB-Servicepacks aufzuaddieren.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
Mit solch einem Integer-Zahlenbereich hat man wirklich 'ausgesorgt'.
Mit der ersten obenstehenden Zahl könnte man knapp 10
Millionen Tera-Byte adressieren !
.
echo $?Hier stammt die 1 von irgendeinem vorherigen Kommando
1
echo $?
0
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 &&Die Kommandos werden der Reihe nach nur ausgeführt, solange der Exit-Kode zu dem jeweiligen
kdoD && kdoE ¦¦ echo Fehler:kdoE
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'.
test ...Das Kommando ist mittels der drei Kommandonamen test, [ und [[ aufrufbar.
[ ... ]
[[ ... ]]
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 existiertDie beiden letzten Beispiele sind wegen doppelter Invertierung identisch.
[ -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
[ -f "$name" ] ...Denn, eine leere Variable ergibt bei $var 'Nichts', "$var" jedoch ergibt wenigstens "",
[ "$str" == packed ] && unzip ...
[[ "$flags" != ??1? ]] && continueBei der Doppelklammerschreibweise werden bei Zeichenkettenvergleichen
[ $# -gt 3 ] ...Das waren zahlenmäßige Vergleiche.
[ ${#sbk} -gt 6 ] ...
[ $sbk -le 64 ] ...
[ sbk -le 64 ] ...
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
Echo="echo abc ABC"Das ist mit der alias-Einrichtung fast gleich:
$Echo
abc ABC
alias Echo="echo abc ABC"Bei Aliases wird das Dollar-Zeichen weggelassen und es dürfen im Aliasnamen auch andere Zeichen
Echo
abc ABC
$( echo echo eeeee)
eeeee
$( echo eeeee )Dieser indirekte Kommandoaufruf per Kommando-Substitution dürfte klar sein.
bsh: Kommando nicht gefunden: 'eeeee'
$( grep "#@xyz" kdo_datei )Man stelle sich vor, eine Kommandodatei enthalte Zeilen mit speziellen Kennungen
Die vorverknüpften Handles:
0 Standardeingabe; ohne Umlenkung: TastaturDas echo-Kommando beispielsweise schreibt auf Handle 1,
1 Standardausgabe; ohne Umlenkung: Bildschirm
2 Standardfehlerausgabe; ohne Umlenkung: Bildschirm
echo aaa > PRN
> PRNIn beiden Fällen wird Handle 1 zum Drucker umverknüpft.
echo aaa
><
echo ddd > dateiZuerst wird 'datei' erzeugt oder aber auf Größe 0 gebracht, dann gelangt 'dddZV' in sie hinein.
echo eee >> datei
tr ab ba < eingabedatei > ausgabedatei
> datei1Hier ist eine while-Schleife zu sehen, mit einer weiteren darin verschachtelten.
6> datei2
< idatei
while readl izeile
do
echo 11111
print -ru6 222:$izeile
< iidatei
while readl iizeile
do
echo 111:$iizeile
done
><
done
><<<
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<&0Hier wird Handle 3 mit dem Gerät verknüpft, mit dem Handle 0 verknüpft ist - mit der Tastatur.
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
}
><
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 > dateiBeide Beispiele erzeugen eine leere Datei.
: > datei
kdo 2>NULDas ist eine Möglichkeit, Fehlerausgaben, die man nicht sehen will, 'in die Wüste' zu schicken.
kdo 2>/dev/null
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.mnkDas geht aber nur in Verbindung mit bsh-internen Einrichtungen!
echo aaxua > datei
tr xu ux < datei
echo aaxua ¦ tr xu uxDas zweite Beispiel zeigt die 'richtige' Pipeline, bei der manuell erzeugte Dateien nicht nötig sind.
echo axaua ¦ tr xu ux ¦ wcDieses ist eine mehrstufige Pipeline, bei der ein Kommando (tr) gleichzeitig
Es können Pipelines mit beliebig vielen Stufen gebildet werden.
[ ax -gt 0 ] && echo axaua ¦ tr xu ux ¦ wcPipelines können fast beliebig mit der anderen Syntax kombiniert werden.
Auch Shell-Funktionen können Bestandteil einer Pipe-Kette sein.
wc datei ¦ read l w cHier wurden die Variablen l, w, c mit der Anzahl Zeilen, Worte und Zeichen in 'datei'
Das Pipe-Zeichen ¦ beendet -wie Semikolon und Zeilenvorschub-
das Sammeln von
Kommandoargumenten, hat aber eben diese zusätzliche Pipe-Funktion.
i=77Mit set und array können mehrere Elemente auf einmal behandelt werden.
a[22]=AAA
let "b[33]=333, c[ 44+6]=5050, d[i]=777"
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]"Hier wurde auf Array-Inhalte zugegriffen, auch mit indexierenden Ausdrücken.
let "y+= b[ k+1 ]-d[2]"
shv="aaa bbb ccc ddd"Die Variable 'shv' wird dadurch zu einem Quasi-Array!
a=b b=c c=dddHier wurde zweifach indirekt über 'a' und 'b' auf den Inhalt von 'c' zugegriffen.
echo ${{{a}
ddd
local a=aaa b=bbb c d
static s t u=UUInnerhalb von Shell-Funktionen -und in fast allen anderen Umgebungen- können
In der FreeLizenz-Version der bsh können relativ wenige globale
(ca.20) und jeweils 'nur' 6 lokale
Variablen bzw. 'Namen' angelegt werden.
echo $0 $# $3 $9 ${100} $* $@Wie man sieht, liegt hier die Grenze nicht bei '9'.
ifdef shv && echo Variable %'shv%' existiertObiges bedarf wohl keiner weiteren Erklärung.
ifset a b c && echo a b und c exist. und sind nicht leer
unset shv b
ifset k && { kdo1; kdo2; kdo3; } &&
echo "Alles wegen 'k' korrekt ausgeführt!"
ifset j ¦¦ {Hier wurden Kommandos zusammengefaßt, jeweils von einer Bedingung abhängig gemacht.
print -u2 "Fehler: 'j' nicht gesetzt!"
kdo ; kdo
kdo && { kdo &¦ kdo;}
echo So allerlei gemacht. ; exit 2
}
if [ $a == Kern ]Es sind beliebig viele 'elif' möglich.
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
case "$a" inDer Inhalt von 'a' wird mit den Fall-Mustern) der Reihe nach verglichen.
[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
while :Die Schleifenbedingung steht zwischen while und do.
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 verschiedenen Syntax-Konstruktionen verarbeiten die Kommandos:
break nzum Abbruch, Fortsetzen bei Bedingung, Abbruch und Verlassen einer Shell-Funktion.
continue n
goend n
return e
echo aaa bbb ccc $b ddd $( kdo) eee *.txt $*
for a in aaa bbb ccc $b ddd $( kdo) eee *.txt $*Das echo-Kommando soll deutlich machen, wie die Schleife funktioniert:
do
echo $a
done
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 )Hier werden für jeden Durchlauf die drei angegebenen Variablen auf einmal gesetzt.
do ...; done
< dateiZum zeilenweisen Lesen aus einer tabellarischen Datei bietet sich obige Variante an.
while read n m M -
do
echo $n:$m:$M
done
><
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.; doneDieses ist eine Zahlen-Schleife, die mit einer Zahl durchläuft, von einem Anfangswert bis zu
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() {Verschachtelung mit der zusätzlichen Klammer ) bedeutet, daß man innerhalb einer Funktion
Beliebig viele
beliebige
Kommandos
ifset vf && verschachtelt()) { ...; )}
return 0
}
stop() { read "-?<Enter> "; }
stopDies ist eine kleine Stop-Funktion: man muß Enter drücken, damit's weiter geht.
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?" && exitDies ist eine praktische Abfrage-Funktion.
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'.
. 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!Reguläre Ausdrücke wirken sehr kompliziert, wenn man sich fertige Ausdrücke anschaut.
Bei [-...] oder [...-] ist '-' ein gewöhnliches Zeichen!
^ und $ sind nur am Anfang bzw. am Ende speziell!
(Maskierung durch Position)
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'.
Anselm Katastroff <AnseKat@t-online.de>
expr "$zeile" :mail '<%([^<>]%{5,%}%)>'
expr "$zeile" :name '^%([^<>]%{1,%}[^<> ]%) *<'
echo $name%n$mailDer Teilausdruck [^<> ] ist wichtig, falls vor < mal mehrere Leerzeichen stehen sollten.
Anselm Katastroff
AnseKat@t-online.de
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.
Ersetze() {Zuerst wird geprüft, ob das Suchmuster überhaupt vorhanden ist ($2 in $1).
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
Ersetze aaabbbccc "cc" CC
aaabbbCCc
Ersetze aaabbbccc "aa*" A
Abbbccc
Ersetze aaabbbccc "bbc" BBcFalls mehrere Vorkommnisse eines Suchmusters ersetzt werden sollen, kann die Funktion
aaabBBccc
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.
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%[%([^]]*%)]%[%([^]]*%)]' %Es ist schon 'ein Hammer', was man mit echten(!) RAs machen kann.
'Aik[%2][%1][k]'
Aik[i+1][23][k]
expr 'A[k[i]][sec]' : 'A%[%(..*%)]%[%(..*%)]' 'A[%2][%1]'Diese Variante wirkt unsicherer eingegrenzt, funktioniert aber ebenfalls und ist kürzer.
A[sec][k[i]]
'%(..*%)%1'
abcabcObiger RA paßt zu allen nichtleeren Zeichenketten, die aus zwei genau gleichen
aaaa
k3rk3r
'+' 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).
# fork '/bin/sleep 30' '/bin/sleep 40'Hintergrund-Prozesse und wait gehören zusammen.
1222
1224
# wait 1222
1222
# wait
1224
# _
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 .
Hintergrund-Prozesse sind sinnvoll bei langsamen Speichervorgängen
von/zu Geräten,
oder bei Daemons, die etwas überwachen sollen.
kill [-signo] pid ...
# echo $$Mit kill sendet man Signale zu Prozessen.
1314
# kill 1314
bsh: *** Signal 15 *** ...
$ _
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' .
.
. : 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
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
. Skript-Aufruf (. datei arg ...)Siehe bsh(K), xyz(K).
: 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
bshOption -E startet gleich den Kommandozeilen-Editor; innerhalb: set -E ; set +E
bsh -E
Startet ein Shell-Script von 'außen' und am bsh-Prompt:
bsh script.bsh [arg...]
# script.bsh [arg...]Startet für eine Kommandoausführung:
# . script.bsh [arg...]
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.
In Ihrer \autoexec.bat sollten Sie mindestensWichtig ist, daß die Programme bsh.exe, man.exe, pg.exe, upat.exe 'im Pfad' (PATH) liegen
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
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 .
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:
Danach wird z.B. bei# COMSPEC=...\CMD.EXE
# export COMSPEC
cmd.exe benutzt, der NT-Interpreter, der mit seinen Kommandos ja lange Dateinamen# DIR
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.
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.