Serveradministration – DaFRK – Online Brainnailing At It's Best http://dafrk-blog.com Servers, Foods and Other Nerd-Stuff Tue, 12 Apr 2016 22:49:03 +0000 de-DE hourly 1 https://wordpress.org/?v=4.4.2 Linux Shell Skripting Tutorial http://dafrk-blog.com/?p=4177 http://dafrk-blog.com/?p=4177#respond Tue, 09 Feb 2016 22:54:20 +0000 http://dafrk-blog.com/?p=4177 Linux Shell Skripting Tutorial weiterlesen →]]> Grundlagen

Mit Linux Shell Skripting könne Shell-Befehle automatisiert ausgeführt werden. Ich persönlich gehe davon aus, dass Sie grundsätzlich schon einmal auf einer Linux-Shell unterwegs waren und deshalb schon von den grundlegenden Kommandos etwas gehört haben, wie beispielsweise cat, grep, ls usw., und auch mit einem TExteditor wie vi oder nano schon zu tun hatten.

Skripte sind Dateien, die man bestenfalls mit der Endung .sh abspeichert, damit sie als Shell-Skripte deutlich erkennbar sind. Solche DAteien kann man von jedem Ordner aus ausführen (egal in welchem print working direcotry man sich gerade befindet), wenn man diese in einem Pfad ablegt, der in der PATH-Variable hinterlegt ist (die PATH-Variable enthält Ordner, in denen die Shell automatisch nach Binaries wie grep, ls oder cat sucht. Befindet sich dort ein Skript, wird es gefunden und kann ausgeführt werden, ohne dass man den vollen Pfad zum Skript angeben muss).

Neben Skriptdateien werden in der Unix/Linux-Welt zur Automatisierung von Aufgaben häufig auch Programmiersprachen wie Ruby, Python oder – am beliebtesten – Perl verwendet. In einem anderen Post habe ich versucht zu erörtern, welche Sprache denn in welchen Situationen zur Automatisierung besser geeignet ist – ein  Perl-Programm oder ein Shell-Skript.

Damit diese Dateien überhaupt ausgeführt werden können, müssen in den chmod-Dateiberechtigungen für den User, der die Datie ausführt, Ausführrechte vergeben werden.

chmod u+x <skript>

Skripte werden ausgeführt mit

sh <skriptdatei>

#oder

bash <skriptdatei>

#oder 

zsh <skriptdatei>

#oder

csh <skriptdatei>

oder, wenn die sogenannte Shebang-Zeile gepflegt ist (siehe weiter unten), indem man einfach nur die Datei ausführt

mein_skript.sh

jenachdem, mit welcher Shell man das Skript ausführen möchte. Die beiden häufisgten Shells sind bash und sh. Die meisten Skripte solltet ihr mit der bash ausführen, da die meisten Skripte speziell für die bash geschrieben werden.

Wenn ein Skript fehlerhaft geschrieben wird und es nicht sauber durchläuft, sieht man leider erstmal nicht, warum. Damit man eventuelle Fehlermeldungen beim Ausführen eines Skripts sieht, muss man den PArameter -x mitgeben, also bspw.

bash -x <skriptdatei>

Am Anfang eines Shell-skritps gibt man an, mit welcher Shell man es ausführen möchte. Dazu schriebt amn in die erste Zeile

#!/bin/bash

Dies ist die sogenannte Shebang-Zeile. Damit ist sichegrestellt, dass egal mit welcher Shell man das skript ausführt, am Ende doch die richitge Shell genutzt wird. Selbst wenn ich dieses skript jetzt mit

csh mein_skript.sh

ausführe, wird am Ende doch die bash verwendet. ist die Shebang-Zeile gepflegt, kann man das Skript einfach nur über den Dateinamen ausführen.

Wenn Sie schon mal gurndsätzlich mit einer Unix/Linux-Shell gearbeitet haben, wird Ihnen bekantn sein, dass Sie mit dem kommando export <variablenname> Variablen setzen und über echo $<variablenname> den Wert der Variablen ausgeben können. Solche Variablen sind wichtig für das SEtzen von if else-STatements, also „Wenn dies dann mach das, wenn das andere dann mach jenes“-Verzweigungen in einem Skript.

unsere erste Verzweigung

if [[ $USER = "dafrk" ]]

    then

         echo "Hallo DaFRK"

    else

         echo "Du bist nicht DaFRK"

fi

If-Statements im Shlel-Skripting werden also mit dem Wort if und der Bedingung in doppelten eckigen klammern eingeleitet und mit fi geschlossen.

Die doppelten Eckigen Klammern werden genutzt, wenn man Zeichenketten testen möchte. Möchte man hingegen numerische Werte abfragen, also Zahlen, dann nutzt man doppelte runde Klammenr

if (( $ALTER >= 18 ))

    then

        echo "Zugriff erteilt"

    else

        echo "Sie sind noch nicht alt genug"

fi

Wenn wir dazwischen jetzt nochmal eine KOnditionsabfrage machen wollen, geben wir ein

if (( $ALTER >= 18 ))

    then

        echo "Zugriff erteilt"

      elif (($ALTER <=18 && $ALTER >= 16))

       then

        echo "Zwar noch keine 18, aber ist ja      nicht mehr lange :)"

      else

        echo "Sie sind noch nicht alt genug"

fi

Wichtig ist hier zu verstehen, dass man für das elif wieder ein then braucht, aber es kein schließendes file (elif rückwärts) gibt.

Code Snippets und Skeleton files mit vi

Wenn Sie Shell-Skripting auf einem bestimmten Level betreiben wollen, kommen Sie um einen professionellen TExteditor wie vi, vim oder emacs nicht herum. Mit anderen Texteditoren wie nano können Sie zwar auch die Skripte schreiben, es wird Ihnen jedoch ab einem gewissen LEvel mit vi und Co. wesentlich einfacher fallen. Im Folgenden zieg eich Ihnen am Beispiel vom Texteditor vi, warum.

Zum einen geht es darum, sich ein Code Snippet Directory aufzubauen. Code Snippets sind Codeschnipsel, die ihr immer wieder mal verwenden wollt. Diese Codeschnipsel speichert ihr in Textdateien, die ihr nennen könnt, wie ihr wollt. Beispielsweise könnt ihr euch in eurem home Verzeichnis einen ORdner ~/code_snippets machen und dort eine Datei if_string und eine Datei if_number erstellen, welche den Aufbau eines if-else-Statements für String (eckige Klammern) und Nummern (runde Klammern) enthalten.

Im Editor vi könnt ihr jetzt einen Code Snipped laden über das kommando

:r ~/code_snippets/if_number

Nun wird an der Position des Cursors der inahtl des Codeschnipseldatei eingefügt. So müsst ihr euch nicht immer alles merken und könnt solche Codeschnipsel einfach einfügen. Eure Codeschnipsel-Bibliothek wird so mit der Ziet immer größer und ihr spart euch dadurch sehr viel Zeit.

Nicht nur DAteien könnt ihr einlesen, sondern auch die Ausgabe von Befehlen. Beispielswiese könnt ihr euch in einer kommentierten Zeile notiern, wann ihr ein Skript erstellt habt. Nach unserer Shebang-Zeile machen wir eine neue Zeile und führen im vi das kommando

:r! date

aus. Nun wird in die Zeile das katuelle Datum ausgegeben. Mit einer Raute (#) am Anfang der Zeile kommentieren wir die Zeile ein und verhindenr somit, dass die Shell später versucht, die Zeile auszuführen.

Die nächste Feinheit ist, dass man mit dem Vi Tastenmakros erstellen kann. Beispielswiese können wir mit dem kommando

:map <F3> i#Das ist ein TExtbaustein <ESC>:r!date <ESC> jj

einstellen, dass jedes mal, wenn wir F3 drücken, der vi in den Einfüge-Modus geht (weil das drücken der i-Taste simuliert wird.) und dann an der Position des cursors einfügt „Das ist ein Textbaustein“ und dann das Drücken der Escape-Taste simuliert, um den Einfüge-Modus wieder zu verlassen. Danach wird die Ausgabe des Komamndos date an der aktuellen Cursorposition eingelesen. Danach wird zweimal die Taste „j“ simuliert, was beim vi dazu führt, dass der Cursor, falls möglich, um zwei Zeilen nach unten bewegt wird. Sinn macht das ganze natürlich nicht, ich wollte euch nur ziegen, was man alles auf eine simple Taste mappen kann.

Leider wird dieser map command standardmäßig nicht permanent gespeichert. Wenn wir wollen, dass das Key-MApping dauerhaft gepseichert wird, müssen wir das kommando foglendermaßen in die Datei ~./.vimrc reinschreiben.

map <F3> i#Das ist ein TExtbaustein <ESC>:r!date <ESC> jj

es fehlt also  der Doppelpunkt am Anfang, den wir normalerwiese im vi eingeben würden, um in den kommandomodus zu gelangen.

Ein weiteres wichtiges Feature ist, dass man mit dem Editor vi Skeleton-Files machen kann. Beispielsweise sollten wir ja am Anfang eines jeden Shell-Skripts die Shebang-Zeile schreiben. Wir können im editor vi festlegen, dass jedesmal, wenn wir mit dem Editor vi eine Datei mit der endung .sh erstellen, diese Zeile automatisch eingefügt wird. dazu fügen wir in unsere .vimrc ein:

  autocmd BufNewFile *.sh 0put =\"#!/bin/bash\<nl>"|$

DAs meiste können Sie sich sicher zusammenreimen. <nl> steht beispielsweise für new Line, also für einen Zeilenumbruch.

Denken Sie nur mal über die Möglichkeiten nach. WEnn Sie ein Webdesigner sind, können Sie somit automatisch ein Skeleton File für ein .html-Dokument erstellen usw.

Parameter übergeben

was ist nun, wenn ihr eurem Skript Parameter übergeben wollt? Wenn ihr auf der kommandozeile beim Aufrufen eines Skripts nach der Skritpdatei noch irgendwas hinten dran schriebt, z. B.

mein_skript.sh DaFRK dafrk-blog.com

dann wird in diesem Fall DaFRK in der Variable $1 und dafrk-blog.com in der variable $2 gespeichert.

Diese Variablen könnt ihr dann in einer verzweigung auswerten, beispielsweise

if [[ $1 = "DaFRK" ]]

 then

  echo "Hallo DaFRK"

 else

  echo "Du bist nicht DaFRK. Verpiss dich du   Schmock"

fi



if [[ $2 = "dafrk-blog.com" ]]

 then

  echo "Die Domain ist richitg"

 else

  echo "Die Domain ist falsch"

fi

Anstatt die PArameter jedoch direkt in der kommnandozeile einzugeben, könnt ihr den Benutzer auch bieten, interaktiv während der Ausführung des skripts PArameter festzulegen. DAs folgende kommando fragt erst den Benutzer nach seinem Namen und speicher diesen dann in der Variable NAME. Mit dieser Variable kann dann im wieteren Verlauf des Skripts gearbeitet werden.

echo -e "Wie ist dein Name, Schwachkopf? \c"

read NAME

<weitere Code, der danach asugeführt werden soll, nachdem der Benutzer seinen Namen eingibt>

Mit Hilfe dieser Variablen können Sie auch das Verhatlen Ihres Skriptes steuern. Wenn der Parameter $1 oder bspw. der Parameter $NAME diesen oder jenen Wert hat, soll sich das skript so oder so verhalten.

die Ausgbae von kommandos als Variable speichern

Doch nicht nur Eingabeparameter könnt ihr in Variablen speichern, sondenr auch den output von ganz normalen Linux-Kommandos. Die folgende Zeile speichert die Ausgabe von die Ausgabe des kommandos du -b /tmp/file1 | cut -f1 in die Variable GROESSE

GROESSE=$(du -b /tmp/file1 | cut -f1 )

oder alternative Schreibweise:

{ GROSSE=$(du -b /tmp/file1 | cut -f1 ); }

 

Inhalte von Dateien in Variablen speichern

Aber nicht nur die Ausgbae von kommandos können Sie als Variable speichern, sondern auch die inhalte von Dateien. Als Beispiel nehmen wir mal eine .csv-Datei, die verschiedene Werte im folgenden Format abgelegt hat

Hans,Müller,45,Rechnungswesen

Holger,Hauzenberger,33,Personalwesen

Alexandra,Altenbach,23,Marketing

Die folgende while-SChleife liest die vier Spalten Vorname, Name, Alter und Abteilung für jeden mitarbeiter ein.

$IFS=","

while read vorname name alter abteilung

 do

  echo -e "$vorname $name $alter $abteilung"

done < $1

Die Variable $IFS ist eine eingebaute Variable für Trennzeichen, welche die Funktionalität des Einlesens mehrere Werte in VAriablen unterstützt, wei wir es hier sehen. < $1 sagt hier, dass das Skript eine .csv-Datei einlesen soll, dessen Namen wir dem Skript als Paramter angeben. Wir würden das skript also beispielsweise ausführen über

meinskript.sh meine_mitarbeiter.csv

Das Skript gibt dann für jeden mitarbeiter die Werte aus, nur dass diesmal das komma verschwunden, sondern nur noch LEreezichen dazwischen sind. DAs ist aber nicht das Wichtige. Wichtig ist dass ihr seht, dass iwr in dem Skript mit den Variablen vorname, name, alter und abtelung arbeiten konnten.

Unsere zweite Verzweigung: case-Statements

neben if-else-Verzweigungen können wir auch caste-Statements nutzen. Diese machen ab einer gewissen Menge an Konditionsprüfungen Sinn, da man sich somit sehr viel Arbeit sparen kann. Scahut euch mal dieses Skript an.

#!/bin/bash



if [[ $1 = "verzeichnisse" ]]

 then

  find /etc -type d

 elif [[ $1 = "links" ]]

  then

   find /etc -type l

 elife [[ $1 = "dateien" ]]

  then

   find /etc -maxdepth 1 -type f

 else

   echo "So nutzt du das Skript: $0 verzeichnisse | dateien | links"

fi

Je nachdem, ob man im Skript links, dateien oder verzeichnisse eingibt, wird nach dem entsprechenden Dateityp im Verzeichnis /etc gesucht. Wird gar nichts eingegeben, wird eine Erklärung ausgegeben, wie man das Skript verwendet. Dieses Skript könnten wir mit Hilfe eines case-Statement swesentlich einfacher schreiben.

case $1 in

 "verzeichnisse")

  find /etc -type d

 ;;

 "dateien")

  find /etc -type f

 ;;

 "links")

  find /etc -type l

 ;;

 *)

  echo "So nutzt du das Skrpit: $0 dateien | verzeichnisse | links "

 ;;

esac

Merkst du, um wie viel einfacher der Code zu schreiben war? Ab einer gewissne Komplexität lohtn es sich also durchaus, case-Statements zu benutzen.

Reguläre Ausdrücke in Shell-Skripten

Wenn du meinen Post über Reguläre Ausdrücke gelesen hast, bist ud optimal darauf vorbereitet, diese in deinen Shell Skripts zu verwenden.

So trifft beispielswiese

case $1 in

[1-6]*)

 echo "eine Zahl, die mit einer Ziffer von 1-6 anfängt"

;;

 [7-9]?)

echo "eine dreistellige Zahl, die mit einer Ziffer von 7-9 anfängt"

;;

*)

echo "rigendwas anderes"

;;

esac

auf alle Zahlen, die mit einer Ziffer zwischen und 1 und 6 anfangen, also es trifft sowohl auf die Zahlen 1-5, aber auch auf Zahlen wie 500, 666, 654321 usw. zu.  Sie müssne sich nur merken, das sie den regulären Ausdruck bei if- und case-Statements mit dem Stichwort in einleiten müssen.

while loops

Schleifen (loops) setzt man ein, wenn man unter bestimmten Bedingungen eine Gruppe von Aktionen mehrmals hintereinander ausführen möchte.

Ein Beispiel für eine Endlosscheife, die ständig augseführt wird, wäre

while true

 do

 echo

done

Until loops

until Loops sind while-Loops sehr ähnlich.´Nur dass while-Loop etwas macht, solange eine Bedingung war ist, und der until Loop etwas macht, bis eine Bedingung wahr ist.

In diesem Zusammenhang prüfen wir mal, ob eine Datei auf unserem DAteisystem existiert. Die folgende Schleife wird so lange im 1-sekünigen Abstand durchgeführt, bis es im System eine Datei ~/hoer_endlich_auf.txt gibt.

until [-e ~/hoer_endlich_auf.txt ]

 do

  echo "Datei gibt's noch nicht'

  sleep 1

done

 echo "Ist gut, ich hör jetzt auf"

in der PRaxis empfinde ich jedoch das Konzept von until-Loops etwas überflüssig, schließlich kann man alles auch mit while Loops machen. Denn wir hätten ja auch einen while loop machen können, der so lange ausgeführt wird, solange es die Datei noch nicht gibt. Wenn Ihnen das Konzept der until-Loops allerdings gefällt, können Sie persönlich die schliefe gerne nutzen.

for loops

Skripte automatisiert ausführen lassen über Cronjobs

Wenn ihr wollt, dass eure Skripte zu einer regelmäßigen Zeit immer wieder ausgeführt werden, könnt ihr einen Cronjob einrichten, der das für euch macht. Dazu loggt ihr euch als der User ein, unter dem das skript ausgeführt werden soll, und gebt ein

crontab -e

es öffnet sich der Standard-Texteditor mit einer (vermutlichen leeren) Textdatei. in diese Datei tragt ihr Einträge im Format

minute (0-59), hour (0-23, 0 = midnight), day (1-31), month (1-12), weekday (0-6, 0 = Sunday), Befehl, also:

02 14 1 1 1 /root/skript.sh

ein. Dieser einträge würde /root/skript.sh jeden Montag am 01.01. um 14:02 ausführen, also einmal im Jahr. Der Eintrag

00 22 * * * /root/skript.sh

hingeegen führt das Skript jeden Tag um 22 Uhr aus.

Speichert das Textdokument und startet danach den Crondienst neu über

/etc/init.d/cron restart

von nun an werden die Einträge aus der Textdatei so regelmäßig ausgeführt, wie ihr das festgelegt habt.

Fehler im Skript mit Hilfe von trap und REturn Codes abfangen

 

Mit Trap lassen sich Skripte managen. Beispielsweise könnte man damit verhindern, dass ein Skript, welches auf einen Fehler stößt, Datenmüll hinterlässt. Wichtig dabie ist die Variable $$, die imemerauf die Prozess-ID des Prozesses zeig,t unter dem das Skript läuft. das kommando echo $$ in einem Skript würde also die Prozess-ID des Skripts ausgeben.

Desweiteren ist die Variable $0 wichtig, die den vollen Dateinamen zum Skript enthält, also den absoluten Pfad. Wenn ihr nur den Dateinamen (also nicht den kompletten Pfad) wollt, dann könnt ihr diesen ausgeben über

echo $(basename $0)

trap ermöglicht es uns, auf Signale zu reagieren, die ovn außen kommen. Beispielsweise aknn das Skript eine bestimmte Aktion ausführen, wenn der User versucht, das Ausführen des Skripts mit der Tastenkombination Strg+C abzubrechen. Wir können beispielsweise am Fanng unserer Skriptdatei schrieben

trap "rm temp_file.txt; exit" SIGHUP SIGINT SIGTERM

In idesem Fall wird das skript, wann immer der User versucht, über Strg+C oder schließen seiner Shell, das Skript abzubrechen, die Datie temp_file.txt löschen und danach das Skript selber abbrechen (exit-Kommando).

Sobald solche „sauber-mach“-Funktionen für solche Fälle, wo ein Skript, komplizierter werden, sollte amn sich nicht direkt in das trap-Statement schreiben, sondern sich eine Funktion machen (siehe unten), und dann die Funktion aufrufen. WEnn beispielsweise unsere Funktion namens clean_up_function alle Befehle enthält, die beim Abbrechen durch den Benutzer ausgeführt werden solen, können wir diese folgendermaßen ausführen lassen

trap clean_up_function SIGHUP SIGINT SIGTERM

Ihr könnt aber auch verhindern, dass das Skript mit dieser Tastenkombination abgebrochen wird, indem ihr beispielsweise festlegt:

trap 'echo " deaktiviert"' SIGHUP SIGINT SIGTERM

Statt dass das Skript abgebrochen wird, erhält der Benutzer jetzt jedesmal die Nachricht „deaktiviert“.

Nun könnt ihr euch vorstellen, dass dies eine Möglichkeit ist, auf Fehler im Skript zu reagieren. Wann immer in eurem Skript ein Kommando, welches im Skript festgelegt ist, nicht funktioniert, wird das Skript mit einem bestimmten Fehlercode beendet. en Fehlercode eines Befehls könnt ihr sogar komplett ohne Skript austesten. Wenn ihr beispielswiese im Skript touch.sh mit dem Befehl

touch /root/test.txt

versucht, die Datei test.txt im root-Homeverzeichnis zu erstlelen, und dieses kommando mit einem User ausführt, der im Verzeichnis /root/ nicht schreiben darf, könnt ihr euch den Exit-Code, den dieser -fehler verursacht, ansehen mittels

echo $?

die Variable $? liefert immer den Exit Code des letzten Kommandos zurück. In unserem Fall wäre das der Exit Code 1. Wenn ihr hingegen versucht, das selbe Kommando mit dem User root auszufürhen, der logishcerweise im Verzeihcnis /root schreiben darf, bekommt ihr den Return Code 0.

Was heißt das jetzt für euer Skript? DAs bedeutet für euch, ihr könnt beispielsweise in einer if-else- oder case-Verzweigung prüfen, mit welchem Exit Code ein bestimmtes Kommando ausgeführt wird, und abhängig davon auf die verschiedenen möglichen Fehler reagieren.

Es kommt noch besser: Ihr könnt sogar für eure Funktionen und Skripte bestimmen, mit welchem Fehlercode diese abschließen sollen. Beispielswiese könntet ihr euch ein Skript namens name.sh schreiben.

echo "Wie ist dein Name?"

read NAME

if [[$NAME = dafrk ]]

 then

  echo "Zugriff erteilt"

  exit 0

 else

  echo "Zugriff verweigert"

  exit 1

fi

Und diesen REturn Code könnt ihr jetzt in einem anderen Skript oder in einer anderen Funktion aufgreifen. Beispielsweise kann ich jetzt in einem anderen Skript namens auswertung.sh schreiben

name.sh

if (( $? = 0 ))

 then

  echo "alles klar"

  exit 0

 else

  echo "da ist aber was schief gelaufen"

  exit 1

fi

Das heißt Skript auswertung.sh verhält sich, abhängig davon mit welchem Exit Code das Skript name.sh abgeschlossen hat, unterschiedlich.

und selbstverständlich könnt ihr diese Exit Codes auch trappen. Beispielsweise möchte ich, dass wenn mein Skript touch.sh beim Estellen der Datei /root/test.txt fehlschlägt, die Funktion clean_up ausführt. Das erreiche ich über

trap clean_up 1

touch /root/test.txt

Wenn jetzt das Skript touch.sh mit dem Exit Code 1 abschließt, wird vor Beendigung des skripts die Funktion clean_up ausgeführt, welche ich an einer anderen Stelle im Skript definiert hätte.

Die mehrfache ausführung eines skripts mit Lock Files verhindern

Manchmal passiert es, dass das Betriebssystem oder auch ein User versehentlich ein Skript mehrfach ausführt. Das kann zum Problem werden, wenn man im skript beispielsweise sehr ressourcenintensive Prozesse, wie beispielsweise das Kopieren eines sehr großen Ordners, anstößt, und dieser prozess dann mehrfach gleichzeitig ausgeführt wird. Wenn ihr ein kopierskript 5-10 mal ausführt, wird eure Festplatte entsprechend 5-10 mal langsamer, als wenn ihr das skript nur einmal ausführt.

Bei Backup-Skripten kann es sogar passieren, dass ihr euch mit dem mehrfachen Ausführen eines backup-Skripts sogar euer Backup kaputt macht, weil die Skripte meistens das Backup in ein und die selbe datei sichern wollen (beispielsweise ein .tar.gz-Archiv) und dabei mehrere Skripte gleichzeitig versuchen, auf die Datei zu schreiben, was die lustigsten Ergebnisse haben kann.

Wie schützen wir jetzt also unser Skript davor, sich mehrmals auszuführen? Mit einer sogenannten Lock-Datei. Das skript prüft, ob eine Datei mit einem gewissen Namen existiert, die normalerweise erst nach dieser Überprüfung angelegt wird. Existiert die Datei aber bereits zum Zeitpunkt der Prüfung, weiß das Skript, dass das Skript vorher schon einmal angestartet wurde. Das Skript sagt dann „ok, eine Version von mir läuft bereits, ich beende mich jetzt“. Das Skript, welches dann als einziges läuft, löscht die Lock-Datei, sobald es fertig ist.

Wie könnten wir das nun beispielsweise realisieren? Nehmt beispielsweise mla dieses Skript

#!/bin/sh

LOCK=/var/tmp/lockfile.lock

if [ -f $LOCK ]; then

  echo Skript läuft bereits du Honk

  exit 1

fi

touch $LOCK

# das eigentliche skript

rm $LOCK

exit 0

Das Skript speichert den Standort des Lockfiles in einer variablen und prüft, ob die Datei beriets existiert. Existiert die Datei bereits, erkennt dies das skript und beendet isch mit einem Return Code von 1.

Existiert die Datei hingegen nicht, wird diese angelegt, bevor das Skript seine eigentliche Arbeit vollzieht. Nachdem das Skript durchgelaufen ist, löscht es die sperrdatei und beendet sich mit Return Code 0.

Das Lock File solltet ihr logischerweise auf eine lokale Festplatte schrieben lassen, denn auf einer Netzwerkfreigabe kann es sein, dass die Netzwerkfreigabe an sich nicht erreichbar ist und daher das skirpt Probleme hat, die Datei überhaupt anzulegen. solange das skript die Datie nicht anlegen kann, wird das Skript nicht weiterlaufen bzw. sich selbst beenden, was dazu führt, dass euer skript nie anfängt zu arbeiten.

Einige werden sich vielleicht denken, dass dies aber auch zu Problemen führen kann. Und zwar genau dann, wenn zwischen dem Anlegen der sperrdatei und dem löschen der sperrdatie etwas schief geht. Stellt euch vor, ihr schreibt ein Skript, welches jeden Tag ein Backup von eurem Webserver machen soll, und irgendwann schlägt das skript fehl, beispielsweise weil nicht genügend Speicherplatz frei war, um das Bakcup zu schreiben. Ihr räumt den Speicherplatz wieder frei, vergesst aber, die Lock-Datei zu löschen. Das würde bedeuten, dass am nächsten Tag und auch die darauffolgenden tage das skript nicht mehr läuft, weil die Lock-Datei ja da ist.

Deswegen ist es wichtig, dass ihr return Codes, die beim fehlerhaften Ausführen eines schrittes in eurem Skript entstehen könne, entweder mit einem if statement abfrägt, wie oben schonmal gezeigt

#befehl um ein Backup zu erstellen

if (( $? = 1 )) # Backup hat nicht funktioniert

 then

  #Lock-Datei löschen

fi

oder gleich alle return Codes, die in so einem Fall vorkommen können, trapt, wie oben ebenfalls schonmal gezeigt. so stellt ihr sicher, dass die Lock-Datei auch immer dann gelöscht wird, wenn euer Skript auf einen Fehler trifft.

Logging in eurem Shell-Skript

WEnn ihr irgendwann anfangt, eure shell-Skripte automatisiert ausführen zu lassen, habt ihr ein Interesse daran, eione Übersicht darüber zu generieren, ob eure skripte sauber durchgelaufen sind, oder nicht. Daher macht es Sinn, Log-Dateien zu schreiben, die aufzeichnen, wann ein Skript sauber beendet wurde und falls nicht, an welcher Stelle es auf einen Fehler gestoßen ist.

Der erste Schritt ist, das Logging seiner Shell-Skripte logisch aufzutrennen. Fehlermeldungen im Skript sind in der Regel wesentlich interessanter als das kommando an sich.

Wenn ihr ein Skript ausführt mit folgender Zeile

meinskript.sh 2> errors.log

dann wird, wenn das Skript auf einen Fehler stößt und eine Fehlermeldung ausgibt, diese fehlermeldung nicht am Bildschirm ausgegeben (und wäre danach für immer verloren), sondern permanent in die Datei errors.log gespeichert, wo ihr euch die fehlermeldung am nächsten Tag beispielsweise, wenn ihr kontrollieren wollt, ob euer Skript durchgelaufen ist, anschauen könnt. Und zwar nur die Fehler. Würdet ihr nicht nur die fehlermeldungen in eine, sondern auch die normalen Meldungen des Befehls in eine andere datei loggen wollen, dann würdet ihr schreiben

meinskript.sh 1> normale_meldungen.log 2> errors.log

somit werdne normale meldungen in die Datei normale_meldungen.log und die fehlermeldungen in die Datei errors.log geschrieben.

Wenn ihr hingegen beide Angaben in eine einzige datei schreiben wollt, würdet ihr schreiben

meinskript.sh 1>> skript.log 2>> skript.log

somit werden beide meldungsarten in eine Datei geschrieben. Die doppelten pfeilspitzen >> zeigen an, dass eine Ausgabe die anderen nicht überschreibt, sondern dass diese Ausgaben aneinander gehängt werden. Sonst würde beispielsweise eine Ausgabe einer fehlermeldung dazu führen, dassa alle bisher geschriebenen normalen Meldungen gelöscht (genauer: überschrieben) werden.

Wenn ihr nicht nur die Meldungen, sondern auch die Befehle loggen wollt, die das skript ausführt, müsst ihr das skript so ausführen

bash -x meinskript.sh 1>> skript.log 2>> skript.log

Durch den Parameter -x führt die bash das skript so aus, dass auch alle Befehle „mit ausgedruckt“ werden, die dann über den File Descriptor 1>> in eure Datei skript.log mit aufgenommen werden. Würden wir wollen, dass die befehle zusammen mit den normalen meldungen in eine Datei skript.log und alle Fehlermeldungen in eine Datei errors.log geloggt werden, würden wir hingegen wieder schreiben

bash -x meinskript.sh 1> skript.log 2> errors.log

Nun kommt ihr vielleicht auf die Idee, die rückmeldung eines Befehls in einer Variablen speichern  zu wollen, um die rückmeldung des Befehls in eurem Skript wiederverwenden zu können. als erstes werdet ihr hierbei sowas probieren wie bspw.:

{ error=$(befehl 2>&1); }

Um diese zeile verstehen zu können, müsst ihr euch in Erinnerung rufen, dass wir mit der schreibweise { <variablenname>=$(befehl); } die Ausgabe eines Befehles in eine Variable speichern. Die Ausgabe eines Befehls gebt ihr mit dem File Descriptor >1 aus, den wir in der Praxis weg lassen können, da dieser file Descriptor standardmäßig immer ausgegeben wird. Deswegen würden wir, wenn wir nur die normalen meldungen eines Befehls speichern wollen würden, schreiben

{ error=$(befehl); }

Da wir nun aber auch die Fehlermeldung speichern wollen, bedienen wir uns einem Trick, indem wir die den File Descriptor für Fehlermeldungen, also 2>, in den File Descriptor für normale Ausgbaen, also 1> umleiten, weswegen die Zeile

{ error=$(befehl 2>&1); }

zustande kommt. Was aber, wenn ihr nur die Fehlermeldung und nicht die normalen ausgabemeldungen des Befehls speichern wollt? In diesem Fall müssen wir den normalen Output des Descriptors 1> terminieren, also explizit raus nehmen, damit dieser normale output nicht mit in die Variable $error gespeichert wird. Damit das geshcieht, leiten wir den inhalt von 1> nach /dev/null um

{ error=$(command 2>&1 > /dev/null); }

Wenn ihr hingegen Fehlermeldung und normale Ausgabe in zwei verschiedene Variablen speichern wollt, dann macehn wir:

{ error=$(command 2>&1 1>&$out); }

Jetzt befindet sich dank unserer Umleitung von 1> in $out  einzig und allein nur noch die ausggabe von 2>, also dem Fehlerkanal, in der variable $error. Und damit lässt sich jetzt nämlich arbeiten.

#!/bin/bash

{ error=$(Testbefehl 2>&1 1>&$out); }

if (( $? = 1 ))

 then



  echo Fehler bei Testbefehl, Fehlermeldung     lautet: $error > errors.log

fi

Sofern das letzte kommando mit einem Return Code von 1 fehl schlägt, schreibt das skiprt also

Fehler bei Testbefehl, Fehlermeldung lautet: <Fehlermeldung>

in die Datei errors.log. Den normalen output hätten wir über die Varable $out wo anders hin schreiben können, wenn wir wollen.

Natürlich könnt ihr auch nur Fehlermeldungen einzelner befehle innerhlab eures Skripts auf diese Art und Weise loggen lassen. mit Hilfe der Return Codes, die wir von weiter oben können, könnten wir damit beispielsweise etwas bauen wie hier:

#!/bin/bash



Befehl 1

if (( $? = 1 ))

 then

  echo Fehler bei Befehl 1

fi

 

 

skript oder schleife nach einer gewissen Zeitspanne automatisch beenden

Ebenso wie ihr verhindern wollt, dass euer skript zu oft läuft, weshalb wir Lock Files eingeführt haben, wollt ihr auch verhindern, dass euer Skript in einer endlosschleife durchläuft, weil es immer und immer wieder versucht, etwas zu machen, damit aber nicht fertig wird. Wenn euer Backup-Skript 24 stunden lang versucht, ein Backup von eurem server zu machen, verhindert es die Ausführung des Backup-Skripts am nächsten Tag, da es seine Lockdatei niemals löschen wird.

Ebenso können Skripte, die in einer Endlosschleife festhängen und dauernd etwas am System machen, unnötigg Systemressourcen verschlingen und damit sogar dazu führen, dass das System dadurch hängt.

Wenn ihr wollt, dass euer skript nach einer maximalen Laufzeit von 2 Stundeen automatisch beendet wird, dann startet ihr euer Skript mit dem Befehel timeout

timeout 2h meinskript.sh

Wenn ihr hingegen nur eine schleife, beispielsweise einene while-loop in eureem Skript, nach einer bestimmten Zeit automatisch verlassen wollt, dann könnt ihr die Variable $SECONDS nutzen, welche immer die Zeit enthält, die euer skript bereits läuft.

#! /bin/bash

end=$((SECONDS+30))



while [ $SECONDS -lt $end ]; do

    # Do what you want.

    :

done

Zuerst wird diee Variablee $end auf 30 Sekundne gesetzt, danach wird in der while-Schleife bei jedem Durchlauf geprüft, ob die Anzahl an Sekundene in der variable $SECONDS noch kleiner ist als die in der Variable $end. Wenn ja, wird die schleife noch ausgeführt.

expect

Ein Problem im shell-Skripting, welches es zu lösen gibt, ist, wenn die Shell Rückfragen vom User erwartet, beispielsweise wenn ihr selbst nicht Administrator auf dem Linux System seid und versucht, über ein skript einen Befehl mit sudo auszuführen.

sudo mkdir /root/testordner

dann wird euch die Kommandozeile beim Ausführen dieses Befehles nach dem Passwort dees root-Benutzers (oder eures eigenen) fragen:

[sudo] password for user root:

Wie reagiert man nun in einem sheell skript auf so eine rückfrage? Mit expect. Damit expect funktioniert, brauchen wir erst eine  andereShebang-Zeile in unseerem Skript

#!/usr/bin/expect

somit nutzen wir in unsereem Skript die expect-Binary. Nach diesen beidene Shebang-Zeilen kommt dann das eigentliche Skript.

Damit expect auf eine Rückfrage eeines kommandos reagieeren kann, müsst ihr den Kommando mit dem Schlüsseelwort spawn ausführen

#!/usr/bin/expect

#!/bin/bash

spawn sudo mkdir /root/testordner

Danach müsst ihr expect mitteilen, auf welche Rückfragee der Kommandozeile es reagieren soll, also beispielsweise

#!/usr/bin/expect

spawn sudo mkdir /root/testordner

expect "\[sudo\] password for root:"

Jetzt wartet das skript auf die aufforderung [sudo] password for root: Jetzt geben wir noch an, was passieren soll, wenn diese rückfrage auf dem Bildschirm erscheint

send "password\n"

Damit senden wir die Tastaturanschläge für das Wort password und das \n sagt expect, dass es dann Enter drücken soll.

in einem Skript, welches mit der shebang-Zeile

#!/usr/bin/expect

eingeleitet wird, muss man jedesmal mit spawn und expect arbeiten, das heißt man kann nicht wie in eineem normalen Shell Skript gnaz normal seine kommandos runterschreiben.

Deswegen empfiehlt es sich in der Praxis, erst ein normales Shell-Skript zu schreiben und dan dne Stellen, wo man expect braucht, ein zweites Skript auszuführen, welches explizit nur die expect-Kommandos enthält.

Funktionen

Wenn Sie mehrere male das gleiche machen müssen, nervt es Sie vielleicht, dass Sie mehrere Befehlsketten mehrfach schreiben müssen. Um dies zu vermeideen, können Sie sich Funktionen schreiben. Statt

Befehl1

Befehl2

Befehl3

Befehl4



Befehl1

Befehl2

Befehl3

Befehl4



Befehl1

Befehl2

Befehl3

Befehl4

zu schreiben, können Sie in ihrem skript erst eine Funktion schreiben.

function testfunktion()

{

Befehl1

Befehl2

Befehl3

Befehl4

}

und dann diese Funktion einfach nur noch dreimal im Skript aufrufen.

testfunktion

testfunktion

testfunktion

Das spart nicht nur Schreibarbeit, sondern auch die Möglichkeit, dass sie sich an einem der 12 komamndos, die Sie sonst tippen müssten, sich veerschreiben.

Sie können auch Parameter übergeben. Beispielweise wollen wir unserem Skript einen Dateinamen und eein Passwort übergbeen, dann defeinieeren wir die Funktion so

function testfunktion($dateiname, $passwort) {

     Befehl1

     Befehl 2

     Befehl 3

}

Damit können wir dann die Funktion in unserem Skript aufrufen übeer

testfunktion(/pfad/zur/datei, meinPasswort)

Die Werte /pfad/zur/datei und meinPasswort weerden dann innerhalb der funktion in den Variablen $dateiname und $passwort gespeichert, die in den Befehlen innerhalb der funktion verwendet werden können, um mit diesen Werten zu arbeiten.

System-Health-Skripte

Zum Abschluss dieses Tutorials zeige ich Ihnen noch, wie Sie den Output eines Kommandos für Skripte nutzen, die Ihr System am Laufen halten.

Ich habe mir ein skript geschrieben, welches jede Stunde über das Kommando

/etc/init.d/mysql status | grep Uptime

test, ob der server läuft. Wird der String gefunden, beendet das Kommando mit exit Code 0, wird ere nicht gefunden, mit Exit Code 1. Somit kann ich feeststellen, ob der server läuft, oder nicht. In meinem Skript kann ich dann mit einer if-Abfrage den mysql-Server neu starten, falls er nicht läuft

/etc/init.d/mysql status | grep Uptime

if (( $? = 1)

 then

  /etc/init.d/mysql start

fi

 

 

 

Wenn dir dieser Post gefallen hat, teile ihn doch auf Social Media, abonniere und verfolge mich per E-Mail, RSS-Feed, Social Media oder registriere dich auf meiner Seite. Wenn du registriert bist, like den Post bitte, wenn er dir gefallen hat!

]]>
http://dafrk-blog.com/?feed=rss2&p=4177 0
Festplatten unter Windows und Linux gleichzeitig nutzen http://dafrk-blog.com/?p=4243 http://dafrk-blog.com/?p=4243#respond Sun, 07 Feb 2016 17:38:32 +0000 http://dafrk-blog.com/?p=4243 Festplatten unter Windows und Linux gleichzeitig nutzen weiterlesen →]]> Es gibt verschiedene Szenarien, in denen Sie eine Festplatte sowohl unter Windows als auch unter Linux nutzen wollen. Beispiel 1: Sie haben auf Ihrem PC ein Dual Boot System eingerichtet, auf welchem sowohl Windows als auch Linux installiert sind und Sie hin und wieder das betriebssyste, mit welchem Sie arbeiten, wechseln

Beispiel 2: Sie befüllen eine externe Festplatte auf einem Windows- oder Linux-Desktop-Rechner, wollen diese aber danach auf einem Server im Netzwerk freigeben, der das jeweils andere Betriebssystem nutzt.

Welches Dateisystem soll die festplatte bekommen?

Die erste Frage, die Sie sich stellen ist: Mit welchem Dateisystem soll ich die Festplatte formatieren? Ist es besser, ein windowsnahes Dateisystem wie NTFS oder exFAT zu benutzen, oder ein linuxnahes Dateisystem wie ext4?

Meine Antwort zu dieser Problematik: Wenn Sie eine externe Festplatte abwechselnd an einem Windows und Linux Desktop Rechner nutzen wollen, dann nehmen Sie ein windows-nahes Dateisystem. Wenn Sie die Festplatte hingegen im Netzwerk auf einem Server hosten wollen, dann nehmen Sie das Dateisystem, welches dem Server am nächsten ist, sprich: bei einem Windows-Server NTFS oder exFAT, bei Linux ext4 oder ext3.

auf Linux Rechner Festplatten größer 2 TB mounten über GPT Partitionstabelle

Wenn Sie Ihre Festplatte mit einer Kapazität von mehr als 2 TB auf einem Linux-System einbinden wollen, müssen Sie zudem sicher stellen, dass die Festplatte, die Sie verwenden, mit einer GPT-Partitionierungstabelle formatiert wird. Denn eine alte MBR-Partitionstabelle unterstützt standardmäßig nur Festplatten mit maximal 2 TB.

Am besten ist es, Sie richten die GPT-Partitionstabelle ein, bevor Sie die Festplatte zum ersten Mal mit Daten befüllen.

prüfen, ob eine Festplatte bereits eine GPT Partitionstabelle hat

unter Windows starten Sie eine eingabeaufforderung als Administrator und geben ein

C:\Windows\system32>diskpart

diskpart>list disk

In der Ausgabe des kommandos list disk sollte es in der Spalte GPT in der Zeile der betreffenden Festplatte ein Kreuz geben. Ist dieses nicht vorhanden, ist die Platte noch MBR.

Alternativ zeigt ihnen das Tool MiniTool Partition Wizard Free an, ob die Festplatte mit MBR oder GPT intitialisiert wurde.

2016-02-07_18h03_52

Unter Linux hingegen nutzen Sie das kommando

parted -l

Sie bekommen einen Output, dessen erste Zeilen so aussehen

Model: ATA TOSHIBA THNSNS25 (scsi)

Disk /dev/sda: 256GB

Sector size (logical/physical): 512B/512B

Partition Table: msdos

Der für sie interessante Eintrag ist fett markiert. Steht an dieser Stelle msdos, ist die Festplatte noch mit einer MBR-Partitionstabelle initialisiert. Ansonsten würde hier gpt stehen.

 GPT frisch unter Windows einrichten

Wenn Sei die Festplatte unter Windows formatieren, stecken Sie die Festplatte am Windows-Rechner an. Wir gehen in diesem Szenario davon aus, dass Ihre Festplatte keine produktiven Daten enthält. Wenn Sie eine Festplatte konvertieren wollen, die wichtige Daten enthält (sie die Festplatte also nicht formatierne wollen), dann befassen wir uns damit weiter unten. In diesem Szenario löschen Sie, falls vorhanden, alle existierenden Partitionen auf der fesplatte über die Windows-Datenträgerverwaltung. Rechtsklicken Sie dazu auf das Volume und wählen Sie Volume löschen.

Danach ist ihre festplatte in einem Zustand, in welchem Sie die Platte erst neu initialisieren müssen. Wählen Sie Datenträger initialisieren. Sie werden gefragt, welche Partitionstabelle der Datenträger nutzen soll. Wählen Sie GPT.

Alternativ können Sie diese Schritte auch über die kommandozeile durchführen. Dazu nutzen wir das kommandozeilentool diskpart

C:\Windows\system32>diskpart

diskpart>list disk

Besorgen Sie sich aus der Ausgabe des list disk-Kommandos die Nummer des Datenträgers. Diese wählen wir nun aus über

diskpart>select disk <nummer>

und formatieren diese über

diskpart>clean

Nach dem Formatieren könne wir die Festplatte mit einer GPT-Partitionstabelle neu initialisieren über

diskpart>convert gpt

Nun ist die Festplatte mit einer gpt-Partitionstabelle intitialisiert.

GPT frisch unter Linux einrichten

Wir gehen in diesem Szenario davon aus, dass Ihre Festplatte keine produktiven Daten enthält. Wenn Sie eine Festplatte konvertieren wollen, die wichtige Daten enthält (sie die Festplatte also nicht formatierne wollen), dann befassen wir uns damit weiter unten.

 

Wie Sie eine Partition mit GPT-Partitionstabelle unter Linux erstellen, habe ich Ihnen hier schon einmal gezeigt. Und zwar geht das über den parted-Befehl mklabel. Dort geben Sie dann gpt ein.

mklabel

new disk label type? gpt

eine bestehende Platte nach GPT konvertieren unter Windows

Wenn Sie die Festplatte hingegen bereits befüllt haben, wollen Sie vielleicht nicht die Festplatte neu formatieren. Denn wenn Sei die Festplatte neu formatieren, müssen Sie diese nach der Formatierung neu befüllen, was bei einer festplatte mit einer Kapazität von mehr als 2 TB durchaus seine Zeit dauern kann.

Deswegen gibt es sowohl unter Windows als auch unter Linux Möglichkeiten, um eine bestehende Festplatte mit MBr-Partitionstabelle nach GTP zu konvertieren. Trotzdem sollten Sie vorher ein Backup der Daten auf einen anderen Datenträger, beispeilsweise eine zweite Festplatte, vornehmen, bevor Sie diese Konvertierung versuchen, da dieser Prozess grundsätzlich ein Risiko darstellt, vor allem wenn er im laufenden Betrieb unterbrochen wird (etwa durch einen Stromausfall).

Unter Windows können Sie das Tool Partition Wizard Free Edition verwenden, um eine bestehende Festplatte mit MRB-tabelle nach GPT zu konvertieren. Installieren Sie das tool, starten es als ADministrator und wählen in der Übersicht der Datenträger den zu konvertierendne Datenträger aus.Wählen Sie dann im Menü Disk unter Convert MBR Disk to GPT Disk. Ist die Aktion ausgegraut, stellen Sie sicher, dass Sie die Disk und nicht die Partition mit der Maus ausgewählt haben. Die aktion wird in diesem moment noch nicht ausgeführt. Jetzt gehen wir noch vorher über die Windows Systemsteuerung zu Energieoptionen und wählen Energiesparplaneinstellungen bearbeiten. Dort stellen wir sicher, dass bei Energiesparmodus nach der eintrag niemals aktiviert ist. Das verhindert, dass der Konvertierungsvorgang unerwünscht unterbrochen wird.   Jetzt können wir im programm noch auf den Button Apply klicken, damit die Konvertierung gestartet wird.

eine bestehende Platte nach GPT konvertieren unter Linux

Wenn Sie die Festplatte hingegen bereits befüllt haben, wollen Sie vielleicht nicht die Festplatte neu formatieren. Denn wenn Sei die Festplatte neu formatieren, müssen Sie diese nach der Formatierung neu befüllen, was bei einer festplatte mit einer Kapazität von mehr als 2 TB durchaus seine Zeit dauern kann.

Deswegen gibt es sowohl unter Windows als auch unter Linux Möglichkeiten, um eine bestehende Festplatte mit MBr-Partitionstabelle nach GTP zu konvertieren. Trotzdem sollten Sie vorher ein Backup der Daten auf einen anderen Datenträger, beispeilsweise eine zweite Festplatte, vornehmen, bevor Sie diese Konvertierung versuchen, da dieser Prozess grundsätzlich ein Risiko darstellt, vor allem wenn er im laufenden Betrieb unterbrochen wird (etwa durch einen Stromausfall).

Unter linux können Sie dann das Tool gdisk installieren

apt-get install gdisk

und dann die Konvertierung starten über

gdisk /dev/sd<x>

und dann über das kommando

gdisk>w

eine GPT-partition auf die platte schreiben. hierbei sollten sie sehr vorsichtig sein und sich alle im Vorfeld geäußerten Fehlermeldungen genauestens durchlesen und erörtern.

Auf windowsnahe Dateisysteme unter Linux zugreifen

In diesem Kapitel zeige ich Ihnen, wie Sie von einem Linux-System aus auf die windowsnahen Dateisysteme NTFS und exFAT zugreifen.

auf exFAT-Datenträger zurgeifen.

als erstes müssen wir einige Tools installieren

sudo apt-get install exfat-fuse exfat-utils

Eventuell müssen Sie vorher Repositories hinzufügen, damit die Pakete gefunden werden. Unter Ubuntu wären dies beispielsweise

sudo add-apt-repository universes

sudo apt-get update

oder allgemein

sudo apt-add-repository ppa:relan/exfat

sudo apt-get update

Danach können wir die Datenträger mounten über

sudo mount -t exfat /dev/sdxx /<mountordner>

Wenn Sie den Datenträger automatisch nach einem Neustart mounten wollen, geht das über sein Label, was ich Ihnen hier schonmal gezeigt habe. Der Einträge in der /etc/fstab wäre demnach

LABEL=<Label>   /<moutnpoint>   exfat-fuse <optionen>   0 0

Auf NTFS-Datenträger zugreifen

Zuerst müssen wir wieder Tools installieren

apt-get install ntfs-3g

Den Datenträger können Sie dann direkt mounten über

# mount -t ntfs-3g /dev/sd<xx> /mount/point

oder

ntfs-3g /dev/sd<xx> /mount/point

Über die /etc/fstab können Sie den Datenträger bei jedem Neustart über sein Label mounten, was ich Ihnen hier schon einmal gezeigt habe. Der Einträg wäre demnach

LABEL=<Label>   /<moutnpoint>   ntfs-3g <optionen>   0 0

Auf linuxnahe Dateisysteme unter Windows zugreifen

In diesem Abschnitt zeige ich Ihnen, wie Sie unter einem Windows-System auf linuxnahe Dateisyteme wie ext2 ext3 oderzugreifen.

Derzeit ist es mit dem Tool leider nicht möglich, auf ext4-formatierte Dateisyteme zuzugreifen, die Community arbeitet jedoch an einer Unterstützung für das Dateisystem.

die beste Möglichkeit ist das Tool ext2fsd. Denn nur dieses Tool erlaubt es Ihnen, auf den Datenträger über einen Laufwerksbuchstaben zuzugreifen, also von jedem Windows-Programm aus, selbstverständlich auch über den Windows-Explorer, und zwar nicht nur lesend, sondern auch schreibend.

Wenn Sie ext2fsd installieren, werdne Sie gefragt, ob Sie ext2fsd beim Windows-Start automatisch gestartet werdne soll, was Sie bejahen, wenn Sie nicht extra den Dienst manuell starten wollen, um auf die Datenträger zuzugreifen. Wenn Sie auf dem Datenträger unter Windows auch Dateien ändern oder neue erstellen wollen, dann aktiiveren Sie außerdem die Option enable write support for ext2 partitions sowie enable force writing support on ext3 partitions.

Nach der installation starten Sie den ext2 Volume Manager, in dem Sie einen Mountpoint für den Linux-Datenträger bestimmen können. Dieser Mountpoint ermöglicht es dann, dass Sie den Datenträger unter einem ganz gewöhnlichen Windows-Laufwerksbuchstaben sehen und somit mit jedem Windows-Tool auf das Dateisystem zugreifen können.

zwei andere Tools, die zwar auch auf Linux-Dateisysteme lesend (nicht schreiebnd) zurgeifen können, sind der DiskInternals Linux Reader und ext2explore. Leider können diese Tools jedoch nur über den eigenen Explorer auf die Dateisysteme zugreifen. Ein Zugriff über den Windows-Explorer oder eine beliebige windows-Anwendung ist somit leider nicht möglich.

 

Wenn dir dieser Post gefallen hat, teile ihn doch auf Social Media, abonniere und verfolge mich per E-Mail, RSS-Feed, Social Media oder registriere dich auf meiner Seite. Wenn du registriert bist, like den Post bitte, wenn er dir gefallen hat!

]]>
http://dafrk-blog.com/?feed=rss2&p=4243 0
Windows PowerShell Tutorial http://dafrk-blog.com/?p=4254 http://dafrk-blog.com/?p=4254#respond Mon, 01 Feb 2016 23:13:48 +0000 http://dafrk-blog.com/?p=4254 Windows PowerShell Tutorial weiterlesen →]]> Download und Installation der PowerShell

die Powershell wird seit Windows Vista standardmäßig auf allen Client-Rechnern und seit Windows Server 2008 auf allen Server-Betriebssystemen ausgeliefert. Auf allen Systemen, in denen die PowerShell nicht oder nicht mehr installiert ist, kann sie nachinstalliert werden. Die PowerShell steht hier zum Download bereit.cd

Erste Schritte

Startet die Windows PowerShell. Standardmäßig wird euch dann eine Kommandozeile mit blauem Hintergrund und weißer Schrift ausgeliefert. Bevor ihr startet, solltet ihr das LAyout eurer PowerShell bearbeiten. Schiebt die Powershell an den absolut linken Rand eures Monitors. Rechtsklickt dazua uf den Fenstertitel der Windows PoweRShell und wählt Eigenschaften. Dort klickt ihr auf den Reiter Layout. Ändert unter Fensterpuffergörße die Breite des powershell-Fensters so ab, dass die PowerShell bestenfalls die gesamte Breite des Bildschirms einnimmt. Stellt danach unter FEnstergröße die Breite auf den selben WErt ein. So stellt ihr sicher, dass für Ausgaben, die sehr viel Platz brauchen, auch genügend Platz vorhanden ist.

Mit dem Kommando

get-help <Thema>

könnt ihr euch Hilfe über ein bestimmtes Thema anzeigen lassen. Wenn ihr Hilfe über get-help selbst haben wollt, gebt ihr beispielsweise ein:

get-help get-help

wenn ihr hingegen Hilfe bezüglich Powershell-Kommandos haben wollt, die mit Windows-Diensten zu tun haben, gebt ihr ein

get-help services

Mit dem Parameter -online werdet ihr euch die Microsoft Knowledge Base weitergeleitet, wo ihr euch Online-Artikel zum Thema ansehen könnt.

get-help services -online

Grundsätzlich ist die PowerShell abwärtskompatibel zur alten DOS-Eingabeaufforderung, und zwar in dem Sinne, dass alle Kommandos, die unter der Windows-Eingabeaufforderung funktionieren, auhc in der PowerShell laufen. Ich gehe in diesem Tutorial davon aus, dass ihr schon einmal grundlegend mit der Windows-Eingabeaufforderung gearbeitet habt und dass euch dementsprechend Befehle wie dir, cd, echo, cls usw. schon etwas sagen.

Und das gute ist, dass Sie mit diesem kommando auch sehr einfach ein passendes Kommando für Ihren Bedarf finden können. Nehmen wir beispielsweise einmal an, Sie wollen irgendetwas mit Ihren Windows Logs machen. DAnn würden Sie einfach nach passenden Kommandos suchen über

get-help *log*

und schon bekommen Sie eine Liste mit allen PowerShell-Themen, die auf irgendeine Art und Weise mit Logs zu tun haben – inklusive Beschreibung.

Wenn Sie hingegen eine Liste mit allen Befehlen haben wollen, kölnnen Sie das Komamndo get-command, kurz gcm, nutzen

gcm -noun *log* -CommandType cmdlet

Ohne den PArameter -CommandType würdne Sie auch .ddl-Dateien etc. bekommen, was wir hier natürlich nicht wollen.

Nun fangen wir aber in den Grundlagen bereits an, die ersten cmdlets der PowerShell zu nutzen. Nehmen wir beispielsweise an, ihr wollt euch die Eigenschaften einer Datei in der Powershell anzeigen lassen. In dem FAll tippen wir einfach ein.

get-itemproperty <datei>

Wichtig zu verstehen ist hier, dass die Powershell nicht einfach so seine Ausgabe in die kommandozeile schreibt, sondenr ein Objekt erzeugt, dessen Ausgabe man manipulieren kann. DAs heißt die Powershell gibt standardmäßig die hier angezeiten Informationen über ein Objekt aus, welches mit get-itemproperty abgefragt wurde. Ihr könnt das Objekt aber an einen anderen Befehl weiterleiten und somit die Ausgabe manipulieren. Wie unter Linux geschieht dieses Weiterleiten über eine Pipe. Wir leiten unser Objekt mal an den Befehl format-list weiter.

get-itemproperty <datei> | format-list

Ihr merkt, dass nun andere informationen dargestellt. Um vollends zu verstehen, wie das mit den ganzen Objekten nun funktioniert, könnt ihr einfach mal den Befehl dir in einem Ordner eingeben. Für jede Datei, die das kommando dir ausgibt, wird ein extra Objekt erzeugt, welche ihr gebündelt an andere Kommandos weiterleiten und prozessieren lassen könnt. Beispielsweise

dir | get-itemproperty | format-list

Ziemlich cool, oder? Vielleicht habt ihr jetzt schon eine Idee davon bekommen, warum die Powershell so mächtig ist, nämlich, weil man jetzt sozusagen objektorientiert Skripten kann.

Kennt ihr noch das Konzept aus alten Tagen, wo ihr die Ausgabe der Windows-Eingabeaufforderung nach einem Befehl über > in eine Datei umgeleitet habt? Natürlich würde diese Variante auhc noch über die Powershell gehen, denn diese ist ja abwärtskompatibel. Jedoch macht es durchaus Sinn, auch hier wieder objektorientiert zu arbeiten. Deswegen hat die Powershell hierfür einen extra Befehl, out-file. Der folgende Bvefehl gibt die Ausgabe des oberen Befehls in eine Datei test.txt aus.

dir | get-itemproperty | format-list | out-file test.txt

Beispielsweise gibt es verschiedene out-Kommands, auf die man die Ausgabe eines ovrangehenden Befehls pipen kann, beispielsweise dir | out-gridview zeigt die Ergebnisse in einem extra Windows-Fenster im Tabellen-Layout an.

get-service | export-csv c:\services.csv

erstellt eine .csv-Datei. HIngegen exportiert

 get-service | export clixml c:\services.xml

eine saubere .xml-Datei. Und mit

get-service | convertto-html

konvertiert die Asugabe in eine HTML-TAbelle. Wenn Sie diese Ausgbae in eine .html-Datei speichern wollen, müssen Sie den Befehl noch auf out-file pipen

get-service | convertto-html | out-file services.html

wenn ihr die Ausgabe eines befehls an einen anderen Befehl piped, muss der nächste befehl irgendwie wissen, welchen Parameter ihr mit dieser Pipe befüllen wollt. Standardmäßig versucht die Powershell über die sogenannte By-Value methode herauszufinden, welcher PArameter des nächsten Kommandos mit der Ausgabe des Vorgängers arbeiten soll. Wenn ihr beispielsweise folgendes kommando absetzt.

get-service | stop-service

dann wird dies nicht funktionieren. Warum ist das so? nun, wenn ihr euch den Befehl get-service anseht über

get-service | gm

Dann seht ihr in der ersten Zeile den sogenannten TypeName. Das ist der Typ des ausgabeobjekts, den der befehl get-service erzeugt. Also den Typ ServiceController.

Jetzt schauen wir uns den befehl stop-serivce an über

help stop-service -full

2016-04-10_13h55_38

Dabei stellt ihr fest, dass stop-service standardmäßig den Parameter -Name (1) erwartet und somit von einem einfachen String ausgeht.  der Befehl get-service erzeug tallerdings einen serviceController, der mit dem PAremater -InputObject angesprochen werden muss (2). Dieser wird allerdings stnadarmdäßig nicht genommen, sondern stop-service erwartet das piping eines strings für den Parameter -Name.

eine andere Mtehode ist die Ausgabe des ersten Kommandos nicht by-Value, sondern by-PropertyName pipen. Dazu müsst ihr erst in der vollen Hilfe des zweiten komandos nach Paramtern suchen, die Pipeline-Input byPropertyName unterstützen. Und das unterstützt unser Parameter -Name zufälligerweise

2016-04-10_16h20_11

Wenn wir uns jetzt nochmal  get-service | gm anschauen, sehen wir, dass get-service eine AliasProperty namens Name hat. Und diese AliasProperty Name wiederum können wir nun an den Parameter -Name des Nachfolgekommandos, nämlich stop-service, pipen. Das ganze geht dann so.

get-service | select -ExpandProperty Name | stop-service

Wir wählen also aus der Ausgabe von get-service nur die Eigenschaft Name aus und pipen diese Eigenschaft Name an das cmdlet stop-service, dessen Parameter -Name wiederum den input byPropertyName akzeptiert und daher den relevanten Service stoppt. Für das cmdlet select gäbe es auch den Paramater -Property, aber manchmal kommt mit -Property eine Eigenscahft immer noch als Objekt raus und nicht als String, wie wir es in diesem Fall gebraucht hätten.

Manchmal kommt es vor, dass ein Kommando mehr Spalten ausigbt als auf den Bidlschirm passen. Also gibt es Dateine in der Powerhsell, die bestimmen, welche Spalten eines Befehls standarmdäßig ausgegeben werdne sollen. Der Befehl get-service beispielweise gibt nur drei Spatlen aus. Es ist aber möglihc, noch wesentlich mehr Spalten auszugeben. Diese findet man heraus über

get-service | get-member

Der Befhel listet alle Spalten, die man explizit einblenden kann,. (vom Typ Property, AliasProperty oder scriptProperty). Andere Datensätze sind beispielsweise vom Typ Method oder Event, die nicht explizitr eingeblendet werden können.

Stattdessen kann man aber mit Events „Befehle“ auf Objekte absetzen, die man zuvor über ein anderes cmdlet gesammelt hat. Wenn wir also beispielsweise über get-service eine Liste aller Dienste gesammelt haben, können wir alle Dienste auf einmal über das event Start starten.

get-service Start

Auch sortieren können wir ganz einfach über die powershell nach diesen spalten. Wenn wir get-service pipen auf get-member, finden wir beispielsweise die Spalte „threads“ als eine der Properties. Und nach dieser Property können wir soriteren.

get-service | sort threads

mit dem Paremter -Ascending oder -Descending kann man die Reihenfolge der Sortierung beeinflussen

get-service | sort threads -Descending

jetzt nehmen wir mal an, wir wollen nur die dienste haben, die laufen (Status RUNNING) und deren Namen die Zeichenkette „SQL“ enthält. So finden wir unter umständen alle laufenden Datenbank-Prozesse

get-service | where-object -FilterScript { $_.Status -eq 'Running' -and $_.Name -like '*SQL*'}

Hierbei durchsuchen wir also die Properties Status und Name der Ausgabe von get-service.

hierbie könnt ihr verschiedene Vergleichsoperatoren wählen wie beispielsweise

  • eq – gleich
  • ceq – case sensitive gleich
  • like – ist wie zeichenkette
  • lt – kleiner als
  • gt – größer als

Input Pipelining

über select

Jetzt mal ein etwas fortgeschritteneres Beispiel. Nehmen wir mal an, ihr lasst euch mit dem Kommando

get-adcomputer -filter *

Infomrationen über die gepflegten „computer“ im Active Directory anzeigen, an denen sich Benutzer anmelden können. Dazu filtert ihr mit * nach allen Computern im Active Directory.

Ihr bekommt einen output und in der Spalte „Name“ findet ihr die Computernamen der einzelnen computer, die im Active Directory für diese Computer hinterlegt sind.

Was ist jetzt, wenn ihr für all diese ComputerNamen bestimmt informationen abfragen wollt, beispielsweise, ihr wollte euch die PRozesse aller Computer anzeigen lassen, die im Active Directory registriert sind und die gerade laufen. Somit könn tihr beispielsweise feststellen, auf welchem Server ein bestimmter Dienst läuft, wenn ihr euch nicht mehr dran erinnern könnt.

dazu müsstet ihr normalerweise das kommando

get-process -ComputerName <ComputerName>

eingeben, um alle Prozesse dieses speziellen Computers zu bekommen. Dabei müsstet ihr jetzt für jeden einzelnen Computer den computerNamen raussuchen und diesen jeweils hier eintippen.

Das ganze geht aber natürlich einfacher. mit folgendem Kommando lesen wir die spalte name aus der ausgabe des kommandos get-adcomputer aus und speichern diese als Parameter ‚computername‘. Diesen Parmeter ‚computername‘ pipen wir dann an das kommando get-process. Denn get-process besitzt einen Parameter -ComputerName, der die Prozesse des genannten Computers auflistet. Da in unserer Abfrage aber die Computernamen aller computer im Active Directory als Objekt referenziert sind, bekommen wir gleich die Prozesse aller computer eine nach dem anderen ausgegeben.

get-adcomputer -filter * | select @{n='computername';e={$_.name}} | get-process

über .csv-Datei

ihr könnt auch die Parameter eines kommandos über eine .csv-datei füttern. Beispielsweise könntet ihr vor haben, in euem Active directory eine Reihe neuer User anzulegen, die zum Monatsanfang in eurem Unternehmen beginnen. Dazu könnt ihr in der PowerShell für jeden neuen mitarbeiter eine Kommandozeile mit dem Befehl new-aduser anlegen. Die entsprehcenden Parameter des Befehls new-aduser könnt ihr euch wieder über die hilfe ausgeben lassen.

statt jedoch für jeden neuen mitarbeiter eine extra Zeile anzufertigen, könnt ihr auch die Daten der Mitarbeiter in eine .csv-datei speichern. Die .csv-Datei hätte hierbei folgendes format

samaccountname,name,department,title,city

<AD-Accountname Mitarbeiter 1>,<Echter Mitarbeitername Mitarbeiter 1>,<Abteilung Mitarbeiter 1>,<Position Mitarbeiter 1>,<standort Mitarbeiter 1>

<AD-Accountname Mitarbeiter 2>,<Echter Mitarbeitername Mitarbeiter 2>,<Abteilung Mitarbeiter 2>,<Position Mitarbeiter 2>,<standort Mitarbeiter 2>

Wichtig ist, dass die Namen der Attribute in der ersten Zeile samaccount, name, department etc. genau so heißenwie die Parameter des Kommandos new-aduser, was hier der fall ist.

Die übergeben wir dann an den PArameter new-aduser

import-csv neue-mitarbeiter.csv | select samaccountname,name,department,title,city | new-aduser

Ist dem nicht der fall, weil ihr beispielswiese von der Personalabteilung eures unternehmens eine falsch formatierte .csv-Datei bekommen habt, wo die erste Spalte beispielsweise so formatiert ist.

Kontoname,name,department,title,city

<AD-Accountname Mitarbeiter 1>,<Echter Mitarbeitername Mitarbeiter 1>,<Abteilung Mitarbeiter 1>,<Position Mitarbeiter 1>,<standort Mitarbeiter 1>

<AD-Accountname Mitarbeiter 2>,<Echter Mitarbeitername Mitarbeiter 2>,<Abteilung Mitarbeiter 2>,<Position Mitarbeiter 2>,<standort Mitarbeiter 2>

dann könnt ihr diese falsch formatierte Zeile so  umbiegen

import-csv neue-mitarbeiter.csv | select @{n='samaccountname',e={$_.'Kontoname'}},<restliche Parameter>

Seine richtige MAcht entfaltet die Powershell, wie die Linux-Shell auch, wenn man Skripte damit schreibt. Powershell-Skripte werden mit der Dateinendung .ps1 gespeichert und ermöglichen es, dass dieses Skirpt dann von der PowerShell von oben nach unten prozessiert wird.

Die Powershell unterstützt das sogenannte Code-Signing, das heißt mit der Powershell ist es möglich, skripte mit Zertifikaten zu versehen. Dies soll verhindern, dass Skripte von fremdne ausgeführt werden, die unlautere Absichten hegen. Ihr werdet daher feststellen, dass ihr in einer Windows-Standardinstallation zunächst einmal keine .ps1-Skripte ausführen könnt, wenn die Sicherheitseinstellungen entsprechend gesetzt sind. Mit folgendem Befehl könnt ihr als Administrator die Sicherhehitseinstellungen so lockern, dass ihr eure eigenen Skripte ausführen könnt, aber nicht die von Fremden.

set-executionpolicy remotesigned

diese lokale Eisntellung kann durch eine Gruppenrichtlinie überschrieben werden.

Domain -> Default Domain Policy -> Edit -> Computer Configuration -> Policies  -> Amdinsitrative Templates -> Windows Components -> Windows Powershell -> Turn on Script Execution.

Ein weiteres Feature von PowerShell ist, dass man Powershell-Skripte mit einem Zertifikat signieren kann. Davon profitieren vor allem Unternehmen, die bereits eine interne Public Key infrastrcuture (PKI )aufgebaut haben und daher Zertifikate ausstellen könne,n mdenen firmenintern vertraut wird.

Zur Not kann man auch sogenannte self-sgine certificates erstellen, so dass man Skripte zumindest mit einem selbsterstellten Zertifikat signieren kann.

 

formatieren

format-wide

 

PowerShell INtegrated Scripting Environment

Grundsätzlich könnt ihr Powershell-Skripte mit einem ganz normalen TExteditor schrieben. Es empfiehlt sich jedoch, hierfür stattdessen die Windows Powershell ISE zu verwenden.

2016-02-11_11h50_37

 

Variablen

einzelne Werte, aber auch die OBjekte, die ein kommando zurückliefert, können wir in einer Variablen speichern.

Einfache WErte können ganz einfach in Variablen hinterlegt werden

$Text = "Dies ist ein Text"

Der Befehl hinterlegt Dies ist ein Text in der variable $Text. Ausgeben könnt ihr euch den WErt dieser Variablen über die PowerShell dann mittels

Write-Host "$Text"

Viel mehr Sinn macht es allerdings, Objekte in Variablen zu speichern. Beispielsweise könnt ihr so ganz einfach alle Objekte in eine Variable speichern, die das Kommando dir zurückliefert:

$alleDateienInVerzeichnis = dir

Ihr seht also, ihr schreibt hinter dem Variablennamen einfach den Befehl, den ihr ausführen wollt. Die Powershell führt ihn aus und speichert dann alle Objekte in der Variablen $alleDateienInVerzeichnis.

Diese Objekte können wir dann beipsielsweise wieder für get-itemproperty nutzen.

get-itemproperty $alleDateienInVerzeichnis

Ihr könnt in eurem Skript aber auch den User darum bitten, einen Wert einzugeben, der dann in einer Variable gespeichert wird

$name = Read-Host "Wie heißen Sie?"

Das cmdlet Read-Host stellt also dem User die Frage Wie heißen Sie?, woraufhin der User einen Namen eingeben kann, der dann in der Variable gespeichert wird.

Wir können eine Variable auch über ein Eingabefeld einlesen lassen. Sie können beispielsweise einfach mal folgenden Code ausprobieren.

OS>[system.reflection.assembly]::loadwithpartialname('Microsoft.VisualBasic') | Out-Null

OS>$computername = [Microosft.Visualbasic.interaction]::inputbox('Geben Sie einen Namen ein','Frage Titel','Standard-Vorschlag')

 

Alle Variablen sind im PSDrive varaible: aufgelsitet.

Eine VAriable lässt sich mit dem cmdlet REmove-Variable -name ersetzen. Hierbei darf man jedoch nicht die Variable mit dem Dolalrzeichen ansprehcen, sondern man gibt nur den Namen der Varibale an. Um also die VAriable $a zu löschen, gibt man ein

Remove-Variable -name a

natürlich kann man die ausgbae von Befehlen inv Ariablen speichern. das folgende Kommando liest in die Variable services alle gepflegten Windows-Dienste auf dme Systme ein.

$services = get-service

Es können VAriablen mit lEerzeicehn im Namen gesetzt werde, wobei man jedoch folgende syntax verwendne muss

${Alle Services} = get-service

Beim Blegen von Variablen mit String-WErten ist darauf zu achten, dass bei einfachen Anführungszeichen ein $<Name> nicht als VAriable behandelt wird, bei doppelten Anführungszeichen hingegen schon

$x = 'Welt'

$y = 'Hallo $x'

$z = "Hallo $x"

OS>$y

Hallo $x

OS>$z

Hallo Welt

Hallo $x

Aus der Programmierung kennt man vielleicht den VAriablentyp von Arrays, der es erlaubt, mehrere Werte in einer einzigen VAriable zu hinterlegen. Jedes cmdlet, welches mehrere OBjekte erzeugt, legt seine Objekte in einem ARray ab, wenn man die Ausgabe des cmdlets in eine Variable speichert. Beispielsweise gibt

$services = get-service

ein Array zurück. Auf  die ersten zwei SErvices in der liste greift man dann etwa zu über

$services[0]

$services[1]

Parameter übergeben

Wie schon beim Linux Shell Skripting könt ihr auch PArameter an ein Skript übergeben. Beispielsweise könntet ihr an den Anfang eines Skriptes schrieben

param (

$hostname = 'localhost'

)

Wenn jetzt jemand  das Powershell-Skript ausführt, ohne etwas dahinter einzgueben, wird der Stnadardwert localhost verwendet. Ansonsten kann jemand mit

<Pfad zur skriptdatei> -hostname meinhostname

jetzt wird das skript mit dem Wert meinhostname für den Parameter hostname ausgeführt.

KOmmentare

Wie auch bei Batch-Skripten könnt ihr mit PowerShell ganz einfach Kommentare schrieben. Neben den Kommentaren, die mit — eingeleitet werden, die wir schon aus Batch kennen

--dies ist ein Kommentar

können wir auch mehrzielige Kommenatre schreiben.

<#

dies ist ein 

mehrzeiliger

Kommentar

#>

 

Sortieren

Die Asugbae eines Befehls lässt sich sortieren.

Escape Zeichen

Aus verschiedenen Progrmamiersprahcen kennt ihr vielleicht Escape-Zeichen wie \n, \t usw., die es ermöglichen, in die Ausgabe eines befehls einen TAbstopp oder ienen Zeilenumbruch o. Ä. einzufügen.

Auch die Powershell bietet solche Zeichen, sie werden jedoch nicht mit einem Backslash, sondern mit einem ` eingeleitet. Mehr ifnormationen findet ihr über help about_escape_characters

Verzweigungen

if

Eine If-Verzweigung ermöglicht es, eine Aktion auszuführen, wenn eine bestimmte Bedingung eintritt, und auch untershciedliche Aktionen auszuführen, wenn unterschiedliche Bedingungen zutreffen. Die if-Verzweigung prüft, ob die jeweiligen Bedingungen erfüllt werden und die PoweRShell verhält sich dann entsprechend.

Beispielsweise können Sie prüfen, ob eine Datei existiert, bevor Sie überhaupt versuchen, diese zu löschen

$dateiname = test.txt

if (test-path $dateiname) {

 del $dateiname

}

Die if-Verweigung prüft, ob es im aktuellen Ordner eine Datei test.txt gibt, und falls ja, wird diese gelöscht. Trifft die Bedingung nicht zu, springt das Skript an das Ende der geschlossenen geschweiften Klammer } und würde danach weiter machen, ohne den Inhalt der if-Verzweigung auszuführen.

switch

switch eignet sich dafür, wenn man für eine einzige Variable viele verschiedene Fälle unterscheiden möchte

switch ($variable) {

 'wert1' {     

Befehle für Wert 1

break

 }

'wert2' {

Befehle für Wert 2

break

}

default {

 Standardbefehle

}

}

 

Schleifen

foreach

Jetzt da wir aus dem oberen Kapitel wissen, wie man mehrere Objekte in einer Variablen speichert, kommt die foreach-Schleife ziemlich handy. Nehmen wir beispielswiese mla an, ihr wollt von jeder Datei ine ienm Verzeichnis die Eigenschaften aus dem Befehl get-itemproperty in einer extra Datei haben.

$alleDateienInVerzeichnis = dir

foreach ($datei in $alleDateienInVerzeichnis) {

  $infofile = $datei.name + ".info"

  get-itemproperty $datei | format-list | out-file $infofile

}

Diese foreach-Schleife geht jedes Objekt, welches in der Varible $alleDateienInVerzeichnis liegt, einzeln durch und speichert das aktuelle OBjekt in der Variable $datei. Von diesem OBjekt wird die Egienschaft .name, also der Dateinamen, genommen und mit der Dateiendung .info in die Variable $infofile gespeichert.

Danach wird vom aktuellen Objekt noch die Eigenschaftenliste über get-itemproperty | format list geholt und dies dann in eine Datei gespecihert, die den Dateinamen aus der Variable $infofile bekommt.

for

Auch diese Art der Schleife kennen wir bereits aus dem Linux Shell Skritp Tutorial. Die folgende Schliefe zählt von 0-9 hoch und gibt in jedem Schleifendurchlauf die aktuelel Zahl wieder.

for ($i=0; $i -lt 10; $i++) {

  write-host $i

}

PSDrives: Registry und Zertifikate wie ein Dateisystem behandeln

Die Windows PowerShell führt das Knzept der Power Shell Drives (PSDrives) ein, mit denen es beispielsweise die Möglichkeit bietet, die Windows Registry so zu navgiieren, als wäre es ein Dateisystem auf einer Festplattenpartition. Über

cd HKCKU:

kommen Sie beispielsweise in den Registry-Knoten HKEY_CURRENT_USER, über HKLM zum Zweig HKEY_LOCAL_MACHINE.

Somit können Sie die Windows Registry auf die gleiche Art und Weise manipulieren. wie lokale Systemdateien

Nicht nur die Registry, sondenr auch lokal installierte Zertifikate, beispielsweise zur SSL- oder TLS-Kommunikation, lassen sich erforschen. Dazu wechseln Sie in das PSdrive cert:.

Alle verfügbaren psdrives können Sie sich einzeigen lassen über

get-psdrive

Sie können auch seösbt ein neues PSDrive erstellen. So können Sie beispielsweise ein Dateisystem mit einem benannten PSDrive mounten statt mit einem langweiligen LAufwerksbuchstaben.

New-PSDrive -name meinOrdner -PSProvider -Root c:\Ordner

Nach diesem Befehl können wir mit dem kommando

cd meinOrdner:

in dieses Dateisystem wechseln, statt den ursprügnlichen Weg über c:\Ordner zu gehen. Bewusst muss euch natürlich sein, dass ihr dieses LAufwerk nur innerhlab von PoweRshell ansteuern könnt. Alle anderen Windows-Programme werden diesen Mechanismus nicht beherrschen.

Neben DAteisystemen kann man auch beispielsweise UNC-Pfade, Registry-Pfade usw. mounten.

CommonParameters

Wenn Sie vorher mit der cmd.exe oder mit der Linux Shell gearbeitet haben, wissen Sie, dass jedes Kommando seinen eigenen SAtz an PArametern hat. Unter Linux können Sie beispielsweise die Unterordner eines Ordners mit dem Befhel cp -R kopieren. Dabei können Sie aber nicht automatisch erwarten, dass der Befehl zum Ausschneiden/Verschieben, mv, ebenfalls einen Parameter -R hat (hat er nämlich nicht).

Unter PowerShell ist das zumindest zum TEil anders. Denn jeder, und damit wirklich jeder, Powershell-Befehl hat einen SAtz von sogenannten CommonParameters, die für jeden Powershell-Befehl gelten.

Eine Liste aller CommonParmeters bekommt ihr über

get-help about_CommonParameters

Die Hilfe listet Ihnen, was die einzlenen CommonParameters tun, daher lohnt es sich durchaus, sich diese einmal durchzulesen.

Die wichtigsten Befehle

In dieser Überschrift zeige ich Ihnen die wichtigsten Befehle, die Sie am häufigsten mit der Windows PowerShell brauchen werden.

Wenn Sie sich den Inhalt von Textdokumenten anzeigen lassen wollen, können Sie das Kommando

gc <Datei>

verwenden.

Wenn Sie zwei Versionen einer Datei miteinander vergleichen wollen – das Linux-Äquivalent wäre das kommando diff – können Sie Compare-Object verwenden.

Compare-Object -ReferenceObject (import-clixml c:\alt.xml) -DifferenceObject (C:\neu.xml)

Man kann auch beispielsweise ein altes kommando, welches mit  als .xml exportiert wurde, mit der aktuellen Ausgabe eins Kommandos verlgeichen

Compare-Object -ReferenceObject (import-clixml c:\processes-alt.xml) -DifferenceObject (get-process)

Und man kann sogar nur eine bestimmte Eigenschaft eines solchen Ausgabebefehls, der seine Ausgabe in Tabellenstruktur ausgibt, vergleichen.

Compare-Object -ReferenceObject (import-clixml c:\processes-alt.xml) -DifferenceObject (get-process) -property Name

der Befehl vergleicht nur den PArameter Name.

PoweRshell-Skripte anderer USer

Unter powerhell.com/cs/media können Sie sich Funktionen und Skritpe anderer User herunterladen, um IHre Bibliothek zu erweitern.

PowerShell-Snapins und Module

die Powershell ermöglicht das LAden von Modulen. Eine Lsite aller verfügbaren MOdule kriegt man über

get-module -ListAvailable

Hierbei ist zu unterscheiden zwischen der32- und 64-Bit-Powershell. für die 64-Bit-Powershell werden mehr module verfügbar sein als für die 32-bit-Version.

Ein modul lässt sich dann in der PoweRshell laden über

import-module <Name>

dancah können wir uns die Kommandos anzeigen lassen, die mit dem Modul dazu gekommen sind

get-command -module <name>

Das ist besonders nützlich, weil es für viele Administratoinstools unter Windows extra Module für die Powershell gibt, etwa den Gruppenrichjtlinieneditor oder das Acitve Directory.

Man kann die Funktioanlitäten der Powershell mit Snapins erweitern. Eine Liste aller verfügbaren Snapins kriegt man über

Get-PSSnapin -regtistered

Will man solch ein Snapin in der aktuellen PoweRshell-Sessoin nut6zen, muss man dieses Snappin erst hinzufügen

Add-PSSnapin <name Snapin>

Nun könnt ihr euch ansehen, welche KOmmandos das Snapin zur Powershell hinzufügt

get-command -pssnapin <name Snapin>

Powershell-Profile

Unter Linux kann man verschiedene Kommandos automatisch ausführen lassen, wenn sich der Rechner neu startet oder sich beispielsweise ein User anmeldet. Dafür igbt es verschiedene Standardskripts ide unter vordefinierten Verzeichnissen liegen, beispielweise ~/.profile für eine Useranmeldung oder die Skripte unter /etc/rc<runlevel>.d für einen Neustart.

Wenn ihr wollt, dass beim STarten von Powershell verschiedene Komamndos automatishc ausgeführt werden, dann müsst ihr ein Powershell-Profil erstellen. DAs erstellt ihr unter dem Documents-Verzeichnis des Users, also beispielsweise C:\users\<meinuser>\documents\. Dort erstellen wir einen Ordner WindowsPowerShell und darin eine Datei profile.ps1

In dieses Profil  könnt ihr nun Powershell-Kommandos ausführen, die bei jedem STart der Powershell automatisch ausgeführt werden sollen, eintippen und das Skript speichern. Ihr könnt nun beim nächsten Start der PowerShell eventuell eine Fehlermeldung bekommen, dass die ExecutionPolicy die Ausführng des Skripts nicht erlaubt. Diesen behebt ihr mti dem Kommando

set-executionpolicy remotesigned

im elevated Mode.

Ergebnisse von SQL-Statements in eine Datei exportieren

Viele kommandozeilen-Utilities von Datenbankmanagementsystemen unterstützten nicht die Umleitung der Befehlsausgabe in eine Datei über die gewohnten maßnhamen. Wenn Sie beispielsweise mit dem kommandozeilentool slqcmd auf einer MS SQL Server Datenbank sind und versuchen über

SELECT * FROM qs3.DDXTT~ > c:\users\qs3adm\desktop\ddxtt.txt

den inhalt der tabelle DDXTT~ in der Datenbankinstanz qs3 in eine Datei auszugeben, werden sie auf Granit beißen.

Das kommandozeilentool isql von Sybase ASE hingegen erlaubt  zwar eine eingebaute Maßnahme die Ausgbae in eine Datei im format

1>select * from qs3.DDXTT~

2>go > c:\users\qs3adm\desktop\ddxtt.txt

und mysql kennt INTO OUTFILE

MySQL>select * form DDXTT~

MySQL>INTO OUTFILE '<dateipfad>';

das war es aber auch schon.  Diese Möglichkeit ist in isql und mysql von den Programmieren eingebaut worden. Sie können deshalb leider beispielsweise die Ausgabe nicht über die Powershell pipen, etwa so

1>select * from qs3.DDXTT~

2>go | out-gridview

Um diese Schwächen der kommandozeilen-Tools diverser Datenbanken zu umgehen, gibt es verschiedene MÖglihckeiten. Zum einen liefern viele Datenbankmanagementsysteme eigene, dedizierte Tools mit, die den Inhalt von Tabellen in eine Datei exportieren. Sowohl isql als auch sqlcmd beispielsweise nutzen hierfür ein Tool namens bcp

bcp "SELECT Col1,Col2,Col3 FROM MyDatabase.dbo.MyTable" queryout "D:\MyTable.csv" -c -t , -S SERVERNAME -T

Jedoch können Sie jedoch

  1. nicht immer darauf verlassen, dass ein DBMS ein solcehs Tool mitliefert
  2. mit diesem Tool die ausgabe zwar in eine Datei exportieren, aber immer noch nicht an andere powershell-Kommandos pipen, wie etwa an out-gridview

manchmal unterstützen die command line utilities das Pipen, wenn man die Utilities auf PoweRshell-ebene aufruft und das entsprechende SQL-Statement hinten anhängt, also etwa

Powershell>SQLCMD -S <servername> -E -Q "SELECT Col1,Col2,Col3 FROM MyDatabase.dbo.MyTable"

-s "," | out-gridview

Das funktioniert jetdoch auch nicht immer.

Powerhsell-Befehle im Hintergrund ausführen

Normalerweise müsst ihr in der powoerhsell warten, bis ein kommadno fertig ist, bevor ihr einen neuen eingeben könnt. Manche befehle laufen aber etwas länger, sodass ihr normalerweise ein zweites powershell fenster aufmachen müsstet.

stattdessen könnt ihr ein powershell skript im hintergurnd als job ausführen

start-job -command { <kommando> }

solange der job läuft, könnt ihr euch seinen status einzeigen lassen über

get-job

 

Remoting

remoting bezeichnet die Funktion von Powershell, eine Powershell-Kommandozeile nicht nur lokal auf dem eigenen rechner, sondern auch auf einem entfernten Rechner auszuführen, auf dem die Powershell ebenfalls installiert ist.

damit das geht, muss remoting erstmal aktiviert werden. Remoting muss nur auf den Rechnern aktiviert werden, wleche Befehle vona nderen Rechnern empfangen sollen. Die Senderseite muss also nicht extra aktiivert werden. Auf der Empfängerseite geben wir ienfach ein

enable-psremoting

In Enterprise-Umgebungen gehört natürlich mehr dazu. Hierfür müssen dann nämlich Gruppenrichtlinien gesetzt werden.

Das ganze geht über

invoke-command -computer <ip adresse oder hostname entfernter computer> -ScriptBlock { <Kommandozeilen die ausgeführt werden sollen>  }

Die Funktionalität kann auch 1-to-many genutzt werden. Man kann also mit einer einzigen Powershell mehrere remote-rechner fernsteuern.

invoke-command -computer rechner1,rechner2 -ScriptBlock { <komamndos> }

Diese Art des Remotings sendet ein kommando an die rechenr, die im Parmater -computer angegeben werden, und schickt ide ausgabe dieser befehle zurück an die Powershell auf dem eigenen Rechner.

die andere Art, Remoting zu nutzen, ist, sich auf einen anderen server aufzuschalten, um auf diesem Server explizit ein- und ausgabe auszuführen. Dieser modus ist dann vergleich mit einer SSh-Verbindung auf einen Linux/Unix-server. Man verbindet isch auf den Host über

new-pssesssion -ComputerName <hoistname oder ip>

Diese Art des Remoting wird ganz häufig genutzt, wenn man einen Windows Server-rechner mit der core-Option installiert hat, also der Windows Server keine grafische Oberfläche besitzt. Hierüfr muss man sich jedoch häufig mit anderen Anmeldeinfomrationen anmelden. denn der benutzer, der sich auf den entfernen Rechner aufschalten möchte, braucht dazu entsprechende rechte. Will man sich nun mit dem Windows-Benutzerkonto des Systemadministrators des Servers anmelden, gibt man diese Informationen mit dem Parameter -Credential an.

Die remote Session kann man wieder beenden über

exit-psssession

Invoke-commands auf entfernte computer können auch im Hintergrund als Job ausgeführt werdend, sodass man nicht warten muss, bis diese Kommandos ausgeführt sind, bevor man auf der powershell weiterarbeiten kann

invoke-command -computer <hostname> -ScriptBlock {<kommandos> } -asjob -jobname <name>

wir können mehrere Sessions in einer Variable speichern

$sessions = enter-pssesssion -Computername computer1,computer2

$sessions ist hierbei ein Arary. In eine einzelne Session können wir dann  rein über

enter-pssesssion  -session $sessions[0]

Eigene Funktionen

wie schon im shell-Skripting Tutorial werden wir auch mit der Powershell eigene Funktionen schreiben. Die Grundstruktur ist

function MeineFunktoin {

 param (

  $parameter1 = 'Standardwert1',

  $parameter2 = 'Standardwert2'

)

Befehl1

Befehl2

}

Es ist in Powershell sogar möglich, Funktionen zu laden, damit man diese dann nicht nur in einem Skript, sondern auch im interaktiven modus, wenn man quasi die Powershell selber geöffnet hat, nutzen kann. Dazu schreibt ihr ein Skript mit der funktion und ladet die darin definierten Funktionen über

. .\<pfad zum Skript>

damit sind die im Skript befindlichen funktionen nun in eurer PowerShell-Umgebung und ihr könnt die Funktionen interaktiv nutzen.

Ein häfuiger Awnendungsfall einer Funktion ist, dass am Ende der funktion etwas ausgegeben werden soll. Dies erzielt man etwa so

function MeineFunktoin {



$variable1 = irgendeinwert

Befehle, die varaiable1 manipuulieren



Write-Output $variable1

}



MeineFunktion | ConvertTo-HTML | Out-File ergebnis.html

Diese Ausgabes Skript schriebt den Wert der $variable1 in die kommandozeile, konvertiert dieses ins .html-Format und schreibt dann eine DAtie ergebnis.html

Try Cath, ErrorVairable und Trap

Wie beim Shell Skritping gibt es auch in der Powershell einen Try-Catch-Block.

Skritpe können in verschiedene Fehler laufen. Damit unser Skript nicht einfach abbricht, wenn eine bestimmte Fehleristuation eintritt, sondern wir aktiv im Skript etwas dagegen tun können, wollen wir Fehlersituationen abfangen und behandeln.

In einem try-Block schrieben wir die Befehle rein, die wir ausführen wollen.

try {

 Befehl1

Befehl2

}

In einem catch-Block schreiben wir dann rein, was passieren soll, wenn einer der im try-Block stehenden Befehle einen Fehler verursacht

try {

Befehl 1

Befehl 2

}



catch {

Gegenmaßnahme 1

Gegenmaßnahme 2

}

Manche Fehler von Befehlen können nicht standardmäßig „gecacht“ werden. DAs heißt einige Powershell-cmdlets sind standarmdäßig so konfiguriert, dass ihre Fehler nur „hinweise“ sind. ein try-catch-Block kann aber nur solche Fehler abfangen, die zum Stoppen des cmdlets führen würden (sogenannte terminating errors). Wenn euer try-catch-Block nicht funktioniert, obwohl euer Skript fehler wirft, habt ihr vermutlich so ein cmdlet vor euch liegen. Ihr könnt jedoch das Verhalten eines Befehls ändern, indem ihr dem Befehl den APrameter –erroraction STop mitgebt. jedes cmdlet hat diesen Parameter und wenn dieser auf Stop gesetzt ist, würde das Skript anhalten und der Fehler fällt somit in den try-catch-Block mit rein.

Nun fragt ihr euch vielleicht, dass diese Art der Behandlung von Fehleranalyse noch sehr grob ist, weil ihr noch nicht untershcieden könnt,w elche Art von Fehler aufgetereten ist. Ein Backup-:Skritp beispielsweise kann auf viele verschiedene Arten fehl schlagen.  Etwa weil der Festplattenspeicher voll ist , keine Schreibrechte vorhanden sind oider das zu sichernde File schlciht und einfach nicht vorhanden ist.

Viele cmdlets erlauben eine Unterscheidung verschiedener Fehlerkategorien anhand der error codes, die sie schmeißen, wenn etwas schief läuft. Viele cmdlets schmeißen bei unterschiedlcihe error codes, je nachdem, was die URsache für ihr scheitern war.

Wie unter Linux könnt ihr von jedem cmdlet den Fehlercode auslesen. Dazu müsst ihr bei dem cmdlet, welches ihr ausführt, den APramteer –errorvariable mitgeben. Diesem PArameter gebt ihr den Namen einer Variable mit, in welcher der Error Code des cmdlets hinterlegt werden soll. Da ein cmdlet mehrere Fehler auf einmal produzieren kann, ist diese error Variable immer ein Array, welches mehrere Fehlermeldungen aufnehmen kann. Das heißt, wenn wir folgenden Block schreiben

try {

Befehl1 --errorvariable fehlercode

}

catch {

Write-Host $fehlercode[0]

}

dann ist $fehlercode[0] der erste Fehlercode im array $fehlercode, welches durchaus noch weitere Fehlercodes enthalten kann.

Das bedeutet nun, dass ihr in eurem catch-Block das array nach seiner Länge analysieren könnt, um festzustellen, wie viele Fehlermeldungen im Array drin sind, und dann könnt ihr schauen, welche fehler codes genau aufgetreten sind. Mit einer if-Verzweigung beispielsweise könnt ihr dann verschiedene Gegenmaßnahmen einleiten, je nachdem, welche Fehlercodes aufgetreten sind.

Ein Problem haben wir noch: Bisher können wir nur die FEhler auslesen, die einzelne cmdlets ausgeben. Denn nur cmdlets haben den PArameter –errroraction Stop und nur solche werfen daher fehler, die in einem catch-Block abgefangen werden können.

Was wir dezriet noch nicht abfangen können, sind Fehler von Methoden eines cmdlets oder von Methoden innerhlab einer selbst geschriebenen Funktion.

Diese Art von Fehler müssen wir mit einem try-Block abfangen. Der try-Block muss deifniert werden, bevor der Befehl ausgeführt wird, der möglciherweise denFehler wirft. ein trap-Block

Ein guter Artikel zum Error Hnadling in der Powershell ist hier zu finden.

WMI

WMI ist eine super Möglichkeit, um sich HArdwareinformationen vom eignene oder von entfernten Rechnern zu holen. Beispielsweise gibt folgender befehl informationen büer das BIOS bzw. UEFI des Systems aus

Gwmi Win32_BIOS

von einem entfernten Rechner über

Gwmi Win32_BIOS -computer <name oder IP>

WMI hat eine eigene Filtering-Sprache. So kann man beispielsweise mit folgendem Befehl nach Datenträgertypen sortieren

gwmi win32_logicaldisk -Filter "drivetype=3"

hierbie können als Vergleihcsoperatoren wieder die gewöhnlichen Powershell-operatoren wie beispielswiese ceq, eq, lt, gt und like benutzt werden.

Das schwieirge an der ganzen sache ist, erstmal das richtige WMI-Objekt zu finden, welches man für die gewünschte Aufgabe verwenden kann. Dabei können Drittanbietertools sinnvoll sein, wie etwa der Primaltools WMI Explorer

Auch WMI-Objekte können als Job ausgeführt werden. Dazu bedient man sich invoke-command

invoke-command -scriptblock { get-wmiobject -class win32_process } -computer <hostname> -asjob

Auf Datenbanken mit der Powershell zurgeifen

Mit Hilfe der PowerShell ist es möglich, sich auf Datenbanken zu verbinden und auf diese Abfragen abzusetzen. Solche Powershell Skripte werdne aus verschiedenen Anlässen genutzt. Beispielsweise speichern sich Datenbankadministratoren in Powershell-Skripten häufig verwendete SQL-Abfragen, die sie dann nur noch per Doppelklick und anschließender PAssworteingabe asuführen, anstatt die Kommandos jedes mal selbst ausführen zu müssen.

Desweiteren ist es möglich, automatische Backups der Datenabnk über so ein Powershell-Skjript laufen zu lassen, oder etwas jedes mal, wenn sich ein Benutzer am System anmeldet oder eine wichtige Änderung macht, einen Log-Eintrag in eine Datenbank zu schreiben. Das Powershell-Skript könnte man dann über eine Windows Active Directory Gruppenrichtlinie immer laufen lassen, sobald sich ein Nutzer an der domäne anmeldet.

Um mit Datenbanken zu arbeiten, brauchen wir erstmal einige Voraussetzungen:

  • .NET Framework >= 3.5 ist auf dem Sytem installiert
  • PoweRshell 2.0 und Powershell ISE sind installiert
  • desweiteren ist es empfehlenswert, dass ihr nicht die 64-Bit-Version der Powershell für das Skript verwendet, sondern die 32-Bit-Version. Einige Datnebanken haben PRobleme mit der 64-Bit-PowerShell

Danach intiialisierten wir ein benötigtes Objekt über

[assembly.reflection]::loadwithpartialname('System.Data')

Dann brauchen wir einen Connection String

Schleifen

foreach

 

Passwörter über einen Secure Store bekommen

 

Wenn dir dieser Post gefallen hat, teile ihn doch auf Social Media, abonniere und verfolge mich per E-Mail, RSS-Feed, Social Media oder registriere dich auf meiner Seite. Wenn du registriert bist, like den Post bitte, wenn er dir gefallen hat!

]]>
http://dafrk-blog.com/?feed=rss2&p=4254 0
Windows SMB/CIFS Freigabe unter Linux mounten http://dafrk-blog.com/?p=4191 http://dafrk-blog.com/?p=4191#respond Thu, 28 Jan 2016 14:42:47 +0000 http://dafrk-blog.com/?p=4191 Windows SMB/CIFS Freigabe unter Linux mounten weiterlesen →]]> Haben Sie in Ihrem Heimnetzwerk einen Linux Server, auf dem Sie eine Windows-Freigabe von einem Windows-Rechner einhängen möchten, um auf dem Server auf die Daten zugreifen zu können?

Oder haben sie in ihrem Unternehmen Linux-Systeme, die auf Freigaben innerhalb Ihrer Microsoft Windows Active Directory-Struktur zugreifen müssen (beispielsweise um Backups abzulegen)?

Dann ist dieser Post genau der Richtige für Sie. Wenn Sie hingegen wissen wollen, wie Sie auf einem Linux Server Dateifreigaben einrichten, die man dann an einem Windows Client einbinden kann, schauen Sie sich meinen post über Samba an.

Als installieren wir uns auf dem Linux-System benötigte Pakete

apt-get install cifs-utils

Dann erstellen wir das Verzeichnis, in welches die Windows Freigabe später gemountet werden soll

mkdir /windowsfreigabe

Im ersten Szenario werden wir eine Windows Freigabe mounten, die nicht durch Benutzer/Passwort geschützt ist, also Gast-Freigaben, auf die jeder zugreifen kann.

Dann editieren wir einfach nur unsere /etc/fstab und fügen am Ende folgende Zeile ein

//<hostname oder ip-adresse windows freigabeserver>/<Freigabename>  /windowsfreigabe  cifs  guest,uid=1000,iocharset=utf8  0  0

Bei jedem Neustart wird die Freigabe automatisch gemountet, wenn ihr sie ohne Neustart gleich mounten wollt gebt ihr einfach ein

mount -a

Nun wollen wir mal eine Windows-Freigabe mounten, die durch Benuztername und Passwort geschützt ist. Der einfachste, aber nicht unbedingt sichere WEg ist der, dass man den Eintrag in der /etc/fstab so abändert

//<hostname oder IP Windows Freigabeserver>/<Freigabename>  /windowsfreigabe  cifs  username=<windows username>,password=<windows passwort>,iocharset=utf8,sec=ntlm

DAs ist jedoch ein Sicherheitsriskiko, da jeder Benutzer auf der Linux-Kiste die /etc/fstab und somit das Passwort lesen kann.

Stattdessen erstellen wir also im Homeverzeichnis des Users, der als einziger in diesem Linux System auf die Freigabe zugreifen können soll, eine Datei ~/.smbcredentials und schreiben dort folgendes rein:

username=<windows username>

password=<windows passwort>

Dann ändern wir die Berechtigungen der Datei

chown <linux user, der zugreifen soll> ~/.symbcredentials

chmod 600 ~/.smbcredentials

Dann bearbeiten wir unseren Eintrag in der /etc/fstab folgendermaßen:

//<hostanem oder ip windows freigabeserver>/<freigabename> /windowsfreigabe cifs credentials=/home/<username>/.smbcredentials,iocharset=utf8,sec=ntlm 0 0

 

 

Wenn dir dieser Post gefallen hat, teile ihn doch auf Social Media, abonniere und verfolge mich per E-Mail, RSS-Feed, Social Media oder registriere dich auf meiner Seite. Wenn du registriert bist, like den Post bitte, wenn er dir gefallen hat!

]]>
http://dafrk-blog.com/?feed=rss2&p=4191 0
Dateiübertragungen und -kopien verifizieren mit Checksummen http://dafrk-blog.com/?p=4185 http://dafrk-blog.com/?p=4185#respond Mon, 25 Jan 2016 20:02:06 +0000 http://dafrk-blog.com/?p=4185 Dateiübertragungen und -kopien verifizieren mit Checksummen weiterlesen →]]> Hier ein kurzer Post der beschreibt, wie man beim Übertragen von Dateien von einem Ort zum anderen schnell sicherstellen kann, dass die File Contents gleich sind

Haben die beiden Ordner die selbe Größe?

Erstmal, bevor ihr genauer analysiert, wollt ihr überhaupt wissen, ob zwei Ordner, die ihr an zwei STellen kopiert habt, ungefähr die selbe Größe haben. DAs lässt sich  über du -sh rausfinden

du -sh /somedir

du -sh /newdir

Die Checksumme bei unterschiedlichen Verzeichnissen

Szenario: Ihr habt zwei Verzeichnisse, die einen anderen PFad haben, in dem sich aber die selben Dateien befinden. Eine Version der Dateistruktur habt ihr unter /somedir und die andere unter /newdir

Im Folgenden gehen wir davon aus, dass wir eine ganzes Verzeichnis mit mehreren Unterordnern kopiert haben, beispielsweise über einen Befehl wie

cp -rpf /somedir/* /newdir

Wir wollen jetzt natürlich sichergehen, dass in newdir wirklich alles aus somedir angekommen ist. ES wäre viel zu aufwändig, jede einzelne Datei zu checksummen und zu vergleichen. Daher bedinene wir uns einem kommando im Format

find -s /somedir -type f -exec md5sum {} \; | cut -d" " -f1 | md5sum

und vergleichen die Checksumme von somedir mit der Checksumme von newdir

find -s /newdir -type f -exec md5sum {} \; | cut -d" " -f1 | md5sum

Zusätzlich zur Checksumme solltet ihr noch grob die Größe der beiden Verzeichniss emiteinander vergleichen.

Die Checksumme bei gleichen Verzeichnissen

Ihr habt auf zwei Systemen verschiedene Dateien im selben Verzeichnis.

find /somedir -xdev -type f -print0 | LC_COLLATE=C sort -z | xargs -0 cat | md5sum -

Und das ander prüft ihr entsprechend gleich auf dem anderen System

find /somedir -xdev -type f -print0 | LC_COLLATE=C sort -z | xargs -0 cat | md5sum -

Downloads verifizieren

Wenn Sie sich Dateien von einem Webserver oder einer sonstigen Quelle herunterladen, veröffentlichen findige administratoren zusätzlich zum Download immer die Checksumme, die der Veröffentlicher des Downloads zuvor festgestellt hat. Sie als Downloadender prüfen dann die checksumme des Veröffentlichers mit der checksumme, die Sie bei Ihrer gedownloadeten Datei bekommen. somit können Sie feststellen, ob entweder beim Download Übertragungsfehler vorgekommen sind und die datei in einem fehlerhaften Zustand heruntergeladen wurde, oder ob vielleicht sogar jemand ohne Wissen des Veröffentlichers die zum Herunterladen angebotene Datei verändert und manipuliert hat, um beispielsweise einen Virus einzuschleusen.

Der Veröffentlicher von Downloads nutzt verschiedene kanäle, um Ihnen seine checksumme bereitzustellen. Einige schreiben die checksumme auf der Seite, auf der die datei heruntergeladen wird, hin. so passiert das Beispielsweise beim Downloaden von Images für den raspberry pi.

2016-01-30_15h13_53

wie sie sehen wird auf der Website vom Raspberry pi die Art der checksumme, in diesem Fall SHA-1, und danach die checksumme selbst, angezeigt. sie als Downloader müssen nur wissen, welchen Befehl Sie auf Ihre heruntergeladene Datei für welche Art von Checksumme ausführen müssen. Die gängigsten Befehle und arten sind:

  • der Befehl md5sum für MD5-Checksummen
  • der Befehl sha1sum für SHA-1 Checksummen
  • cksum für CRC32-Checksummen

Da es sich auf der Website um eine SHA-1Checksumme handelt, können Sie Ihre eigene checksumme rauskriegen über

sha1sum <Datei>

vergleichen Sie nun Ihre Checksumme mit der checksumme auf der Website. Unterscheiden sich diese, würde ich zuerst versuchen, die Datei erneut herunterzuladen. Unterscheidet Sie sich auch beim zweiten Mal, wurde sie womöglich manipuliert und ich würde sie nicht öffnen bzw. verwenden.

Andere Veröffentlicher von Downloads speichern die Checksumme in eine Datei, die dem Download beiliegt. somit können sie auch noch Jahre später, nachdem die Datei womöglich gar nicht mehr zum Download angeboten wird, noch prüfen, ob diese noch korrekt ist. Wenn Sie selbst eine Datei zum Download anbieten und die checksumme in eine Datei schreiben wollen, können Sie beispielsweise eine MD5 Checksumme über

 

md5sum <datei> > checksumme.txt

reinschreiben. Diese Datei bieten Sie dann entweder extra zum Download an, oder Sie packen den ursprünglichen Download und diese Textdatei nochmal in ein extra Archiv, beispielsweise ein .zip oder .rar-archiv, und bieten dann dieses zum Download an.

Wichtig für den Downloader ist dann, dass der nicht die Checksumme dieses Archivs, in dem die checksummen Datei mit drin ist, testet, sondern den eigentlichen Download testet. Wenn Sie etwa eine .rar-Datei downloaden, in der sich eine weitere .rar-Datei mit dem eigentlichen Download und eine Datei mit der Checksumme befindet, dann testen Sie  nicht die äußere .rar-Datei, sondern die .rar-Datei vom eigentlichen Download, da nur auf diese die Checksumme passt.

Sie können die checksumme entweder selber vergleichen, indem Sie einmal in die datei und einmal auf die ausgabe des md5sum-Befehls schauen, oder sie lassen md5sum den Vergleiche machen über

md5sum -c < checksumme.txt

Damit das geht, müssen Sie im Verzeichnis sein, in welchem sich die Datei befindet, deren Checksumme mit der Checksumme in der Datei checksumme.txt zusammenpassen soll.

Umständlich wird dies, wenn Sie die checksummen mehrere Dateien, also beispielswiese von einem ganzen ordner, vergleichen wollen. Wie Sie selbst checksummen von ganzen Ordnern erstellen und diese an einer anderen Stelle vergleichen, habe ich Ihnen wieter oben schon gezeigt. Wenn Sie jedoch einen ganzen Ordner zum Download anbieten oder einen solchen herunterladen, bieteet es sich an, die CRC32-checksummen aller Dateien in einem ordner in eine .sfv-Datei schreiben. .sfv steht für Simple File Verification. Als Veröffentlicher erstellen Sie eine .sfv-Datei mit den Checksummen aller Dateien in einem ordner über die Software QuickSFV, die es sowohl für Windows als auch Linux gibt. Der Downloader kann dann diese .sfv-datei mit dem gleichen Tool öffnen und schauen, ob die Dateien in seinem heruntergeladenen Ordner mit den Checksummen in der .sfv-Datei übereinstimmen.

Wenn dir dieser Post gefallen hat, teile ihn doch auf Social Media, abonniere und verfolge mich per E-Mail, RSS-Feed, Social Media oder registriere dich auf meiner Seite. Wenn du registriert bist, like den Post bitte, wenn er dir gefallen hat!

]]>
http://dafrk-blog.com/?feed=rss2&p=4185 0
Daten aus Datenbanken, SQL, JSON, XML und Flat Files extrahieren und umwandeln http://dafrk-blog.com/?p=4169 http://dafrk-blog.com/?p=4169#respond Mon, 25 Jan 2016 14:42:38 +0000 http://dafrk-blog.com/?p=4169 Daten aus Datenbanken, SQL, JSON, XML und Flat Files extrahieren und umwandeln weiterlesen →]]> Als Systemadministrator oder Entwickler kommen Sie irgendwann an die Herausforderung, mit Daten umzugehen, die in verschiedenen Formaten vorliegen. Teilweise als Flat File, also beispielsweise als .csv-Datei, die über Microsoft Excel gepflegt wurde, teilweise als .xml-Datei, und teilweise müssen Sie Daten aus einer datenbank extrahieren, um sie in eine andere Datenbank zu laden oder in eine .csv- bzw. xml-Datei zu exportieren. Und einige Datenbanken bieten die Funktion zum Exportieren der Daten in eine Datei mit SQL Statements, aus der Quelltabelle in einer neuen Datenbank wieder aufgebaut werden kann.

In diesem Beitrag, der regelmäßig aktualisiert wird, zeige ich Ihnen, wie Sie daten von verschiedenen Datenbankmanagementsystemen in SQL Statements Files, Flat Files oder .xml-Files exportieren und umgekehrt aus diesen Dateien auch wieder in die Datnebank importieren. Desweiteren zeige ich IHnen, wie Sie .xml-Files in Flat Files und umgekehrt Flat Files in .xml-Files konvertieren.

Das extrahieren von Daten aus einer Quelle, das Umwandeln in ein Zwischenformat und das Laden der Daten in ein Ziel wird auch als Extract, Transform and Load (ETL) bezeichnet.

Datenbanken

In diesem Abschnitt befassen wir uns damit, wie wir Daten aus einzelnen Tabellen einer Datenbank exportieren können. Ich zeige Ihnen hier, wie Sie die Daten aus einer oder mehreren Tabellen der Datenbank in ein Flat File und in SQL-Statement-File bekommen und wie Sie die Tabelle in einer anderen Datenbank aus dem SQL Statement File wieder aufbauen können. Desweiteren zeige ich Ihenn, wie Sie eine bstehende Tabelle aus einem beliebiegn Flat File  befüllen und aufbauen.

SAP HANA

Daten von der Datenbank in ein Flat File oder SQL Statement File exportieren

die einfachste Möglihckeit, DAten aus einer SAP HANA datenbank zu exportieren, ist es,  die gewünschte Tabelle im HANA Studio unter Catalog -> <Schema Name> – Tables auszuwählen und damm im kontextmenü auf Export zu gehen.

2016-01-23_23h47_44

 

Im nächstne FEnster können Sie mehere Tabellen hinzufügen. Haben Sie alle gewünschten objekte selektiert, wählen Sie die Schaltfläche NExt.

2016-01-23_23h50_22

Alternativ können Sie auch bei TAbles im kontextmenü Export wählen und dort dann die Tabelle suchen und hinzufügen

2016-01-23_23h57_34

2016-01-23_23h58_33

Die Daten können Sie nun entweder als BINARY oder als CSV-DAtei exportieren. Egal was Sie hier wählen, Sie exportieren auf diesem Weg nur die MEtadaten über die Tabelle. Das heißt Sie bekommen eine sogenannte create.sql, mit deren Hilfe Sie die Tabelle in einer anderen Datenabnke wieder erstellen können (jedoch ohne daten darin) sowe eine .xml-Datei, welche die Metadaten nochmal extra ausschildert.

Wenn Sie die Daten einer Tabelle (und nicht nur die TAbelle selbst) exportieren wollen, müssen Sie folgendermaßen vorgehen. Suchen Sie die Tabelle, indem Sie im kontextmenü Find Tables wählen und dann den NAmen der tabelle iengeben. Wenn Sie die tabelle dann gefunden haben, setzen Sie einen Haken bei Show Content und Show definition, um sowohol den Inahtl als auch die Definition der tabelle anzuzeigen

2016-01-23_23h55_17

Nun wird Ihnen der inhalt der Tabelle angezeigt. Markieren Sie alle Zeilen, die Sie exportieren wollen, meistens also alle. Ihre erste Möglihckeit ist nun im Kontextmenü zu wählen Export Result. Wenn Sie das tun, erhalten Sie ein Flat File, welches mit Semika getrennt ist. Dieses Ausgabeformat ist super, wenn Sie die Daten anderweitig laden und analysieren wollen, beispielsweise in einer Excel Datei o. Ä.

2016-01-24_00h04_39

Ihre nächste Möglichekti ist im kontextmenü Insert Statement auszuwählen. Sie bekommen einen SQL Editor mit den Insert into Statements eines jeden Datensatzes angezeigt. Diese Insert into-Statements können Sie sich nun rauskopieren und in einer .sql-Datei, beispielsweise data.sql,  abspeichern.

Wenn Sie nun, wie im ersten Schritt beschrieben, die create.sql-Datei der Tabelle exportiert haben, können Sie die Tabelle erst mit Hilfe der create.sql bauen und dann die Daten mithilfe der data.sql, die wir jetzt gerade exportiert haben, wieder befüllen.

Flat File in eine Tabelle laden

Zwar können Sie unter SAP HANA auch über das HANA Studio .csv-Dateien in eine Tabelle laden, das ist jedoch ab einer bestimmten Größe des Flat Files nicht mehr zumutbar, da der importporzess hier sehr langsam ist. Stattdessen empfiehlt es sich, ein Flat File ganz normal mit Hilfe einre sogenannten Kontrolldatei zu importieren.

Erstellen Sie eine Datei namens data.ctl. Wir gehen davon aus, dass ihr Flat File data.csv heißt, die einzlenen Spaltenwerte / ATtribute mit KOmmas und die Datensätze durch Zeilenumbrüche getrennt sind. Dann schrieben Sie also in die data.ctl folgenden Inhalt:

IMPORT FROM CSV FILE '/data/data.csv' INTO "MYTABLE"

WITH

RECORD DELIMITED BY '\n'

FIELD DELIMITED BY ','

DATE FORMAT 'MM/DD/YYYY'

BATCH 1000

FAIL ON INVALID DATA

ERROR LOG /data/error.log;

Damit das später funktioniert, müssen Sie sicherstellen, dass der Betriebssystembenutzer <dbsid>adm Leserechte sowohl auf die data.ctl als auch auf die data.csv hat. MYTABLE ist der Name der Tabelle, in die später die Daten geladen werdne sollen. Diese Tabelle muss vor dem Importvorgang bereits existieren. Sie können die Tabelle dazu entweder vorher über ein CREATE TABLE-SQL-Statement erstellen oder alternativ eine kleine Version der .csv-Datei (mit biespielsweise den ersten 20 Datensätzen) über das HANA Studio laden und dadurch die Tabelle mit diesen 20 Datensätzen erstellen lassen. Genauere Einstellungen wie Primärschlüssel und Indizes können Sie dann über ALTER TABLE oder über das HANA STudio einstellen.DATE FORMAT legt fest, in wlechem Format Datumswerte im Flat File abgelegt sind, falls vorhanden.  BATCH 1000 legt fest, dass die Datensätze in der .csv-Datei in Blöcken von 1000 Datensätzen abgearbeitet werden. FAIL ON INVALID DATA stellt sicher, dass der Importvorgang abgebrochen (und nicht fortgesetzt) wird, wenn es Probleme gibt. Dann geben wir noch eine Datei an, in der Fehlermeldungen beim importvorgang geloggt werdne sollen. Die Datie muss vorher icht existieren, der Betriebssystembenutzer <dbsid>adm muss aber Schreibrechte auf dem Verzeichnis haben, in welchem die Datei erstellt werden soll. Die erzeugte error.log-Datei ist standardmäßig read only, das heißt wenn man den Importvorgang nach einem Fehler ein zweites mal ausführt, wir ddie Datei nicht aktualisiert. Daher vorher die .log-Datei löschen oder auf overwriteable stellen.

Bevor wir den Import starten, stellen wir die beim SQL Editor die Konfigurationseinstellung AUTOCOMMIT aus. REchtslickt dazu im HANA Studio auf den SQL Editor und wählt Show In Properties. Dann setzt ihr AUTOCOMMIT auf false.

Bevor ihr den Import statet, prüft nochmal euer Flat File, ob darin kritische Sonderzeichen wie beispielsweise “ oder ‚ vorkommen. Falls ja, müsst ihr die Sonderzeichen entweder mit beispielsweise dem Linux-Kommanodzeilentool sed entfernen oder euch informieren, wie ihr die Sonderzeichen maskieren müsst, damit SAP HANA sie sauber übernimmt.

Der Import wird dann über den SQL Editor gestartet mittels

IMPORT FROM CONTROL FILE '/data/data.ctl';

 

Oracle

Daten von Der Datenbank in ein Flat File exportieren und wieder importieren

Um Daten aus einer Oracle Datenbank in ein Flat File exportieren wollen, nehmen Sie am besten den SQL Loader. Loggen Sie sich dazu auf der kommandozeile über sqlplus als der User ein, dem das Schema gehört, aus dem Sie DAten exportieren wollen, und geben Sie ein

sqlplus> set heading off

sqlplus> set pagesize 0

sqlplus> spool C:\flat_file.txt

sqlplus>SELECT * from <Schema>.Tabelle;

sqlplus>spool off

Nun haben Sie ein Flat File (in unserem Beispiel gespeichert als C:\flat_file.txt) erhalten mit den Daten der Tabelle, abgetrennt mit Kommata.

Wir erstellen jetzt noch ein sogenanntes discard file als C:\discard_file.dis. diese Datei enthält später alle Datensätze, die nicht geladen wurden. Das kläre ich gleich auf

Die selbe Tabelle können Sie nun mit einem solchen Flat File wieder füllen mit Hilfe von SQL Loader. Dazu bruachen Sie zuerst ein sogenanntes Control File, welches wir einfach mla als C:\control_file.ctl erstellen. Dort legen wir fest

LOAD DATA

INFILE 'C:\flat_file.txt'

DISCARDFILE 'C:\discard_file.dis'

INTO TABLE <tabellenname> TRUNCATE

WHEN <Spaltenname> <> '<Wert>'

FIELDS TERMINATED BY ","

(

<Spaltenname1>,

<Spaltenname2>,

... "TRIM (:<letzter_spaltenname>)"

)

Die Datie sit beinahe selbsterklärend. Das infile ist das Flat File, von dem die Daten geladen werden, das Discard File ist die DAtei mit den WErten, die überspringen werden. Wir laden das in die Tabelle, die wir in der Zeile INTO TABLE angeben, und die Zeile mit TRUNCATE bedeutet, dass wir den aktuellen Inhalt der tbaelle löschen, bevor wir die neuen DAten reinladen. die Zeile WHEN  <Spaltennanem> <> ‚<Wert>‘ ist nur ein biespiel dafür, dass man bestimmte Datensätze auslassen kann, das heißt man kann hier beispielsweise solche Sachen festlegen wie „wenn Herkunftsland = ‚Österreich‘, dann den Datensatz nicht in die Tabelle laden“. Die DAtensätze, die aufgrund dieser Regel ausgelassen werden, werden dann nacher in der discard_file.dis gelistet. FIELDS TERMINATED heißt, dass die einzelnen Werte im Flat File von einem Komma getrennt werden, und dann geben wir die Spaltenüberschriften ein, die unsere Spalten in der Tbaelle bekommen sollen. Am Ende der letzten Spaltenüberschrift schreiben wir diese TRIM-Direktive. Diese ignoriert alle Whitespaces (TAbstopps und Leerzeichen) im Flat File und verhindert somit beispielsweise, dass in einer Tabelle, in der es nur zwie Spalten mit Vorname und Name von Personen gibt, der Name als ‚Huber              ‚ (sau viele Leerezciehn danach) gespeihcert wird.

Jetzt laden wir die Daten in die Tabelle über die kommandozeile

C:\User\Public>sqlldr control=C:\control_file.ct log=C:\log_file.log

Nachdem ihr das kommadno absetzt müsst ihr euch mit einem ORacle-User einloggen. Das flat_file müssen wir nicht angeben, da der Pfad dorthin bereits im control_file.ctl steht. Nun werdne die Daten importiert und Fehlermeldungen oder sonstige Warnnugen werdne in die log_file.log geschrieben.

SAP MaxDB

In MaxDB kann man  über das Kommandozeilenprogramm loadercli exporiterne. Aebr auch über das grafische Database Studio geht es. Sie können im Databse Studio die fragliche Datnebankinstanz auswählen, darauf rechtsklicekn und dann im Kontextmenü den Eintrag EXPORT/IMPORT wählen.  Beim Exportierne können Sie ein Format wähhlen.  Eine einzelne Tabelle wird standardmäßig in das .sv-Format epxoriter.t Datenabnken, Schemata oder User werden entweder im Fromat PAGES oder RECORDS exporitert, diewiederum nur von MaxDB-Instanzen selbst geladen werdne können.

Beim LAden der Daten müssen Sie erst im Databsae Studio über WINDOWS / PREFERENCES / LOADER das Format angeben, in welchem die Daten vorliegnt. Wählen Sie pakacge files für PAGES oder RECORDS-Daten, oder Flat Files für .sv-Daten.

XML DAteien

 

Infos aus XML Dateien unter Linux extrahieren

Nehmen wir an, wir haben eine .xml-Datei wie die folgende

<buch>

 <titel>

 A Game of Thrones

 </titel>

 <autor>

 George R. R. Martin

 </autor>

</buch> 



<buch>

 <titel>

 Der Schatz im Silbersee

 </titel>

 <autor>

 Karl May

 </autor>

</buch>

und nehmen wir an, wir wollen aus der Datei alle Titel der Bücher haben. DAs würde unter Linux beispielsweise über das folgende sed-Kommando gehen.

sed "/titel/,/\/titel/" datei.xml

sed sucht hierbei zuerst nach der Zeichenkette „titel“ und gibtdann von dort aus alles aus, bis es au fdie Ziehcenkette „/titel“ trifft. Somit haben wir alle Titel in der Ausgabe drin. In Verbindung mit dem kommando grep können wir dann nach einem bestimmten Titel suchen, also beispielswiese

sed "/titel/,/\/titel/" datei.xml | grep Schatz im Silbersee

XML-Datei in Flat File umwandeln

Was ist nun, wenn wir eine XML-Datei in ein Flat File umwandeln wollen? Beispielsiwese in eine .csv-Datei, wo die Spaltenwerte durch kommas getrennt sind. Wir wollen also aus

<buch>

 <titel>

 A Game of Thrones

 </titel>

 <autor>

 George R. R. Martin

 </autor>

</buch> 



<buch>

 <titel>

 Der Schatz im Silbersee

 </titel>

 <autor>

 Karl May

 </autor>

</buch>

das hier machen

A Game of Thrones, George R. R. Martin

Der Schatz im Silbersee, Karl May

Denn wie wir weiter oben schon aus dem kapitel der Datenbanken gelernt haben, können wir solche Flat Files sehr leicht in eine Datenbank laden.

als erstes müssen wir es scahffen, dass alle Datensätze zusammen in einer Zeile stehen. Zuerst bringen wir mal alle Zeichen in der xml in eine einzige Zeile, indem wir alle Zeilenumbrüche entfernen.

tr -d '\n' < datei.xml

Jetzt stehen alle Daten in einer Zeile. Da wir ohnehin die Container <buch> und </buch> nicht mehr brauchen können, ersetzen wir <buch> durch eine leere Zeichenkette

tr -d '\n' < datei.xml | sed "s/buch//g"

und </buch> durch einen Zeilenumbruch.

tr -d '\n' < datei.xml | sed "s/<buch>//g" |sed "s/<\/buch>/\\n/g"

Jetzt stehen alle Bücher in einer extra Zeile.

Jetzt bekommen wir den die anderen Container weg, indem wir den öffnenden Container wieder durch eine leere Zeichenkette ersetzen.

tr -d '\n' < datei.xml | sed "s/<buch>//g" |sed "s/<\/buch>/\\n/g" | sed "s/<titel>//g"

und den schließenden Container durch ein Komma

tr -d '\n' < datei.xml | sed "s/<buch>//g" |sed "s/<\/buch>/\\n/g" | sed "s/<titel>//g" | sed "s/<\/titel>|,/g"

Wenn wir jetzt die Schritte für den Titel-Container für den Autor wiederholen

tr -d '\n' < datei.xml | sed "s/<buch>//g" |sed "s/<\/buch>/\\n/g" | sed "s/<titel>//g" | sed "s/<\/titel>|,/g" | sed "s/<autor>//g" | sed "s/<\/autor>|,/g"

haben wir nur noch das PRoblem, dass hinter dem Autor noch ein komma zu viel steht, dass wir nicht mehr brauchen. Also suchen wir zum Schluss noch nach Kommas, auf die direkt ein Zeilenumbruch folgt, und ersetzen diese durch einen leeren String, dazu pipen wir zu guter letzt einfach nur noch dran:

sed "s/,$//g"

Schon haben wir ein Flat File.

Flat Files in .xml-Dateien umwandeln

Flat Files, das haben Sie mittlerweile schon kennengelernt, sind Dateien, die wie Tabellen aufgebaut sind. Nur sind die Spalten der Tabelle eben durch Delimiters voneinander abgegrenzt. In einer .csv-Datei beispielsweise werden die Daten durch Kommata getrennt.

Wie Sie Flat Files in verschiedene Datenbanken laden, habe ich Ihnen bereits im Kapitel zur jeweiligen Datenbank gezeigt.

Wir wollen im Beispiel aus einem Flat File im Format

Johann,Johanson,45,Rechnungswesen

Anne,Anderson,55,Personalwesen

Mike,Michaelson,31,Programmierung

eine XML-Datei machen im Format

<angestellter>

 <vorname>

  Johann

 </vorname>

 <name>

  JOhnason

 </name>

 <alter>

  45

 </alter>

 <abteilung>

  Rechnungswesen

 </abteilung>

</angestellter>

...

machen.

Als erstes wandeln wir jeden Zeilenumbruch so um, dass ein XML-Container geschlossen und ein neuer begonnen wird.

sed "s/$/<\/angestellter>$<angestellter>/g"

damit erhalten wir

Johann,Johanson,45,Rechnungswesen</angestellter>

<angestellter>Anne,Anderson,55,Personalwesen</angestellter>

<angestellter>Mike,Michaelson,31,Programmierung</angestellter>

Dann schließen wir Vornamen und Nachnamen ein

sed "s/[A-Za-z]+,[A-Za-z]+/<vorname>[a-Z]+<\/vorname><name>[A-Za-z]<\/name>/g"

damit erhalten wird

<vorname>Johann</vorname><name>Johanson</name>,45,Rechnungswesen</angestellter>

<angestellter><vorname>Anne</vorname><name>Anderson</name>,55,Personalwesen</angestellter>

<angestellter><vorname>Mike</vorname><name>Michaelson</name>,31,Programmierung</angestellter>

dann ersetzen wir das Alter

sed "s/,[0-9]{2},/<alter>[0-9]{2}<\/alter>/g"

und erhalten

<vorname>Johann</vorname><name>Johanson</name><alter>45</alter>Rechnungswesen</angestellter>

<angestellter><vorname>Anne</vorname><name>Anderson</name><alter>55</alter>Personalwesen</angestellter>

<angestellter><vorname>Mike</vorname><name>Michaelson</name><alter>31</alter>Programmierung</angestellter>

und zu guter letzt schließ en wir noch die Programmierung ein

sed "s/<\/alter>[a-zA-Z]+/<\alter><abteilung>[a-zA-Z]+<\/abteilung>/g"

und schon ist unsere XML fertig, bis auf ein Detail

<vorname>Johann</vorname><name>Johanson</name><alter>45</alter><abteilung>Rechnungswesen</abteilung></angestellter>

<angestellter><vorname>Anne</vorname><name>Anderson</name><alter>55</alter><abteilung>Personalwesen</abteilung></angestellter>

<angestellter><vorname>Mike</vorname><name>Michaelson</name><alter>31</alter><abteilung>Programmierung</abteilung></angestellter>

und zwar müssen wir noch an den Anfang der Datei ein <angestellter> hinkriegen

sed "1s/^/<angestellter>/"

Die ganzen sed-Befehle hintereinander gepiped liefern uns dann den richtigen Output

<angestellter><vorname>Johann</vorname><name>Johanson</name><alter>45</alter><abteilung>Rechnungswesen</abteilung></angestellter>

<angestellter><vorname>Anne</vorname><name>Anderson</name><alter>55</alter><abteilung>Personalwesen</abteilung></angestellter>

<angestellter><vorname>Mike</vorname><name>Michaelson</name><alter>31</alter><abteilung>Programmierung</abteilung></angestellter>

 

Wenn dir dieser Post gefallen hat, teile ihn doch auf Social Media, abonniere und verfolge mich per E-Mail, RSS-Feed, Social Media oder registriere dich auf meiner Seite. Wenn du registriert bist, like den Post bitte, wenn er dir gefallen hat!

]]>
http://dafrk-blog.com/?feed=rss2&p=4169 0
Textoperationen für Systemadministratoren (Linux Shell Tools, Texteditoren, Microsoft Excel) http://dafrk-blog.com/?p=4116 http://dafrk-blog.com/?p=4116#respond Sat, 23 Jan 2016 21:31:43 +0000 http://dafrk-blog.com/?p=4116 Textoperationen für Systemadministratoren (Linux Shell Tools, Texteditoren, Microsoft Excel) weiterlesen →]]> Als Systemadministrator haben Sie es oft mit TExtdaten zu tun, die Sie auf irgendeine Art und Weise verarbeiten oder sortieren müssen. Dabei kämpfen Sie an allen Fronten: in der Windows- oder Linux-Kommandozeile, mit Microsoft Excel und mit simplen Texteditoren – seien es Linux/Unix-Shelleditroen wie vi, vim oder nano oder grafische Windows-Editoren wie Notepad, Wordpad oder Notepad++. Und zu guter letzt vergleichen Sie noch unterschiedliche Versionen einer Textdatei auf Unterschiede und fügen diese Unterschiede zusammen.

In diesem Blogpost zeige ich Ihnen anhand einiger Situationen aus meinem Administrationsalltag, wie Sie verschiedene Herausforderungen mit Hilfe von Kommandozeilen- und Shell-Kommandos, Microsoft Excel und den TExteditoren vi (Linux) und Notepad++ (Windows) lösen können. Der Post wird aktualisiert, sobald mir irgendwann wieder eine neue Situation begegnet, an der ich feilen muss.

Die Linux-Textverarbeitungstools auf Shell-Ebene

Den output von kommandos in Skripten loggen

Wenn ihr unter Linux ein Skript, etwa in Form einer .sh-Datei, schreibt, ist es euch vielleicht schonmla passiert, dass euer Skript nicht durch gelaufen, also auf einen Fehler gestoßen, ist. Zur Fehlersuche machen sich viele die Mühe und führen die einzelnen kommandos einen nach dem anderen aus, was jedoch absolut unnötig ist.

Stattdessen könnt ihr das Skript ausführen mit

sh -x <Skriptdatei>

bzw. 

bash -x <Skriptdatei>

und euch wird der Output eurer kommandos angezeigt.

Und ihr könnt sogar, ohne das Skript live bie der Ausführung zu beobachten, den Output der Kommandos loggen. Dabei empfiehlt es sich, normalen Output in eine Datei, und Fehlermeldungen in eine sepzielle andere Datei zu loggen. Dazu würde ich einfach an das kommadno anhängen

sh -x <SkriptDatei> > normal.log 2> error.log

somit wird der normale Output der Skript-Kommandos in die Datei normal.log geschrieben und alle Fehlermeldungen in die Datei error.log. Wenn ihr nun eure Skriptdatei beispielsweise über die /etc/crontab so einplant, könnt ihr euch am nächsten Tag, wenn euer skript nicht sauber durchgelaufen ist, ansehen, warum das passiert ist. Dabei muss euch bewusst sein, dass die einfahcen >-Zeichen dafür sorgen, dass die Datei jedesmal überschrieben wird, wenn die Log-Datei neu befüllt wird, das heißt die alten Einträge verschwinden dann. WEnn ihr stattdessen die Einträge jedesmal an den aktuellen Inhalt einfach nur dranhängen wollt (ohne den alten zu überschrieben), müsst ihr statt einem > zwei hintereinander schreiben, also

sh -x <SkriptDatei> >> normal.log 2>> error.log

Was meine persönlich preferierte einstellung ist, mit dem Kommando date füge ich dann jedesmal, wenn das Skript läuft, einen aktuellen Zietstempel hinzu, damit ich später unterscheiden kann, welcher Log-Abschnitt zu welchem TAg gehört.

Wenn du mit Absicht verhindern möchtest, dass du versehentlich DAteien überschreibst, indem du in deinen Skripten statt zwei > nur einen > schreibst, dann kannst ud dies verhindern, indem du in der Shell den Befehl

set -o noclobber

wirfst. Von nun an kannst du Dateien nicht mehr mit einem > überschreiben, es sei denn du schreibst ab sofort >| oder stellst den Parameter wieder um über

set +o noclobber

Ich persönlich setze diesen Parameter immer wenn ich Skripte schreibe oder manuell mit Dateien auf der Kommandozeile rumspiele, zur Sicherheit.

Daten suchen und extrahieren

Daten extrahieren und formatieren mit awk

mit awk können wir die Ausgabe von anderen Kommandos formatieren. Nehemn wir als Beispiel mal die Datie /etc/passwd, deren inhalt aus Zeilen im Folgenden Format besteht
sshd:x:497:495:SSH daemon:/var/lib/sshd:/bin/false

Was ist nun, wenn wir nicht die gesamte /etc/passwd asugeben wollen, sondern nur die Benuzternamen aller Benutzer sehen wollen?

Für diesen Fall können wir awk benutzen, um die einzelnen Felder, die durch Doppelpunkte (:) voneiannder getrennt werden, in Variablen speichern. Die Variable $1 steht für das erste Feld (den Benutzernamen), $3 für das drittte Feld (die uid). Wenn wir also nur die Namen aus der Datei, sprich das erste Feld, ausgeben wollen, setzenw ir das Kommando ab:

awk -F":" ' { print $1 }  ' /etc/passwd

und wir bekommen nun nur noch die Usernamen angezeigt.

Zusätzlich ist jetzt noch möglich, nicht nur die Spalte, sondern aucheinen Zeilenbereich auszuwäheln, den man sich anzeigen lassen möchte. Dazu würden wir unser Kommando einfach abändern in.

awk -F":" ' NR==5,NR==10{ print $1 }  ' /etc/passwd

Damit bekomemn wir nur die Zeilen 5 bis 10.

Wenn wir jetzt mehrere Spalten haben wollen, z. B. zusätzlich noch die uid, ändern wir das kommando ab in.

awk -F":" ' NR==5,NR==10{ print $1,$3 }  ' /etc/passwd

WAs euch jetzt noch auffallen wird ist, dass die Spalten unschön formatiert sind, da die Spalten nicht immer sauber untereinander stehen, je nachdem wie lange die Vorgängerspalte ist.

Wir können aber unsere Spalten so anordnen, dass zusammengehörige Zellen in der selben Spalte sich immer die selbe Menge an Zeichen reservieren, und erst nach diesem Reservierungsraum die nächste Spalte beginnen kann. So stehen die Sptlen sauber untereinander. Das können wir tun mit

awk -F":" ' NR==5,NR==10{ printf "%-8s %3d\n" $1,$3 }  ' /etc/passwd

Wie ist das nun zu interpretieren? Als erstes gilt es zu erkenenn, dass wir nurn printf statt print verwendne. %- heißt, dass wir die erste Spalte linksbündig positionieren und nur % heißt, dass wir die dritte Spalte rechtsbündig positionieren. Die Zahlen danach stehen für die Anzahl der zu reservierenden Ziechen für die Spalte und die kleinen Buchstbaen am Schluss sind der Datentyp – s für „string“, also Zeichenkette, und d für „decimal“, als Dezimalzahl. Das \n ganz am Ende sagt aus, dass wir danach eine neue Zeile anfangen wollen.

Jetzt wollen wir noch Überschriften für unsere Spalten haben. Dazu ändern wir unseren Befehl ab in.

awk -F":" '

 BEGIN ( printf "%-8s %-3s\n", "User", "UID" )

 NR==5,NR==10{ printf "%-8s %3d\n" $1,$3 }  ' /etc/passwd

Na, seid ihr dahintergestiegen? an den Anfang der Datei wird wieder die erste Spalte linksbündig mit acht Zeichen Reservierung mit der Beshriebung „user“ und die rechte Spalte, diesmal allerdings ebenfalls linksbündig psotioniert, mit 3 Zeichen Reseriverung und der Beschreibung „UID“ gesetzt.

Spalten, die nur Zahlenwerte enthatlen, könnenw ir zusammenrechnen

 cat test.csv | awk -F, '{total+=$3} END {print "Summe: "total}'

Jetzt wollen wir nur Datensätze ausgeben, bei denen die letzte Zeile einen kleinbcuhstaben enthält.

cat test.csv | awk -F, '{if ($NF ~ "[a-z]") print}'

 

Daten extrahieren mit cut

die Werte aus den einzelnen Spalten in der /etc/passwd könnten wir aber auch mit cut extrahieren. Beispielsweise können wir, wenn wir nur den usernamen (1. Spalte) wollen, sagen

cat /etc/passwd | cut  -d":" -f1

Wir nehmen also den Output von cat /etc/passwd, sagen cut, dass der Trenner zwischen den Spalten ein Dopplepunkt ist, und geben mit -f1 an, dass wir nur die erste Spalte wollen. Wenn wir jetzt noch die UID dazu wollen, geben wir an

cat /etc/passwd | cut -d":" -f1,3

zählen mit wc

manchmal möchte ich wissen, ob  sich die Anzahl an DAteien in einem Ordner geändert haben. Die Anzahl an Dateien, die das komamdno ls -la in einem Ordner findet, lasse ich mir ausgeben mit

ls -la | wc -l

denn wc -l zählt die Anzahl der Zeilen einer Ausgabe. Wenn ich hingegen beispielsweise nur die Anzahl der Unterordner haben möchte, arbeite ich mit find

find . -type d | wc -l

Wenn ihr ein Kommando nur an wc piped, also beispielswiese

cat /etc/passwd | wc

dann stehen in der ersten Spalte die Anzahl der Zeilen, in der zweiten die Anzahl der erkannten Worte (also zusammengeschriebene Buchstbaen, die durch ein LEerezichen, TAbstopp, Punkt, Doppelpunkt, Komma oder Ähnliches getrennt werden) und ganz zum Schluss die Gesamtanzahl an Zeichen in der Ausgabe.

schauen, welche Dateien einen bestimmten TExt enthalten mit grep

Manchmal wisst ihr, dass eine bestimmte Textdatei einen gewissen Text enthalten muss, seid euch aber nicht mehr sicher, welche. Oder ihr möchtet sicher gehen, dass ein bestimmter Konfigurationsparameter, den ihr geändert habt, nicht noch einmal in einer anderen Konfiugrationsdatei gesetzt wird, sodass eure Änderung wieder überschrieben wird.

Mit dem kommando

grep 'max_memory' /etc/*

sucht ihr in jeder Datei unterhalb von /etc/ nach dem Begriff max_memory.

wenn ihr hingegen alle Dateien innerhalb eines Ordners finden wollt, die einen bestimmten Begriff enthalten, geht das mit

grep -Ril "suchstring" <pfad>

Alternative

grep -rnw '/pfad/' -e "text"

Alternativ kann auch das Komamdnozeilentool ack genutzt werden.

ack 'suchstring'

umgekehrter FAll: gib alles aus einer Datei aus, außer Zeilen mit

den umgekehrten Fall, dass man alles ausgegeben haben will, außer die Zeilen die etwas bestimmtes enthalten, realisiert man mit

grep -v 'max_memory' /etc/*

Damit werden alle Zeilen ausgegeben, außer die, welche max_memory enthalten.

Textdateien, die nicht sauber interpretiert werden

Sehr störend sind unsauber gesetzte Textdateien, die non-printing-characters enthalten, die man mit einem Befehl wie

cat <datei>

nicht sehen kann, jedoch die Interpretation der Datei durch andere Kommnandos oder Programme durcheinander bringen. Solche Zeichen sind biespielswiese die Zeilenumbrüche verschiedener Betriebssysteme (Windows benutzt andere Zeilenumbrüche als Linux und diess wiederum andere als Mac OS X). Solche non-printing-characters kann man sich jedoch markieren lassen über

cat -vet <datei>

Merkspruch: If your cat is ill, you take it to the vet.

Ausgaben sortieren mit sort

mit sort lassen sich die Ausgaben von Befehlen oder die Zielen von DAteien nachträglich sortieren. So ist es beispieslweise möglichsich die Ausgabe der Datei /etc/passwd nach alphabetischer Reihenfolge der Benutzernamen anzeigen zu lassen über

cat /etc/passwd | sort

Wollen wir es in der andren REihenfolge, also von Z nach A, dann geben wir an

cat /etc/passwd | sort -r

Ich sortiere eigentlich so gut wie alle meine Ausgaben unter Linux mit sort, außer die Ausgbae von ls -la, also der Anzeige von Dateien in einem Verzeihcnis, diesen sortiere ich einfach über den PArameter

ls -lart

denn sortiert dann automatisch von obne (älteste Datei) nach unten (neueste Datei), so dass ich am Ende des kommandozeilenoutputs die neuesten Dateien stehen habe, etwa die aktuellste Version einer .log-Datei.

Arbeiten mit regulären Ausdrücken und sed

Als Ergänzung, um die regulären Ausdrücke aus diesem kapitel zu verstehen, solltet ihr meinen Post über reguläre Ausdrücke lesen bzw. darin als Referenz nachschlagen.

Die Linux-Binary sed nutze ich sehr gerne in Verbindung mit regulären Ausdrücken, denn mit sed ist es möglich, nach Zeichenketten zu suchen, die in einen regulären Ausdruck passen, und diese dann durch eine andere Zeichenkette zu ersetzen.

Somit wäre es beispielsweise möglich, die Doppelpunkte (:) zwischen den Einträgen in der /etc/passwd zu ersetzen, indem man sagt

sed -e 's/:/-/g'

Wie sit das kommando zu lesen? der PArmaeter -e sagt dem Kommando sed, dass jetzt ein regulärer Ausdruck kommt (statt einer bloßen „dummen“ Zeichenkette), /s/ steht für substitute, sagt also dem Kommando sed, dass es nach der Zeichenkette, die nach dem ersten SChrägstrich (/) kommt, suchen soll, und diese durch die Zeichenkette ersetzen soll, die nach dem zweiten Schrägstrich kommt. Wir ersetzen also den Doppelpunkt durch den Bindestrich. und /g steht für global, das bedeutet, wir wollen nicht nur die allererste Zeichenkette, die gefunden wird, ersetzen, sondern alle Zeichenketten, die auf diese Art und Weise gefunden werden.

In 99% aller Fälle werdet ihr das Kommando sed in folgendem Format verwenden:

sed -e 's/<suche nach dem>/<ersetze mit dem>/g

Zeichen ersetzen oder löschen mit tr

tr funtioniert ähnlich wie sed, also es sucht nach Zeichenketten und ersetzt oder löscht diese. Beispielsweise ersetzt das kommando

tr a o <datei>

alle klein geschriebenen a’s in einer Datei durch klein geschrieben o’s.

Nun werdet ihr euch denken: „Ja gut, dafür hätte ich aber auch sed nutzen können, warum also tr überhaupt nutzen?“. Naja, mit tr ist es sehr leicht, mehrfache Ersetzungen zu beschreiben, beispielsweise ersetzt das kommando

tr mo Tx <datei>

alle klein geschriebenen m durch groß geschriebene T’s und alle klein greschriebenn o’s durhc klein geschriebene x. Dafür hättet ihr eigentlich zweimal das sed-Kommando hintereinander ausführen müssen.

Immer noch nicht überzeugt? dann lasst uns mal alle KLeinbuchstaben zu Großbuchstbaen umwandeln.

tr [:lower:] [:upper:]

doppelte Zeilen entfernen mit uniq

DAs Tool uniq wird ursprünglich dazu verwendet, um doppelt oder mehrfach vorkommende Zeilen in einer Datei zu entfernen. uniq würde als aus einer Datei mit Inhalt

 

a

a

a

b

b

b

c

c

c

cc

folgendes:

a

b

c

cc

Jedoch funktioniert es leider nicht bie einer Datei wie der folgenden:

a

b

a

b

cc

c

a

c

b

a

c

an dieser Datei würde uniq nämlich leider nichts ändertn, da uniq nur aufeinander folgende doppelte Zeilen entfernt. Dieses Problem könnt ihr lösen mit dem kommando sort, welches wir weiter oben schon kennegelernt haben. DAs Kommando sort bringt die gleichlautenden Zeilen untereinander und dann kann uniq seinen Job machen.

sort <datei> | uniq

Die Anzahl der Redundanzen, also der Vorkommnisse einer Zeile, könnt ihr euch ausgeben lasen über den PArameter -c. Sprich das KOmmando

sort <datei> | uniq -c

würde bei dieser Datei

a

a

a

b

b

b

c

c

c

cc

ausgeben

3 a

3 b

3 c

1 cc

Der Parameter -u hingegen gibt euch nur die Zeilen aus, die nicht wiederholt werden, das kommando

sort <datei> | uniq -u

führt also zur Ausgabe

cc

Mit anderen Tools wie beispielsweise grep, tr, cut und awk kombiniert könnt ihr euch natürlich vorstellen, dass ihr mit uniq auch doppelte Spalten oder Wörter entfernen könnt, indem ihr erst mit grep, cut und awk die Spalten von dem restlichen Inhalt der Datei isoliert, so dass nur noch die Wörter bzw. Spalten in dne Zeilen vorkommen, und ihr diese doppelten dann entsprechend entfernen könnt. Wie auch immer, das funktioniert natürlich nur, wenn man es sich leisten kann, die doppelten Einträge erst in einzelne Zeilen zu extrahieren. WEnn die Originaldatei jedoch unangetastet bleiben soll, müsst ihr Skriptsprachen oder die Suchen und Erstezen Funktion von Texteditoren verwenden. Für das einfache Entfernen von doppelten, direkt hintereinander geschrieben Worten in ein und derselben Zeile jedoch kann euch noch dieser sed Befehl helfen.

Dateiversionen vergleichen mit sdiff

wenn ihr zwei Versionen einer Textdatei vergleichen wollt, um die Unterschiede in den Dateien zu sehen, empfehle ich euch sdiff. Beide Dateien werdne gegenübergestellt und ihr seht die Änderungen sofort.

sdiff <datei1> <datei2>

Die beiden Versionen werden dann gegenübergestellt und in der Mitte wird durch ein Zeichen (Gutter genannt) angedeudetet, ob die Zeilen gleich oder unterschiedlich sind

whitespace Zeilen sind gleich oder Änderungen wurdne ignroiert dank der –ignore option
| Zeilen unterschiedne sich, beide sind unvollständig.
< nur die erste Datei enthält die Zeile
> nur die zweite Datei enthält die Zeile
( nur die erste Datei enthält die Ziele, aber der Unterschied wird ignoriert
) nur die zweite Datei enthält die Zeile, aber der Unterschied wird ignoriert.
\ die Zeilen untershciedne sich und die erste Zeile ist unvollständig
/ die Zeilen unterscheidne sich und eie zweite Zeile ist unterschiedlich

Dateiversionen aktualisieren mit patch und merge

Wenn zwei Personen an der selben Textdatei arbeiten, möchte Person A seine Änderungen PErson B schicken, damit PErson B mit den Implementierungen von PErson A weiterarbeiten kann. Umgekehrt kann Person B seine Ämnderungen Person A schicken, so dass beide auf dem gleichen STand sind.

Person A möchte jetzt Person B seine Änderungen schicken, dazu hat PErson A die letzte Dateiversion, von der beide PErsonen aus angefangen haben zu arbeiten, eine Kopie namens datei1. Und die aktuelle Version, die er bearbeitet hat, ist datei2. Jetzt erzeugt Person A erstmal eine .patch-Datei, die er Person B schicken kann, über:

diff -u <datei1> <datei2> > persona.patch

Wir vergleichen also datei1 mit datei2 und speichern die unterschiede von von datei2 gegenüber datei1 in einer .patch-datei. Person A schickt nun Person B diese .patch-Datei zu.

Nun macht Person B ebenfalls eine .patch-Datei von seiner datei1 über

diff -u <datei1> <datei2> > personb.patch

und schickt diese Version Person A. Beide Personen kommen nun auf den selben Stand, indem sie zusammen auf datei1 beide .patch-DAteien einspielen. Sowohl Person A als auch Person B geben also ein

patch < persona.patch

patch < personb.patch

die ursprüngliche Version von Datei1 wird also mit den Änderungen der beiden Personen gepatcht und somit wieder auf einen gemeinsamen STand gebracht. Dateinamen müssen nicht angegeben werden, da der Name der Orignaldatei <datei1> in den .patch-DAteien hinterlegt ist. Problem ist, dass wenn PErson B eine Änderung in einer Zeile macht, die Person A ebenfalls bearbeitet hat, überschreibt der Patch von PErson B die Änderungen von Person A.

Einige würden jetzt vielleicht argumentieren, dass dieser Prozess ziemlich kompliziert ist, vor allem wenn nicht nur zwei, sondern mehree Personen zusammen an einer Datei arbeiten. Undd as ist auch wirklich so. Linux bietet hierzu noch eine bessere Binary namens merge. Merge nimmt einfach nur die drei Dateien – die Original <datei1>, die <datei2> von Person A und die <datei2> von Person B und führt ide Änderungen aus <datei2> von Person A und <datei2> von Person B automatisch in <datei1> über. Der Vorteil an merge gegenüber patch ist außerdem, dass merge Konflikte erkennt. Wenn also Person B die gleihce Zeile bearbeitet hat wie PErson A, wird eine Warnung ausgegebn nach dem Motto: „einigt euch doch zuerst mal, welche Änderung übernommen bzw. die wie Änderungen zusammengeführt werden sollen“.

Das kommando würde so gehen

merge <datei1> <datei2_persona> <datei2_personb>

Alle Logs in einem Verzeichnis verfolgen

Um Änderungen an mehreren Logdateien in einem Verzeichnis zu folgen – außer den komprimierten Log-DAteien mit einer Dateiendung wie .gz, kann folgendes kommando genutzt werden

tail -f `ls -l /var/logs/myLog* |grep -v .gz$`

Textdateien spalten- oder zeilenweise zusammenführen mit paste

Nehemn wir an, ihr habt zwei TExtdateien, die mehrere Zeilen enthatlen, und diese Zeilen wollt ihr in einer Art Tabellenstruktur zusammenführen. Also die Zeilen von Datei 1 werden die erste Spalte und die Zeilenvon Datei 2 werden die zweite Spalte. DAs komamndo

paste -d: <datei1> <datei2>

führt die Zeilen von Datei 1 und Datei 2 so zusammen, dass die Spalten durch einen Doppelpunkt (:) voneinander getrennt werden

<datei1_zeile1>:<datei2_zeile1>

<datei1_zeile2>:<datei2_zeile2>

<datei1_zeile3>:<datei2_zeile3>

Ihr könnt mit paste aber auch mehrer Zeilen zu einer zusammenfügen. Beispiel, wir haben eine Text Datei mit den Adressdaten mehrerer Personen

Herr

Hubert

Holzmann

0170333333

hubert.holzmann@web.de



Frau

Ute

Ulbricht

0170444444

ute.ulbricht@web.de



Herr

Axel

Mustermann

0170555555

axel.mustermann@web.de

DAnn könnt ihr die Adressdaten jeder Person in eine Spalte kriegen über

< <datei>.txt paste - - - - -

und erhaltet als Ausgabe

Herr Hubert Holzmann 017033333 hubert.holzmann@web.de

Frau Ute Ulbricht 01704444 ute.ulbricht@web.de

Herr Axel Mustermann 01705555 axel.mustermann@web.de

und mit dem PArmaeter -d könnt ihr stattt Leerzeichen auf beispielswiese Kommata oder andere Trennzeichen benutzen, um die Spalten voneiannder abzutrennen. WEnn ihr hierfür beispielsweise Kommata nehmt, könnt ihr die Datensätze so nachher über ein INSERT INTO-Statement in eine Datenbank laden.

Windows-PowerShell-Textverarbeitung

grep unter Powershell mit sls

auch die PowerShell hat eine grep-Funktionalität, mit dessen Hilfe man Text aus der Ausgabe eines komamndos filtern kann. dAs kommando heißt select-string, kurz sls. Wenn ich beispielsweise aus dem kommando netstat-ab alle Verbindungen über einen bestimmten Port rausfinden möchte, beispielsweise 4903, dann gebe ich ein

netstat -ab | sls 4903

häufig möchte ich noch Zeilen sehen, die vor oder nach den gefundenen Zeilen kommen. Wenn ich möchte, dass ich die 5 Zeilen vor und nach der gefundenen Zeile anzeigen lasse, dann gebe ich ein

netstat -ab | sls 4903 -Context 5,5

wollt ihr hingegen nicht die passenden Zeilen ausgeben, sondern nach DAteien suchen, die einen bestimmten TExt enthalten, dann geht das über

Get-ChildItem -Recurse filespec | Select-String pattern | Select-Object -Unique Path

ls -r filespec | sls pattern | select -u Path

head und tail

Auch die Unix/Linux-Kommandos head und tail lassen sich unter Windows mit Hilfe der PowerShell ersetzen. Dazu piped man die Ausgabe des vorgägnerkommandos an das cmdlet select. Die folgende Zeile gibt nur die ersten 10 Dienste auf dem Windows-system aus

get-process | select -first 10

Befehlsausgabe in Windows-FEnster anzeigen mit out-gridview

ein meiner Meinung nach sehr cooles Feature ist die Möglichkeit, sich eine Befehlsausgabe in einem übersichtlichen Windows-Fenster anzeigen zu lassen. Dazu muss allerdings auf dem Rechner die Powershell ISE zwingend installiert sein, was jedoch meistnes der Fall ist.

awk Alternative mit split

wie auch unter Linux wollten wir auch mit der Powershell verschiedene Saplten einer Datei, beispielsweise aus einem .csv-Dokument, extrahieren können. DAzu nutzen wir in der Powershell das split-Tool.

Nehmen wir an, wir haben eine .csv-Dtei, in welcehr die einzlenen Spalten mit kommas getrennt sind. DAnn bekommen wir die zweite Spalte dieser Datei extrahiert über

Get-Content <.csv-Datei> | %{ $_.Split(',')[1]; }

ihr seht also, innerhalb der Split-Satements gebt ihr das Trennzeichen und die Spalte an, die ihr haben wollt. Ebenfalls fällt auf, dass die zweite Spalte mit der Ziffer 1 angegeben wird, das heißt, Split fängt bei 0 an, zu zählen.

 

Mit Split könnt ihr auch eine Spatle, die nur Zahlenwerte enthält, zusammenzählen. WEnn unsere zweite Spalte nur Zahlenwerte enthält, bekommen wir die Summe dieser Spalte über

Get-Content <.csv-Datei> | %{ [int]$total+=$_.Split(',')[1]; } ; Write-Host "Summe: $total"

Und natürlich könnenw ri auch wieder mit regulären Ausdrücken arbeiten. Wir geben einen DAtensatz nur dann aus, wenn die letzte Saptle einen Kleinbuchstaben enthält

Get-Content <csv-Datei> | %{ if ($_.Split(',')[-1] -match "[a-z]") { $_; } }

sed Alternative

Auch eine Alternative zu sed gibt es in der Powershell, mit der wir Zeichen ersetzen können. Auch hier können wir wieder mit regulären Ausdrücken arbeiten

gc <datei> | %{$_ -replace <regulärer Ausdruck>,”replace”}

 

Ein Kommando auf mehrere Dateien in richtiger Reihenfolge

Herausforderung: In meinem Beispiel versuche ich, ein System zu patchen, in welchem verschiedene Dateien abhängig von ihrem Patchlevel in der richtigen Reihenfolge entpackt werden müssen. Der Extraktbefehl ist für alle Dateien derselbe. Anstatt diesen also jedesmal neu einzutippen, möchte ich diesen nur einmal schreiben.

In diesem Projekt verwende ich absichtlich nicht die LinuxKommandozeilentools tr, cut, sed und awk, die ich oben schonmal gezeigt habe, um euch den Lösungsansatz via Texteditor und Excel zu zeigen.

Als erstes exportiere ich mir eine Liste aller Dateien. Die zu entpackenden .SAR-Archive habe ich mir hierzu bereits in ein extra Verzeichnis heruntergeladen, von dem aus ich mir über das Linux-Kommando

ls -la > files.txt

eine Liste erstellen lasse. Windows-Alternatie ist das Kommando dir. Die files.txt lade ich mir mittels SCP oder SFTP vom Linux Server auf meinen Windows-Arbeitsplatz herunter. So sieht meine files.txt derzeit aus.

total 637912

drwxr-xr-x  2 root   root        4096 Jan 22 12:07 .

drwxrwxrwx 17 s4hadm sapsys      4096 Jan 22 11:55 ..

-rw-r--r--  1 root   root           8 Jan 22 11:59 R3check_22-80000699

-rw-r--r--  1 root   root     3957559 Jan 22 11:59 R3check_22-80000699.SAR

-rw-r--r--  1 root   root          15 Jan 22 11:59 R3ldctl_26-80000698

-rw-r--r--  1 root   root     1916846 Jan 22 11:59 R3ldctl_26-80000698.SAR

-rw-r--r--  1 root   root           7 Jan 22 11:59 R3load_23-80000698

-rw-r--r--  1 root   root     3473641 Jan 22 11:59 R3load_23-80000698.SAR

-rw-r--r--  1 root   root           8 Jan 22 11:59 R3szchk_19-80000698

-rw-r--r--  1 root   root     1789191 Jan 22 11:59 R3szchk_19-80000698.SAR

-rw-r--r--  1 root   root           5 Jan 22 11:59 R3ta_28-80000699

-rw-r--r--  1 root   root     2115907 Jan 22 11:59 R3ta_28-80000699.SAR

-rw-r--r--  1 root   root           8 Jan 22 11:59 R3trans_33-80000699

-rw-r--r--  1 root   root     4021570 Jan 22 11:59 R3trans_33-80000699.SAR

-rw-r--r--  1 root   root          14 Jan 22 12:06 SAPCRYPTOLIBP_8447-20011697

-rw-r--r--  1 root   root     2361623 Jan 22 12:06 SAPCRYPTOLIBP_8447-20011697.SAR

-rw-r--r--  1 root   root          21 Jan 22 12:03 SAPEXEDB_23-80000698

-rw-r--r--  1 root   root    14195185 Jan 22 11:56 SAPEXEDB_23-80000698.SAR

-rw-r--r--  1 root   root          20 Jan 22 11:56 SAPEXE_23-80000699

-rw-r--r--  1 root   root   298932912 Jan 22 11:56 SAPEXE_23-80000699.SAR

-rw-r--r--  1 root   root           9 Jan 22 11:56 ccmagent_22-80000699

-rw-r--r--  1 root   root    23276616 Jan 22 11:56 ccmagent_22-80000699.sar

-rw-r--r--  1 root   root          18 Jan 22 12:00 dw_33-80000699

-rw-r--r--  1 root   root   157536215 Jan 22 12:00 dw_33-80000699.sar

-rw-r--r--  1 root   root          17 Jan 22 11:59 dw_utils_28-80000699

-rw-r--r--  1 root   root     6798429 Jan 22 11:59 dw_utils_28-80000699.sar

-rw-r--r--  1 root   root           9 Jan 22 11:56 enserver_33-80000699

-rw-r--r--  1 root   root     8585008 Jan 22 11:56 enserver_33-80000699.sar

-rw-r--r--  1 root   root           0 Jan 22 12:07 files.txt

-rw-r--r--  1 root   root          50 Jan 22 12:06 igsexe_10-20007790

-rw-r--r--  1 root   root    40456133 Jan 22 12:06 igsexe_10-20007790.sar

-rw-r--r--  1 root   root          27 Jan 22 12:06 igshelper_4-10010245

-rw-r--r--  1 root   root    62964419 Jan 22 12:06 igshelper_4-10010245.sar

-rw-r--r--  1 root   root          17 Jan 22 11:56 lib_dbsl_18-80000698

-rw-r--r--  1 root   root      729272 Jan 22 11:56 lib_dbsl_18-80000698.sar

-rw-r--r--  1 root   root           7 Jan 22 12:01 librfc_31-80000699

-rw-r--r--  1 root   root    10317409 Jan 22 12:01 librfc_31-80000699.sar

-rw-r--r--  1 root   root           8 Jan 22 11:59 saphttp_30-80000699

-rw-r--r--  1 root   root     2537721 Jan 22 11:59 saphttp_30-80000699.sar

-rw-r--r--  1 root   root           9 Jan 22 12:03 sapnwrfc_24-80000699

-rw-r--r--  1 root   root     3331542 Jan 22 12:03 sapnwrfc_24-80000699.sar

-rw-r--r--  1 root   root           9 Jan 22 12:03 startsap_27-80000699

-rw-r--r--  1 root   root       25529 Jan 22 12:03 startsap_27-80000699.sar

-rw-r--r--  1 root   root           3 Jan 22 11:59 tp_33-80000699

-rw-r--r--  1 root   root     3770458 Jan 22 11:59 tp_33-80000699.sar

Hierbei gibt es einmal dateien ohne .sar und einmal mit .sar am Ende. Die Dateien ohne Endung sind nutzlos, das sind nur Labels, die beschreiben, was in den DAteien drin ist. Eine bessere Liste ohne diese Dateien könnte ich mir beispielsweise mit dem Linux kommando find ausgeben lassen. Da es einmal mit Dateien mit großem .sar und einmal mit kleinem .SAR gibt, würde ich eintippen.

find <verzeichnis> -name '*.sar' -o -name '*.SAR'

Somit hätte ich dann, wenn ich es wollte, eine Liste ohne die Label-Dateien.

Als erstes lösche ich einfach über Notepad++ die überflüssigen Spalten wie biespielsweise die Linux RWX-Berehcitugngen, File Owner, Dateigröße und Timestamp. Dazu markiere ich einfach mit gedrückter Alt-Taste die Spalten und lösche sie. Dann lösche ich noch in der übrig gebleibeneen letzten Spalte die Zeilen für die files.txt selbst und die Einträge für das aktuelle Verzeichnis (.) sowie das übergeordnete (..). Meine files.txt sieht jetzt nur noch so aus:

R3check_22-80000699

R3check_22-80000699.SAR

R3ldctl_26-80000698

R3ldctl_26-80000698.SAR

R3load_23-80000698

R3load_23-80000698.SAR

R3szchk_19-80000698

R3szchk_19-80000698.SAR

R3ta_28-80000699

R3ta_28-80000699.SAR

R3trans_33-80000699

R3trans_33-80000699.SAR

SAPCRYPTOLIBP_8447-20011697

SAPCRYPTOLIBP_8447-20011697.SAR

SAPEXEDB_23-80000698

SAPEXEDB_23-80000698.SAR

SAPEXE_23-80000699

SAPEXE_23-80000699.SAR

ccmagent_22-80000699

ccmagent_22-80000699.sar

dw_33-80000699

dw_33-80000699.sar

dw_utils_28-80000699

dw_utils_28-80000699.sar

enserver_33-80000699

enserver_33-80000699.sar

igsexe_10-20007790

igsexe_10-20007790.sar

igshelper_4-10010245

igshelper_4-10010245.sar

lib_dbsl_18-80000698

lib_dbsl_18-80000698.sar

librfc_31-80000699

librfc_31-80000699.sar

saphttp_30-80000699

saphttp_30-80000699.sar

sapnwrfc_24-80000699

sapnwrfc_24-80000699.sar

startsap_27-80000699

startsap_27-80000699.sar

tp_33-80000699

tp_33-80000699.sar

So wird die Datei nun in einer tabellenkalkulationssoftware wie beispielsweise Microsoft Office Excel geöffnet. Ich möchte die Dateien nämlich nach dem Patchlevel, welches ich weiter oben an einigen Stellen fett markiert habe (nach dem Unterstrich), sortieren. Dazu muss ich das Patch Level der Dateien erstmal in eine extra Spalte bekommen. Ich gebe also beim ÖFfnen der Datei an, dass die Spalten durch einen Trenner getrennt sind

2016-01-22_13h13_13

und wähle dann bei Zeichen als erstes den Unterstrich

2016-01-22_13h14_12

Jetzt haben wir das Patchlevel in einer extra Spalte. Über die Schaltfläche sortieren kann ich die Dateien un nach PAtchlevel sortieren, somit stehen sie in der richtigen Reihenfolge.

2016-01-22_13h42_04

Würde ich jetzt noch wollen, dass das PAtchlevel auch noch von der Zahl hinter dem Bindestrich (-) getrennt wird, würde ich in der Registerkarte Daten nochmal in der Gruppe Datentools auf das Icon TExt in Spalten klicken und die Trennung nochmal mit dem Bindestrich durchführen, ist aber hier unnötig.

2016-01-22_14h06_33

Nun kopiere ich die beiden Spalten und füge sie wieder in ein leeres Dokument ein, welches ich mit dem Editor Notepad++ wieder bearbeite. Ich erhalte wieder die Dateinamen, jedoch ohne den Unterstrich.

2016-01-22_13h43_43

diesen muss ich jetzt wiederherstellen. Zwischen dem eigentlichen Dateinamen und dem PAtchlevel sind keine Leerzeichen, sondern Tabstopps. Diesen Tabstopp markiere ich einfach mit der Maus und kopiere ihn mir in die Zwischenablage. Dann gehe ich über das Menü von Notepad++ auf Search / Replace und füge bei Find what den Tabstopp ein und gebe bei REplace with den Unterstrich ein.

2016-01-22_13h44_59

Jetzt habe ich meine Dateinamen wieder sauber beisammen.

2016-01-22_13h45_30

Nun müssen vor und hinter die Dateinamen Textschnipsel, die später das Kommando ausmachen. Dazu halte ich die Alt-Taste gedrückt und markiere alle Zeilen des Dokuments, die ich bearbeiten möchte. Dabei markiere ich mit der MAus so, dass ich vor allen Zeilen einen Cursor habe.

2016-01-22_13h47_16

nun kann ich vor allen Dateinamen meinen Befehl eintippen. Danach setze ich mir mit gedrückter Alt-Taste einen Cursor hinter alle Dateinamen, wie im Bild angedeutet.

2016-01-22_13h48_52

Jetzt kann ich die abshcließenden PAramter der Kommandos hinter alle Dateinamen tippen.

2016-01-22_13h50_04

Und nun zu guter letzt kriegen wir mit Search and Replace die überflüssigen LEerzeichen weg, indem wirzwei Leerzeicehn mit nichts ersetzen lassen. Da die Anzahl der Leerzeicehn zwischen den DAteinamen und dem -R Parameter in vielen Fällen ungerade ist, sind dann die mesiten Zeilen schon richitg getrennt.

2016-01-22_13h52_01

2016-01-22_13h53_22

Jedoch, wie im SCreenshot ersichtlich, ein paar Zeilen gab es dann doch, wo die Anzahl der Leerezciehn zwischen Dateiname und -R gerade war und daher kein LEerzeichen mehr dazwischen ist. Also gehe ich im zweiten Schritt her und ersetze „.sar-R“ durch „.sar -R“. Damit passen dann die Leerzeichen wieder bei allen .sar-Dateien.

Unterschiedliche Versionen einer Datei vergleichen.

Folgende Herausforderung

Ich habe eine .TSK-Datei, welche Datenpakete entählt, die ein Interpretertool abarbeiten muss. DAs Interpretertool merkt sich über die .TSK-Datei, welche Datenpakete es noch abarbeiten muss und welche schon fertig sind. Die originale .TSK-Datei in dem Zustand, in welchem alle Datenpakete noch abgeabreitet werden müssen, sieht so aus (nur ein Ausschnitt von 100 Zeilen)

T /AIF/BDC_IF_CONF C xeq

D /AIF/BDC_IF_CONF I xeq

M /AIF/BDC_IF_CONF C xeq

P /AIF/BDC_IF_CONF~0 C xeq

U /AIF/BDC_IF_CONF C xeq

T /AIF/BDC_IF_GEN C xeq

D /AIF/BDC_IF_GEN I xeq

M /AIF/BDC_IF_GEN C xeq

P /AIF/BDC_IF_GEN~0 C xeq

U /AIF/BDC_IF_GEN C xeq

T /AIF/BDC_ST_CONF C xeq

D /AIF/BDC_ST_CONF I xeq

M /AIF/BDC_ST_CONF C xeq

P /AIF/BDC_ST_CONF~0 C xeq

U /AIF/BDC_ST_CONF C xeq

T /AIF/BDC_ST_GEN C xeq

D /AIF/BDC_ST_GEN I xeq

M /AIF/BDC_ST_GEN C xeq

P /AIF/BDC_ST_GEN~0 C xeq

U /AIF/BDC_ST_GEN C xeq

T /AIF/CIFPP_SS_AS C xeq

D /AIF/CIFPP_SS_AS I xeq

M /AIF/CIFPP_SS_AS C xeq

P /AIF/CIFPP_SS_AS~0 C xeq

U /AIF/CIFPP_SS_AS C xeq

T /AIF/CIFPP_TJSEL C xeq

D /AIF/CIFPP_TJSEL I xeq

M /AIF/CIFPP_TJSEL C xeq

P /AIF/CIFPP_TJSEL~0 C xeq

U /AIF/CIFPP_TJSEL C xeq

T /AIF/DOCU C xeq

D /AIF/DOCU I xeq

M /AIF/DOCU C xeq

P /AIF/DOCU~0 C xeq

U /AIF/DOCU C xeq

T /AIF/DOCUT C xeq

D /AIF/DOCUT I xeq

M /AIF/DOCUT C xeq

P /AIF/DOCUT~0 C xeq

U /AIF/DOCUT C xeq

T /AIF/DOCU_TAB C xeq

D /AIF/DOCU_TAB I xeq

M /AIF/DOCU_TAB C xeq

P /AIF/DOCU_TAB~0 C xeq

U /AIF/DOCU_TAB C xeq

T /AIF/DOCU_TABT C xeq

D /AIF/DOCU_TABT I xeq

M /AIF/DOCU_TABT C xeq

P /AIF/DOCU_TABT~0 C xeq

U /AIF/DOCU_TABT C xeq

T /AIF/ERR_APP C xeq

D /AIF/ERR_APP I xeq

M /AIF/ERR_APP C xeq

P /AIF/ERR_APP~0 C xeq

U /AIF/ERR_APP C xeq

T /AIF/ERR_INF C xeq

D /AIF/ERR_INF I xeq

M /AIF/ERR_INF C xeq

P /AIF/ERR_INF~0 C xeq

U /AIF/ERR_INF C xeq

T /AIF/LOGSYST C xeq

D /AIF/LOGSYST I xeq

M /AIF/LOGSYST C xeq

P /AIF/LOGSYST~0 C xeq

U /AIF/LOGSYST C xeq

T /AIF/NEWDEVPROJS C xeq

D /AIF/NEWDEVPROJS I xeq

M /AIF/NEWDEVPROJS C xeq

P /AIF/NEWDEVPROJS~0 C xeq

U /AIF/NEWDEVPROJS C xeq

T /AIF/NEWDEVPROJT C xeq

D /AIF/NEWDEVPROJT I xeq

M /AIF/NEWDEVPROJT C xeq

P /AIF/NEWDEVPROJT~0 C xeq

U /AIF/NEWDEVPROJT C xeq

T /AIF/NEWDEV_IDS C xeq

D /AIF/NEWDEV_IDS I xeq

M /AIF/NEWDEV_IDS C xeq

P /AIF/NEWDEV_IDS~0 C xeq

U /AIF/NEWDEV_IDS C xeq

T /AIF/NEWDEV_IDST C xeq

D /AIF/NEWDEV_IDST I xeq

M /AIF/NEWDEV_IDST C xeq

P /AIF/NEWDEV_IDST~0 C xeq

U /AIF/NEWDEV_IDST C xeq

T /AIF/PERS_RP_MSG C xeq

M /AIF/PERS_RP_MSG C xeq

P /AIF/PERS_RP_MSG~0 C xeq

U /AIF/PERS_RP_MSG C xeq

T /AIF/PERS_RTCFGR C xeq

D /AIF/PERS_RTCFGR I xeq

M /AIF/PERS_RTCFGR C xeq

P /AIF/PERS_RTCFGR~0 C xeq

U /AIF/PERS_RTCFGR C xeq

T /AIF/PERS_RTCFGT C xeq

D /AIF/PERS_RTCFGT I xeq

M /AIF/PERS_RTCFGT C xeq

P /AIF/PERS_RTCFGT~0 C xeq

U /AIF/PERS_RTCFGT C xeq

T /AIF/POC_C_CFGAD C xeq

der Eintrag ganz hinten, xeq, gibt an, dass die Datenpakete noch abgearbeitet werden müssen.

Nun sieht die Datei nach einer WEile vielleicht so aus:

T /AIF/BDC_IF_CONF C err

D /AIF/BDC_IF_CONF I err

M /AIF/BDC_IF_CONF C xeq

P /AIF/BDC_IF_CONF~0 C xeq

U /AIF/BDC_IF_CONF C xeq

T /AIF/BDC_IF_GEN C xeq

D /AIF/BDC_IF_GEN I err

M /AIF/BDC_IF_GEN C err

P /AIF/BDC_IF_GEN~0 C err

U /AIF/BDC_IF_GEN C err

T /AIF/BDC_ST_CONF C xeq

D /AIF/BDC_ST_CONF I xeq

M /AIF/BDC_ST_CONF C xeq

P /AIF/BDC_ST_CONF~0 C xeq

U /AIF/BDC_ST_CONF C err

T /AIF/BDC_ST_GEN C err

D /AIF/BDC_ST_GEN I err

M /AIF/BDC_ST_GEN C err

P /AIF/BDC_ST_GEN~0 C err

U /AIF/BDC_ST_GEN C xeq

T /AIF/CIFPP_SS_AS C xeq

D /AIF/CIFPP_SS_AS I xeq

M /AIF/CIFPP_SS_AS C xeq

P /AIF/CIFPP_SS_AS~0 C err

U /AIF/CIFPP_SS_AS C err

T /AIF/CIFPP_TJSEL C err

D /AIF/CIFPP_TJSEL I err

M /AIF/CIFPP_TJSEL C err

P /AIF/CIFPP_TJSEL~0 C err

U /AIF/CIFPP_TJSEL C xeq

T /AIF/DOCU C xeq

D /AIF/DOCU I xeq

M /AIF/DOCU C xeq

P /AIF/DOCU~0 C xeq

U /AIF/DOCU C xeq

T /AIF/DOCUT C err

D /AIF/DOCUT I err

M /AIF/DOCUT C err

P /AIF/DOCUT~0 C err

U /AIF/DOCUT C err

T /AIF/DOCU_TAB C err

D /AIF/DOCU_TAB I err

M /AIF/DOCU_TAB C err

P /AIF/DOCU_TAB~0 C xeq

U /AIF/DOCU_TAB C xeq

T /AIF/DOCU_TABT C xeq

D /AIF/DOCU_TABT I xeq

M /AIF/DOCU_TABT C xeq

P /AIF/DOCU_TABT~0 C xeq

U /AIF/DOCU_TABT C err

T /AIF/ERR_APP C err

D /AIF/ERR_APP I err

M /AIF/ERR_APP C err

P /AIF/ERR_APP~0 C err

U /AIF/ERR_APP C err

T /AIF/ERR_INF C err

D /AIF/ERR_INF I xeq

M /AIF/ERR_INF C xeq

P /AIF/ERR_INF~0 C xeq

U /AIF/ERR_INF C xeq

T /AIF/LOGSYST C xeq

D /AIF/LOGSYST I err

M /AIF/LOGSYST C err

P /AIF/LOGSYST~0 C err

U /AIF/LOGSYST C err

T /AIF/NEWDEVPROJS C err

D /AIF/NEWDEVPROJS I err

M /AIF/NEWDEVPROJS C err

P /AIF/NEWDEVPROJS~0 C err

U /AIF/NEWDEVPROJS C xeq

T /AIF/NEWDEVPROJT C xeq

D /AIF/NEWDEVPROJT I xeq

M /AIF/NEWDEVPROJT C xeq

P /AIF/NEWDEVPROJT~0 C xeq

U /AIF/NEWDEVPROJT C xeq

T /AIF/NEWDEV_IDS C err

D /AIF/NEWDEV_IDS I err

M /AIF/NEWDEV_IDS C err

P /AIF/NEWDEV_IDS~0 C err

U /AIF/NEWDEV_IDS C ok

T /AIF/NEWDEV_IDST C ok

D /AIF/NEWDEV_IDST I ok

M /AIF/NEWDEV_IDST C ok

P /AIF/NEWDEV_IDST~0 C ok

U /AIF/NEWDEV_IDST C err

T /AIF/PERS_RP_MSG C err

M /AIF/PERS_RP_MSG C xeq

P /AIF/PERS_RP_MSG~0 C xeq

U /AIF/PERS_RP_MSG C ok

T /AIF/PERS_RTCFGR C ok

D /AIF/PERS_RTCFGR I ok

M /AIF/PERS_RTCFGR C ok

P /AIF/PERS_RTCFGR~0 C ok

U /AIF/PERS_RTCFGR C xeq

T /AIF/PERS_RTCFGT C xeq

D /AIF/PERS_RTCFGT I xeq

M /AIF/PERS_RTCFGT C xeq

P /AIF/PERS_RTCFGT~0 C ok

U /AIF/PERS_RTCFGT C ok

T /AIF/POC_C_CFGAD C ok

Status ok beduetet, dass das PAket feritg abgeabreitet wurde, STatus err beduetet, dass das Paket fehlerhaft bearbeitet wurde und daher ebenfalls nochmal abgearbeitet werden muss, jedoch anders als bei xeq.

Ich möchte jetzt zwei SAchen, ich möchte zum einen eine Übersicht haben, welche pakete noch nicht sauber durchgearbeitet wurden, und zum anderen möchte ich eine Datei mit allen Pakleten, die Status xeq oder err haben.

In dem FAll ist es sehr einfach: alle Einträge sind gleich sortiert und in beiden Dateien sind immer alle Einträge vorhanden. Mit einem Tool wie winmerge lade ich die beiden Dateien und lasse sie vergleichen. Die Zeilen, die sich untershcieden, werden mir einfach angezeigt.  So sehe ich in einer Übersicht, welche Pakete noch abgearbeitet werden müssen. Links ist die Liste mit den err und ok statuscodes, rechts ist die Originaldatei.

2016-01-22_14h34_50

2016-01-22_14h36_03

Wenn ich jetzt eine Datei will, wo ur noch alle Pakete mit xeq und err drin sind, habe ich verschiedene Möglichkeiten.

Anders sieht es aus,w enn man die REihenfolge der Dateien änert. Hierzu habe ich einfach mal testweise die REihenfolge der modifzierten Datei geändert, indem ich eine STelle ausgeschnitten und am Ende der DAtei eingefügt habe

T /AIF/BDC_IF_CONF C err

D /AIF/BDC_IF_CONF I err

M /AIF/BDC_IF_CONF C xeq

P /AIF/BDC_IF_CONF~0 C xeq

U /AIF/BDC_IF_CONF C xeq

T /AIF/BDC_IF_GEN C xeq

D /AIF/BDC_IF_GEN I err

M /AIF/BDC_IF_GEN C err

P /AIF/BDC_IF_GEN~0 C err

U /AIF/BDC_IF_GEN C err

T /AIF/BDC_ST_CONF C xeq

D /AIF/BDC_ST_CONF I xeq



P /AIF/DOCUT~0 C err

U /AIF/DOCUT C err

T /AIF/DOCU_TAB C err

D /AIF/DOCU_TAB I err

M /AIF/DOCU_TAB C err

P /AIF/DOCU_TAB~0 C xeq

U /AIF/DOCU_TAB C xeq

T /AIF/DOCU_TABT C xeq

D /AIF/DOCU_TABT I xeq

M /AIF/DOCU_TABT C xeq

P /AIF/DOCU_TABT~0 C xeq

U /AIF/DOCU_TABT C err

T /AIF/ERR_APP C err

D /AIF/ERR_APP I err

M /AIF/ERR_APP C err

P /AIF/ERR_APP~0 C err

U /AIF/ERR_APP C err

T /AIF/ERR_INF C err

D /AIF/ERR_INF I xeq

M /AIF/ERR_INF C xeq

P /AIF/ERR_INF~0 C xeq

U /AIF/ERR_INF C xeq

T /AIF/LOGSYST C xeq

D /AIF/LOGSYST I err

M /AIF/LOGSYST C err

P /AIF/LOGSYST~0 C err

U /AIF/LOGSYST C err

T /AIF/NEWDEVPROJS C err

D /AIF/NEWDEVPROJS I err

M /AIF/NEWDEVPROJS C err

P /AIF/NEWDEVPROJS~0 C err

U /AIF/NEWDEVPROJS C xeq

T /AIF/NEWDEVPROJT C xeq

D /AIF/NEWDEVPROJT I xeq

M /AIF/NEWDEVPROJT C xeq

P /AIF/NEWDEVPROJT~0 C xeq

U /AIF/NEWDEVPROJT C xeq

T /AIF/NEWDEV_IDS C err

D /AIF/NEWDEV_IDS I err

M /AIF/NEWDEV_IDS C err

P /AIF/NEWDEV_IDS~0 C err

U /AIF/NEWDEV_IDS C ok

T /AIF/NEWDEV_IDST C ok

D /AIF/NEWDEV_IDST I ok

M /AIF/NEWDEV_IDST C ok

P /AIF/NEWDEV_IDST~0 C ok

U /AIF/NEWDEV_IDST C err

T /AIF/PERS_RP_MSG C err

M /AIF/PERS_RP_MSG C xeq

P /AIF/PERS_RP_MSG~0 C xeq

U /AIF/PERS_RP_MSG C ok

T /AIF/PERS_RTCFGR C ok

D /AIF/PERS_RTCFGR I ok

M /AIF/PERS_RTCFGR C ok

P /AIF/PERS_RTCFGR~0 C ok

U /AIF/PERS_RTCFGR C xeq

T /AIF/PERS_RTCFGT C xeq

D /AIF/PERS_RTCFGT I xeq

M /AIF/PERS_RTCFGT C xeq

P /AIF/PERS_RTCFGT~0 C ok

U /AIF/PERS_RTCFGT C ok

T /AIF/POC_C_CFGAD C ok



M /AIF/BDC_ST_CONF C xeq

P /AIF/BDC_ST_CONF~0 C xeq

U /AIF/BDC_ST_CONF C err

T /AIF/BDC_ST_GEN C err

D /AIF/BDC_ST_GEN I err

M /AIF/BDC_ST_GEN C err

P /AIF/BDC_ST_GEN~0 C err

U /AIF/BDC_ST_GEN C xeq

T /AIF/CIFPP_SS_AS C xeq

D /AIF/CIFPP_SS_AS I xeq

M /AIF/CIFPP_SS_AS C xeq

P /AIF/CIFPP_SS_AS~0 C err

U /AIF/CIFPP_SS_AS C err

T /AIF/CIFPP_TJSEL C err

D /AIF/CIFPP_TJSEL I err

M /AIF/CIFPP_TJSEL C err

P /AIF/CIFPP_TJSEL~0 C err

U /AIF/CIFPP_TJSEL C xeq

T /AIF/DOCU C xeq

D /AIF/DOCU I xeq

M /AIF/DOCU C xeq

P /AIF/DOCU~0 C xeq

U /AIF/DOCU C xeq

T /AIF/DOCUT C err

D /AIF/DOCUT I err

M /AIF/DOCUT C err

WinMerge erkennt dann zumindest die Zeilen, die keine Entsprechung zueinander haben. DAs heißt in der einen DAtei wird der Bereich als fehlend (grau) und in der anderen dann jeweils als vorhanden (gelb) markiert. Das manuell mit logischem Denken zu verknüpfen funktioniert bei so einem einfachen Beispiel, wo ich nur eine Abschnitt ausgeshcnitten und am Ende eingefügt habe, noch.

2016-01-23_00h13_20

komplizierter wird es dann schon, wenn in einer der beiden Dateien bestimmte Teile fehlen, also wenn beispielsweise eineige Pakete, die den STatus xeq haben und noch abgearbeitet werden müssen, es nicht in die zweite Datei schaffen, aus welchem Grund auch immer. Acuh diese Sektionen markiert dann zwar WinMErge als grau, und somit lässt sich wieder feststellen, welche Berieche fehlen, aber der Endpunkt ist spätestens dann ereicht, wenn einige Einträge in der zweiten DAtei dann auch och doppelt vorkommen.

T /AIF/BDC_IF_CONF C err

D /AIF/BDC_IF_CONF I err

M /AIF/BDC_IF_CONF C xeq

P /AIF/BDC_IF_CONF~0 C xeq

U /AIF/BDC_IF_CONF C xeq

T /AIF/BDC_IF_GEN C xeq

D /AIF/BDC_IF_GEN I err

M /AIF/BDC_IF_GEN C err

P /AIF/BDC_IF_GEN~0 C err

U /AIF/BDC_IF_GEN C err

T /AIF/BDC_ST_CONF C xeq

D /AIF/BDC_ST_CONF I xeq



P /AIF/DOCUT~0 C err

U /AIF/DOCUT C err

T /AIF/DOCU_TAB C err

D /AIF/DOCU_TAB I err

M /AIF/DOCU_TAB C err

P /AIF/DOCU_TAB~0 C xeq

P /AIF/DOCU_TAB~0 C xeq

U /AIF/DOCU_TAB C xeq

T /AIF/DOCU_TABT C xeq

D /AIF/DOCU_TABT I xeq

M /AIF/DOCU_TABT C xeq

P /AIF/DOCU_TABT~0 C xeq

U /AIF/DOCU_TABT C err

T /AIF/ERR_APP C err

D /AIF/ERR_APP I err

M /AIF/ERR_APP C err

P /AIF/ERR_APP~0 C err

U /AIF/ERR_APP C err

T /AIF/ERR_INF C err

D /AIF/ERR_INF I xeq

M /AIF/ERR_INF C xeq

P /AIF/ERR_INF~0 C xeq

U /AIF/ERR_INF C xeq

T /AIF/LOGSYST C xeq

D /AIF/LOGSYST I err

M /AIF/LOGSYST C err

P /AIF/LOGSYST~0 C err

U /AIF/LOGSYST C err

T /AIF/NEWDEVPROJS C err

D /AIF/NEWDEVPROJS I err

M /AIF/NEWDEVPROJS C err

P /AIF/NEWDEVPROJS~0 C err

U /AIF/NEWDEVPROJS C xeq

T /AIF/NEWDEVPROJT C xeq

P /AIF/DOCU_TAB~0 C xeq

D /AIF/NEWDEVPROJT I xeq

M /AIF/NEWDEVPROJT C xeq

P /AIF/NEWDEVPROJT~0 C xeq

U /AIF/NEWDEVPROJT C xeq

T /AIF/NEWDEV_IDS C err

D /AIF/NEWDEV_IDS I err

M /AIF/NEWDEV_IDS C err

P /AIF/NEWDEV_IDS~0 C err

U /AIF/NEWDEV_IDS C ok

T /AIF/NEWDEV_IDST C ok

D /AIF/NEWDEV_IDST I ok

M /AIF/NEWDEV_IDST C ok

P /AIF/NEWDEV_IDST~0 C ok

P /AIF/DOCU_TAB~0 C xeq

U /AIF/NEWDEV_IDST C err

T /AIF/PERS_RP_MSG C err

M /AIF/PERS_RP_MSG C xeq

P /AIF/PERS_RP_MSG~0 C xeq

U /AIF/PERS_RP_MSG C ok

T /AIF/PERS_RTCFGR C ok

D /AIF/PERS_RTCFGR I ok

M /AIF/PERS_RTCFGR C ok

P /AIF/PERS_RTCFGR~0 C ok

U /AIF/PERS_RTCFGR C xeq

T /AIF/PERS_RTCFGT C xeq

D /AIF/PERS_RTCFGT I xeq

M /AIF/PERS_RTCFGT C xeq

P /AIF/PERS_RTCFGT~0 C ok

U /AIF/PERS_RTCFGT C ok

T /AIF/POC_C_CFGAD C ok



M /AIF/BDC_ST_CONF C xeq

P /AIF/BDC_ST_CONF~0 C xeq

U /AIF/BDC_ST_CONF C err

T /AIF/BDC_ST_GEN C err

D /AIF/BDC_ST_GEN I err

P /AIF/DOCU_TAB~0 C xeq

M /AIF/BDC_ST_GEN C err

P /AIF/BDC_ST_GEN~0 C err

U /AIF/BDC_ST_GEN C xeq

T /AIF/CIFPP_SS_AS C xeq

D /AIF/CIFPP_SS_AS I xeq

M /AIF/CIFPP_SS_AS C xeq

P /AIF/CIFPP_SS_AS~0 C err

U /AIF/CIFPP_SS_AS C err

P /AIF/DOCU_TAB~0 C xeq

T /AIF/CIFPP_TJSEL C err

D /AIF/CIFPP_TJSEL I err

M /AIF/CIFPP_TJSEL C err

P /AIF/CIFPP_TJSEL~0 C err

U /AIF/CIFPP_TJSEL C xeq

T /AIF/DOCU C xeq

D /AIF/DOCU I xeq

M /AIF/DOCU C xeq

P /AIF/DOCU~0 C xeq

U /AIF/DOCU C xeq

T /AIF/DOCUT C err

D /AIF/DOCUT I err

M /AIF/DOCUT C err

Der Screenshot zeigt die Textdatei mit veränderter Reihenfolge und doppelten Einträgen. Zwar zeigt WinMErge hier mit grauen STellen an, dass an der gezeigten Stelle die Einträge auseinander gehen, liefert jedoch keine Aussage darüber, ob der Eintrag an anderer Stelle nochmal auftaucht oder sogar doppelt vorhanden ist. Diese informationen manuell herauszusuchen wäre ab einer bestimmten Komplexität nicht mehr zumutbar.

2016-01-23_00h13_20 2016-01-23_00h21_42

WAs muss man also machen? Nun wir müssen eine Methode finden, die Einträge in der Datei wieder so zu sortieren, dass sie wieder in der selben Reihenfolge dargestellt werden, und dann gegenüberstellen, ob ein Eintrag auf der einen Seite mindestens eine  Entsprechung auf der anderen Seite hat (oder mehrere bei doppelten Einträgen).

Eine Möglichkeit ist es, die beiden Dateien in Microsoft Excel einzufügen und wie im Projekt weiter oben beschrieben über ein Trennzeichen (in unserem Fall sind das Leerzeichen) in Spalten aufzuteilen.

2016-01-22_15h36_03

2016-01-23_00h25_04

Das ganze machen wir zweimal, also für beide Dateien, so dass die ersten vier Spateln A-D von der linken Datei und die Spalten G-J von der rechtne Datei eingenommen werden.

2016-01-23_00h26_19

Jetzt markieren wir die zusammengehörigen Saptlen der ersten Datei und wählen im Kontextmenü erstmal Zellen einfügen und dann Ganze Zeile. So schieben wir die inhatle um eine Zeile nach unten, damit wir eine Überschrit einfügen können, um ausienander halten zukönnen, welche Datei welche ist.

2016-01-23_00h27_11

2016-01-23_00h27_572016-01-23_00h29_06

Paketnamen, die nun in einer Datei doppelt vorkommen, können wir nun entfernen, in dem wir in der Registerkarte Daten das Icon Duplikate entfernen wählen.

2016-01-23_00h47_33

Dor twählen wir dann die Saptlen, die zu einer Datei gehören, also beispielsweise A-D, und dann die Spalte, anhand  derer Dupliakte erkannt und entfernt werden sollen, also z. B. B. Bie der anderen Datei amchen wir dann das gleiche. Wir wählen die Saptlen G-J und wählen als Erkennungsmerkaml die Spalte H aus.

Jetzt wollen wir noch die Reihenfolge der Dateien wieder in den Griff kriegen. DAzu klciekn wir links die vier Saptlen der ersten Datei n und klicekn dann in der registerkarte Start auf den Button Sortierne und Filtern – Benutzerdefinierte Sortierung und dann Sortieren nach: Spalte B. Ihr wedet merken, dass die benachbarten Spalten mitgezogen werden, die rechte Ausgangsdatei aber nicht gleich mitsortiert wird, weil wir zwei Saptlen abstand gelassen haben, so soll es sein.

2016-01-23_00h42_49

Diesen Vorgang wiederholen wir dann auch für die zweite Datei und sortieren auch diese. Nun sind alle Paketnamen in beiden Dateien in der selben Reihenfolge sortiert, egal wo sie ursprünglich in der Datei gestanden sind.

2016-01-23_00h34_30

Jetzt können wir ganz einfach über eine Wenn-Formel feststellen, ob eine Zeile in beiden Dateien vorhanden ist, oder nicht. Die Formel wäre beispielsweise

=WENN(B1=H1;"gleich";"fehlt")

Als Ergebnsi in der ersten Zeile erahtlen wir „gleich“. Durch einen Doppeklick auf das kleine Vierekc in der rechten unteren Ecke machen wir den Vergleich für alle Zeilen. Somit kriegt man gnaz einfach raus, welche Zeilen gleich sind und welche nicht.

2016-01-23_00h40_06

Eine ander eMöglichkeit wäre es, die Spalten B und H zu markieren und dann im Reiter Start – Bedingte Formatierung – Reglen zum Hervorheben von Zellen – Doppelte Werte zu wählen. Somit wird gnaz einfach über eine Farbkodierung gezeigt, welche Einträge doppelt vorhanden sind und welche nicht – das funktioniert dann sogar ohne Sortierung, das heißt das Sortieren der Spalten hätten wir uns in diesem Schritt sparen können. Der Screenshot zeigt, dass die bedingte Formatierung den einen Wert zu wenig, den ich in der Ausgangsdatei gelassen und in der mit err/ok-belasteten Datei gelöscht habe, erkannt hat.

2016-01-23_00h56_59

Texteditor-Makros aufzeichnen mit Notepad++

Manche Textaufgaben sind einfach zu kompliziert, um sie mit KOmmandozeilentools oder manuell über einen TExteditor auszuführen. Für solche Afugaben gibt es Makros, die man mit einem TExteditor wie Notepad++ aufzeichnen kann. Ich spare mir hier umschweifende Beschreibungen und füge euch ein Youtube-Video ein, welches das Aufzeichnen und Verwenden eines Makros erklärt.

 

Wenn dir dieser Post gefallen hat, teile ihn doch auf Social Media, abonniere und verfolge mich per E-Mail, RSS-Feed, Social Media oder registriere dich auf meiner Seite. Wenn du registriert bist, like den Post bitte, wenn er dir gefallen hat!

]]>
http://dafrk-blog.com/?feed=rss2&p=4116 0
Reguläre Ausdrücke auf Kommandozeilen und in Programmiersprachen http://dafrk-blog.com/?p=4153 http://dafrk-blog.com/?p=4153#comments Sat, 23 Jan 2016 18:13:25 +0000 http://dafrk-blog.com/?p=4153 Reguläre Ausdrücke auf Kommandozeilen und in Programmiersprachen weiterlesen →]]> Reguläre Ausdrücke (regular ExpressionS) sind eigentlich eine gute Erfindung. Denn: Sie erlauben das Suchen nach oder Ersetzen von  Zeichenketten abhängig von bestimmten TExtmustern. So könnten Sie beispielsweise in einer Textdatei ganz einfach nach einem GEburtsdatum suchen, indem Sie nach einer Zeichenkette aus zwei Zahlen, einem Punkt, weiteren zwei Zahlen, einem Punkt und abschließend 2 oder auch vier Zahlen suchen. So ein Suchmuster ist nur dank regulärer Ausdrücke möglich. REguläre Ausdrücke sind eine echte PAin in the Ass. Denn: Sie sind nicht überall gleich. Je nachdem, ob Sie reguläre Ausdrücke in dieser (z. B. JavaScript) oder jener (z. B. Perl) Skript- bzw. Programmiersprache nutzen wollen oder auf der Linux-Shell oder Windows Power Shell arbeiten, sind sie immer etwas unterschiedlich. Denn die Implementierung regulärer Ausdrücke ist nicht genormt, die Implementierung hängt also einzig allein vom Bereitsteller der Funktionalität ab.

Dieser Post, der regelmäßig geupdatet wird, soll sammeln, wie man reguläre Ausdrücke auf verschiedenen Kommandozeilen, Skript- und Programmiersprachen verwendet.

Aktuell beinahltet der Post nur Grundlagen, die überall gelten, und spezifische Hinweise für die Linux Shell. Mit der Ziet werde ich noch Hinweise für die Windows PowerShell, Windows cmd und für verschiedene Programmiersprachen hinzufügen.

Reguläre Ausdrücke – Grundlagen

die folgenden Grundlagen funktionieren (Gott sei DAnk) in allen Implementierungen von regulären Ausdrücken. So konnte sich zumindest teilweise eine kleine STandardisierung von regulären Ausdrücken durchsetzen.

^ Zeichenketten beginnen mit

Mit dem Zirkumflex können Sie überall nach Zeichenketten suchen, mit einem bestimmten String beginnen sollen. Sie können beispielsweise sagen, ich möchte in einer datei alle Zeilen haben, die mit einer Raute (#) beginnen. Unter Linux würden Sie dazu das  komamndo grep verwenden

grep ^# <datei>

einige Anwendender grübeln immer bei der Frage: „was ist, wenn ich nach einer Zeichenkette suchen möchte, die mit einem Leerzeichen afange?“. DAfür liefern reguläre Ausdrücke entsprehcende Platzhalter, beispielsweise können Sie das folgendermaßen abfragen.

egrep '^\s' <datei>

Das ganze können Sie noch weiter stricken. Was ist denn jeztt, wenn wir wollen, dass sowohl Zeilen, die mit einem Leerzeicehn beginnen, als auch Zeilen, die mit einer Raute beginnen, ausgegeben werden? Nun, dafür bieten reguläre Ausdrücke den Oder-Operator |, sodass wir folgendermaßen arbeiten könnten.

egrep '^\s|^#' <datei>

Leere Zeilen, die nur einen Carriage Return (also einen Zeilenumbruch) enthalten, findet man mit

grep ^$ <datei>

das Dollar steht hier entsprechend stellvertretend für einen Zeilenumbruch.In der Praxis möchte man jedoch meist genua das Gegenteil, also alle Zeilen außer die LEerzeilen finden, daher verwendet amn in der Praxis häufig

grep -v ^$ <datei>

Sie können auch andere sogenannte non-printing characters auf diese Art und Weise finden. Wenn Sie erst wissen müssen, welches Ziechen für den von Ihnen gesuchten non-printing-character steht, können Sie diesen rausfinden über

cat -vet <datei>

Der non printing character wird dann mit dem Zeichen dargestellt, mit dem er in regular Expressions vertreten wird.

^ außerhalb von [] ist nicht das selbe wie innerhalb von [^]

während ein Zirkumflex außerhalb von eckigen Klammern bedeutet, „suche nach Zeichenketten die mit damit beginnen: “ sagt ein Zirkumflex innerhlab von eckigen klammern „Suche nach allem außer dem, was in den eckigen Klammern steht“. [^a-z] sucht also nach allem, was keine Kleinbuchstaben von a-z hat.

$ Zeilen enden mit

Jetzt da wir wissen, dass das Dollar-Zeichen für einen Zeilenumbruch steht, können wir mit

grep .$ <datei>

sagen, wir wollen alles auswählen, was mit einem Punkt aufhört, wenn danach eine neue Zeile beginnt. Wir können also nach Zeilenenden suchen.

Zeichenketten mit einem einzigen Zeichen einer bestimmten Art an einer bestimmten STelle

Was ist, wenn ich aus einer Datei nun alle Zeilen mit Worten mit c<irgendein Buchstbae>t haben möchte, also z. B. cat, cUt, ctt, cOt usw.?

Hierfür gibt es den Punkt. Unter Linux könntet ihr beispielswiese nach einer derartigen Zeichenkette wieder suchen mit

grep "c.t" <datei>

Was nun aber, wenn ihr beispielsweise wollt, dass zwischen c und t nur Vokale stehen dürfen? Damit würden wir noch Zeichenketten mit cat und cut, aber keine mehr mit ctt, cdt o. Ä. erwischen. In disem Fall könnt ihr eine sogenannte Range an Zeichen, die in die Zeichenkette passen dürfen, angeben.

grep "c[aeiou]t" <datei>

Wenn ihr hingeen wollt, dass alle Buchstaben von A-P an der STelle sein dürfen, womit Zeichenketten wie cat, cpt, cot, ckt, cbt etc. ausgewählt werden würden, könntet ihr angeben:

grep "c[a-p]t" <datei>

Im Moment wählen wir aber nur  Kleinbuchstbaen aus. wEnn wir auch Großbuchstaben auswählen wollen, müssen wir mit dem Oder-Operator (|) arbeiten und sagen

grep "c[a-pA-P]|" <datei>

Damit werden nun auch die Großbuchstaben A-P ausgewählt.

Was ist nun, wenn ihr nach Ziffern in einer Datei suchen wollt? In diesem Fall würden wir sagen

grep "[0-9]" <datei>

Und wenn es statt einstelligen Ziffern nur dreistellige Zahlen sein dürfen (100, 101, 199, 200, 999 etc.), dann schreiben wir

grep "[0-9][0-9][0-9]" <datei>

Statt solcher Ranges kann man auch Klassen angeben

  • [[:alpha:]] ist das selbe wie [a-zA-Z]
  • [[:upper:]] ist das selbe wie [A-Z]
  • [[:lower:]] ist das selbe wie [a-z]
  • [[:digit:]] ist das slebe wie [0-9]
  • [[:alnum:]] ist das selbe wie [0-9a-zA-Z]
  • [[:space:]] wählt alle White Spaces, also bspw. Leerzeichen oder Tabstopps, aus.
  • [[:punct:]] wählt alle Sonderzeichen aus, also !"#$%&'()*+,
    \-./:;<=>?@
    [\\\]^_`{|}~]
  • weitere Klasen findet ihr unter http://www.regular-expressions.info/posixbrackets.html

und statt dieser Klassen kann man man auch Abkürzungen schreiben wie beispielswiese

  • \d  ist das selbe wie [0-9]
  • \w ist das selbe wie [0-9a-zA-Z_] (hier ist also der Unterstrich dabei)
  • \s wählt nicht nur Whitespaces (Leerzeciehn und Tabstopps), sondern auch neue Zeilenumbrüche (\n), Zeilenrücksprungmarkierungen (carriage return,\n) und Seitenumbrüche (\f) aus. Achtung: Das ist NICHT das selbe wie [[:space:]]. [[:space:]] kümmert sich NUR und ausschließlich um Whitespaces, also Leerstellen und Tabstopps, \s kümmert sich AUSSERDEM um non-printing-characters, also um Zeichen, die man bei der Ausgbae des Dokuments nicht sieht.

Wenn diese Abkürzungen (Abbreviations) groß geschrieben werden, sucht man nach Zeichenketten, die nicht diese Zeichenketten enthalten.. /D sucht also nach allen Zeichenketten, die keine Ziffer enthalten. [^0-9]

Quantifiers – nicht zu verwechseln mit Wildcards

Natürlich können Sie nach dreistelligen Zahlen statt mit

[0-9][0-9][0-9]

beispielsweise auch suchen über

[0-9]{3}

Dieses Konzept nennt man quantifizieren, also ein Suchmuster muss mehrfals vorkommen. Diese Quantifizierer kombiniert man am besten entweder mit Ranges, z. B.

[a-zA-z]{10}

Jedoch gibt es quhc Auntifizierer, die ohne Ranges auskommen, beispielswiese

  • ^A+ sucht nach allen Zeichenketten, die mindestens ein großes A, aber auch mehrere davon beinahlten.
  • A? sucht nach Zeichenketten, die entweder gar kein oder wenn dann nur ein einziges großes A enthalten.
  • A* sucht nach Zeichenketten, die entweder gar kein oder beliebig viele große A benhalten.

damit sollte auch klar sein, dass A* oder A? in Regulären Ausdrücken was anderes bedueutet als man es von Wildcards gewohnt ist, die man beispielsweise an anderen Stellen in der Linux Shell oder der Windows Eingabeaufforderung verwenden kann. Denn in der Linux Shell oder in der windows Eingabeaufforderung würde A* bedeueten:

„Suche nach allen Dateien, die nach einem großen A entweder gar nichts mehr oder beliebig viele Zeichen haben“

Während also A* bei regulären Ausdrücken neben einer Datei  bzw. Zeichenkette namens A auch eine Datei oder Zeichenkette namens B oder C auswählen würde, würde man bei der Windows eingabeaufforderung damit neben A nicht mehr B oder C, sondern nur noch AA,  AB, AC usw. auswählen, da diese den Asterisk (*) als Wildcard interpretiert.

A? wählt bei regulären Ausdrücken ebenfalls A, B und C aus, während die Linux Shell oder Windows Eingabeaufforderung nur A, AA, AB, usw. auswählt. Diesen Unterschied zu kennen ist essentiell, denn die Linux Binary grep versteht ein A* im Sinne des regulären Ausdrucks, während beispielsweise das Kommando ls -la A* im Sinne einer Wildcard interpretiert. Diese Feinheit bringt viele administratoren zur täglichen Verzweiflung, wenn Sie versuchen, im Linux-Kommando grep den Stern (*) und das Fragezeichen (?) wie eine Wildcard zu verwenden.

Regular Expressions in der Linux Shell

Welche reguläre Ausdrücke unterstützt werden, ist in der Linux Shell leider immer sehr abhängig von der binary, die Sie verwenden wollen. Beispielswiese unterstützt das kommando grep keinen Oder-Operator, sodass Sie bei einem regulären Ausdruck wie beispielsweise

grep '^\s|^#' <datei>

ins Leere schauen würden. Verwenden Sie hingegen stattdessen egrep, funktioniert es wieder. Das führt leider bei vielen unerfahrenen Linux-Usern häufig zu Frustration.

Wie bereits wieter oben bei den Grundlagen im Kapitel Quantifiers erwähnt, gibt es Binaries, die einen Ausdruck wie A* als Quantifizierer im Sinne eines regulären Ausdrucks verstehen (z. B: egrep), und andere Binarys diesen als Wildcard interpretieren (z. B. ls).

Wenn dir dieser Post gefallen hat, teile ihn doch auf Social Media, abonniere und verfolge mich per E-Mail, RSS-Feed, Social Media oder registriere dich auf meiner Seite. Wenn du registriert bist, like den Post bitte, wenn er dir gefallen hat!

]]>
http://dafrk-blog.com/?feed=rss2&p=4153 1
Funk- und USB-Steckdosen Fernsteuern mit dem Raspberry Pi http://dafrk-blog.com/?p=2577 http://dafrk-blog.com/?p=2577#respond Mon, 04 Jan 2016 14:33:00 +0000 http://dafrk-blog.com/?p=2577 Funk- und USB-Steckdosen Fernsteuern mit dem Raspberry Pi weiterlesen →]]> die Hardware

Sie brauchen

 

die USB-STeckdosenleiste fernsteuern

Als erstes lernen wir, wie wir die Gembird (m)SiS-PM STeckdosenleste unter Linux steuern. Dazu installieren wir auf unserem Raspberry Pi erstmal dfie Bibliothek libusb-dev

sudo apt-get install libusb-dev

Danach können wir das Tool sispmctl runterladen und kompilieren, welches für das eigentliche Steuern der Steckdose notwendig ist. Den atkeullen Dwonload-Link bekomt ihr unter http://sispmctl.sourceforge.net/.

tar xzvf sispm_ctl--3.1..tar.gz

cd sispm_ctl-*

 ./configure

make

sudo make install
Prüfen, ob die Software sauber installiert wurde.
which sispmctl
Daraufhin muss der Pfad zur sispmctl-binary angezeigt werden. Ist das der Fall, wurde das tool sauber installiert. Jetzt steckt ihr die USB-Steckdosenleiste per USB an den RAspberry Pi. und in eine Steckdose. LEgt nun den Schalter an der Steckdosenleiste um, so dass sie eingeschaltet ist. Es leuchten nun an vier der Steckdosen LEDs die anzeigen, dass alle vier dieser Steckdosen eingeschaltet sind. Die beiden anderen STeckdosen in de rLeiste lassen sich nicht manuell ein und ausschalten, sondenr sind immer an.
Nun wollen wir mal die Steckdose mit der Nummer 1 ausschalten. Das geht mit
sudo sispmctl -f 1
Die LED bei der Steckdose mt der Nummer 1 müsste jetzt soeben ausgegangen sein.
Nun wollen wir die Steckdose mit der Nummer 1 wieder anschalten, das geht mit
sudo sispmctl -o 1
Nun wisst ihr, wie ihr mit der Kommandozeile  die STeckdosen aus- und einschaltet. Als Zusatz gibt es noch ein Webinterface – d. h., ihr könnt die einzelnen STeckdosen der Leiste auch über den Browser abschalten. dAzu schaltet ihr das Webinterface mittels.
sudo sispmctl -i 0.0.0.0 -p 8989 -l
Nun könnt ihr auf eurem Browser das Webinterface starten über
http://<ip oder hostname raspberry pi>:8989
Nun zu guter letzt zeige ich euch noch ein Beispiel, wie ihr einzelne Steckdosen der Leiste zeitgesteuert ausschalten lassen könnt. Dazu müsst ihr euch eine .sh-Datei erstellen, beispielsweise unter /home/pi/steckdose_aus.sh. Diese kann beispielsweise den Inhalt haben
sudo sispmctl -f 1
Das heißt, diese Skriptdatei wird später die erste Steckdose der Leiste ausschalten.
Nun editiert ihr mit dem Editor vi die Datei /etc/crontab. Nehmen wir an ich möchte, dass jeden Tag um 01:00 Uhr morgens die Steckdose 1 der Leiste automatisch abgeschalten wird. DAnn hänge ich ganz am Schluss der Datei eine Zeile an mit
0 1 * * * pi sh -x /home/pi/steckdose_aus.sh
wenn ich jetzt den cron-Daemon neu starte über
/etc/init.d/cron restart
dann wird die neue Zeile jeden Tag um 1 Uhr ausgeführt. und diese Zeile startet eben unser Skript steckdose_aus.sh.
Wenn ich jetzt möchte, dass jeden Tag um 09:00 Uhr die Steckdose wieder an geht, schreibe ich mir eine zweite Datei steckdose_an.sh und mache das gleiche nochmal.

Die Funksteckdosen fernsteuern

 Verbinden der Komponenten

mit den STeckbrücken verbinden Sie den Ground-Pin des GPIO-Anschlusses (PIN 6) mit dem letzten PIN des Sendemoduls. Den Daten-PIn des Sendemoduls schließen Sie an PIN 17 der GPIO schnittstlele an, Power an PIN Nummer 4. An den Antennen-PIN des Sendemouls stecken Sie einfach nur eine Steckbrücke an ohne dieses am anderen Ende irgendwo anders anzustecken.

Wenn man das funksendemodel also mit der CMOS-Batterie nach oben hält und die vier Pins von links nach rechts mit 1-4 beschriftet, dann wird so angeschlossen

Sendemodul PIN 1 GPIO 6
Sendemodul PIN 2 GPIO 17
Sendemodul PIN 3 GPIO 4
Sendemodul PIN 4 leeres Jumperkabel

Nun müssen noch die Jumper auf der Rückseite der Steckdose richtig gesetzt werden.

Jumper Position
1 oben
2 unten
3 oben
4 unten
5 oben
A siehe unten
B siehe unten
C siehe unten
D siehe unten
E unten

Bei A-D müssen Sie pro Steckdose immer einen der vier Jumper auf die Stellung oben stellen, alle anderen bleiben unten. Damit ist es Ihnen möglich, die einzelnen Steckdosen getrennt voneiannder an- oder auszuschalten. Wenn Sie natürlich alle Steckdosen zusammen an- oder ausschalten wollen, dann stellen Sie bie allen drei Steckdosen den gleichen Jumper nach oben. Der Jumper-Code unserer STeckdose wäre, wenn wir den Jumper A anch oben stellen, 1, beim Jumper B nach oben 2, bei Jumper C 3 und bei Jumper D 4.

Die Jumper 1-5 sind Ihr sogenannter Home-Code. Dieser soll verhindern, dass Sie mit dem Raspberry Pi oder mit der Steckdose versehentlich einen Steckdosensatz mit einem anderen Home-code (etwa vom Nachbarn) steuern. Der home-Code unseres STeckdosensatzes wä#re also in diesem Beispiel 10101.

Grundinstallation

apt-get install git-core

git clone git://git.drogon.net/wiringPi

cd wiringPi/

./build

cd ..

git clone git://github.com/xkonni/raspberry-remote.git

cd raspberry-remote/

make send

Einschalten ohne Weboberfläche

Gehens ei in den ordner von raspberry-remote und geben dort den Befehl ein

sudo ./send <Home-Code> <Jumper-Code> [1|0]

1 am Ende steht für ein, 0 am Ende für aus.

Dieses Einschalten ist natürlich noch nicht wirklich komfortabel. Deswegen müssen wir das noch umkonfigurieren, damit das Ganze einfacher geht.

Ein- und Ausschalten per PowerPi Weboberfläche

Als erstes installieren wir benötigte Pakete und fügen benötigte User und Ordner hinzu

apt-get update

sudo apt-get install nginx php5-fpm php5-cgi php5-cli php5-common

sudo useradd www-data

sudo groupadd www-data

sudo usermod -g www-data www-data

sudo mkdir /var/www

sudo chmod -R 755 /var/www

sudo chown www-data.www-data /var/www

jetzt bearbeitne wir noch die Konfigurationsdatei des nginx-Webservers mit sudo vi /etc/nginx/sites-enabled/default

server {

listen 80;

root /var/www;

index index.html index.php;

server_name <hostname Raspberry Pi>;

location / {

try_files $uri $uri/ /index.php?$args;

}

location ~ .\php$ {

try_files $uri =404;

fastcgi_pass unix:/var/run/php5-fpm.sock;

fastcgi_index index.php;

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

include fastcgi_params;

}

}

und starten den Server neu

sudo /etc/init.d/nginx restart

Jetzt installieren wir diewebanwendung PowerPi, mit der wir später die Steckdosen fernsteuern wollen, und kopieren diese dann in das /var/www-Verzeichnis

wget http://raspberrypiguide.de/stuff/powerpi-0.1.tar.gz

tar xzvf powerpi-0.1.tar.gz

cd powerpi-0.1

sudo make clean

sudo make

sudo make install

sudo /etc/init.d/powerpi start

cp -rpf ./www/* /var/www

chown -R www-data.www-data /var/www

Von  nun an kommt ihr mittels http://<hostname oder IP Raspberry Pi> auf die Weboberfläche von powerpi, mit der ihr die steckdosen fernsteuern könnt.

Ein- und Ausschalten über Weboberfläche RaspCTL oder Smartphone App

In diesem Blogpost habe ich Ihnen gezeigt, wie Sie alternative Konsolenbefehle an den Raspberry Pi über die weboberfläche RaspCTL oder alternativ auch per Android bzw. iOS-App ganz bequem „per Fernbedienung“ ausführen. Natürlich können Sie auf diese Weise auch  die oben gezeigten Send-Befehle zum ein- und ausschalten der Steckdosen verschicken.

 

Wenn dir dieser Post gefallen hat, teile ihn doch auf Social Media, abonniere und verfolge mich per E-Mail, RSS-Feed, Social Media oder registriere dich auf meiner Seite. Wenn du registriert bist, like den Post bitte, wenn er dir gefallen hat!

]]>
http://dafrk-blog.com/?feed=rss2&p=2577 0
Raspberry Pi Skripte über Weboberfläche und Smartphone App ausführen http://dafrk-blog.com/?p=3898 http://dafrk-blog.com/?p=3898#respond Sun, 27 Dec 2015 18:40:45 +0000 http://dafrk-blog.com/?p=3898 Raspberry Pi Skripte über Weboberfläche und Smartphone App ausführen weiterlesen →]]> In meiner neuen Blogkategorie Raspberry Pi sammle ich interessante Projekte für zu Hause und allgemein für die IT-Administration, die mit dem Raspberry Pi zu tun haben. Beispielsweise habe ich Ihnen vor einigen Tagen gezeigt, wie Sie Ihren Raspberry Pi als Netzwerkkamera oder auch als Multi-Room-HiFi-Station nutzen. Es folgen noch Beiträge zum Fernsteuern von Beleuchtung, Stromversorgung (Steckdosen), Rollläden, Garagentore und Fenstern.

Wenn Sie dranbleiben und irgendwann all meine Projekte durchgearbeitet haben, werden Sie sich irgendwann die Frage stelllen, wie Sie denn simpel und einfach die einzelnen Funktionen, die Sie nun implementiert haben, von einem einzigen Gerät aus, beispielswiese einer Fernbedienung, steuern. Es hat ja durchaus seinen Reiz, wenn man allein mit einer Fernbedienung sein gesamtes Haus fernsteuern kann. Welches Geräte wäre dafür besser geeignet als Ihr Smartphone?

falls Sie noch kein Projekt für den Raspberry Pi implementiert haben, empfehle ich Ihnen, erstmal das in Angriff zu nehmen. Eine Fernsteurung des Raspberry Pip über das Smartphone macht schließlich erst dann Sinn. Sie sollten den Raspberry Pi dann auch fertig eingerichtet haben, wie in meinem Post einen Raspberry Pi einrichten – So geht’s erklärt.

Ist Ihr Pi dann bereit, müssen Sie erstmal das Software Repository hinzufügen, um raspctl später herunterladen und installieren zu können. Dazu fügen wir erst den GnuPG Key des Entwicklers hinzu und dann das Repository selbst.

wget debrepo.krenel.org/raspctl.asc

cat raspctl.asc | sudo apt-key add -
echo "deb http://debrepo.krenel.org/ raspctl main" | sudo tee /etc/apt/sources.list.d/raspctl.list

sudo apt-get update && sudo apt-get install raspctl

Jetzt können Sie die Weboberfläche auf Ihrem Smartphone oder einem anderen Gerät mit einem Webbrowser über http://<IP-Adresse oder hostname raspberry pi>:8086 aufrufen. Der Standardnutzer heißt admin mit Passwort admin.

Als erstes gehen Sie im Menü auf configuration / Authentication und ändern dort das Kennwort für den User admin.

Danach können Sie in der Sektion Commands Kommandos festlegen, die Sie über die Weboberfläche ausführen wollen. Das können beispielsweise Skripte zum An- und Ausschalten von  Glühlampen, Steckdosen oder HiFi-Radios sein, die Sie den entsprechenden projektspezifischen Blogposts entnehmen können. Sie können hier leider keine kommandos ausführen, bei denen der User nach dem Ausführen des kommandos noch was eingeben muss (z. B. ein Passwort). Sie müssen zusätzlich daarauf achten, dass Sie den vollen Pfad zu irgendwelchen Tools eingeben. DAs kommando raspistill, welches Sie als User pi in der linux-Shell eingeben können, wird nicht funktionieren, wenn Sie es in der Weboberfläche so einstellen. Sie müssen den vollen Pfad zu der raspistill-binary eingeben. Sie sollten alle kommandos über ein anschließendes &-Zeichen am Ende der Befehlszeile im Hintergrund ausführen lassen.

Die kommandos, die Sie in der Commands-Sektion festlegen, bestehen aus drei Elementen. Das Class-Element gruppiert Befehle, die bestimmte Eigenschaften Teilen. Sie können beispielswiese eine Class mit dem Namen Backup machen für alle Befhele, die mit Backups zu tun haben. DAs Action Element ist sozusagen die Etikette, die beschreibt, was ein kommando macht. Deshalb sollten Sie hier Namen wie beispielsweise Steckdose_Wohnzimmer_Einschalten geben. Das Command-Element letztendlich enthält die Befehlszeile selbst, die Sie nach den oben erläuterten Merkmalen erstellen sollten.

Die erstellte Befehlszeile können Sie nun bereits in der Weboberfläche ausführen, indem Sie auf den Launch-Button drücken. Sie können aber auch eine URL aufrufen im Format

http://<Hostname oder IP raspberry Pi>:8086/execute?class=<Klassenname>&action=<Name Action-Element>

Jetzt da Sie wissen, dass Sie mit dem aufurf einer URL beliebige Befehle auf Ihrem Raspberry Pi abrufen können, können Sie sicha fu dem Pi oder auf einem anderen Gerät noch einen weiteren Webserver installieren, z. B. den Apache. Dort richten Sie dann eine Website ein mit Links zu den einzelnen Befehlen. Diese Links können Sie auf Icons setzen, so dass Sie einfach nur noch mit dem Finger auf ein Bild drücken, um bestimmte Befehle wie Lampe ein/aus auszuführen. in die Erstellung der Seite gehe ich jetzt an dieser Stelle nicht weiter ein, aber die notwendigen HTML-Kenntnisse sind mit etwas Google-Expertise schnell erworben. Nun können Sie von jedem beliebigen Gerät, vor allem aber von Ihrem Smartphone aus, einfach diese Website mit den Icons aufrufen und die jeweiligen Befehle darüber ausführen.

Alternativ: ausführen von Raspberry Pi Skripten über Smartphone-App

anstatt die Befehle über raspctl auszufürhen, können Sie auch eine smartphone-App verwenden. Für Apple iOS installieren Sie sich beispielsweise SimpleSSH (1.99 US$ im App Store). Auf der App erstellen Sie Kommandos, die Sie dann ganz einfach per Fingerdruck auf dem Gerät ausführen können.

Für Android gibt es extra für den Raspberry Pi die App Raspberry SSH Lite. Acuh hier legen Sie vorher Befehle und buttons fest, die Sie dann wie über eine Fernbedienung auslösen können.

 

 

Wenn dir dieser Post gefallen hat, teile ihn doch auf Social Media, abonniere und verfolge mich per E-Mail, RSS-Feed, Social Media oder registriere dich auf meiner Seite. Wenn du registriert bist, like den Post bitte, wenn er dir gefallen hat!

]]>
http://dafrk-blog.com/?feed=rss2&p=3898 0