linux|n00b Linux für Ein- und Umsteiger

25Mai/110

Gewichtete Zufallsauswahl eines Arrayelements

Weil ich das schon Ewigkeiten mal "zu Papier" bringen wollte, habe ich heute mal eine Angelegenheit in Sachen Webprogrammierung mit PHP.

array_rand

Aus einem Array ein Element zufällig auszuwählen, ist mit PHP-Bord-Mitteln denkbar einfach.

$randomKey = array_rand($anyArray);
$randomElement = $anyArray[$randomKey];

dw_rand

Jedoch hat dabei jedes Element die gleiche Ziehungswahrscheinlichkeit. Schon vor einiger Zeit fand ich diese Lösung, um die Auswahl gewichten zu können.

Was diese Lösung nicht kann

Die dort vorgestelle PHP-Funktion dw_rand ist schon mal ein guter Anfang. Mein einziges Problem, welches ich damit nun noch habe, ist, dass ich im Vorhinein nicht weiß, wie die Wahrscheinlichkeiten bestimmt sind. Diese sollen sich aus den Arrayelementen selbst ergeben und nicht explizit vorgegeben sein, so wie es die Funktion dw_rand erfordert.

Wie ich das meine? Vielleicht kann folgendes Beispiel das ein wenig verdeutlichen.

Anwendungsbeispiel

Angenommen wir betreiben einen Internet-Shop, bei dem es auch mal einen Artikel zu gewinnen gibt. Nun soll aus einem Teil der Produktpalette zufällig ein Produkt gelost und als Gewinn verschenkt werden. Aber teure Produkte sollen eine geringere Wahrscheinlichkeit erhalten als die preisgünstigeren Produkte, so dass letztere häufiger ausgelost werden.
(Zugegebenermaßen ist mein eigener Anwendungsbereich ein etwas anderer, aber als Beispiel kann dies trotzdem dienen, hoffe ich.)

Veranschaulichen wir das mal mit ein wenig Quellcode. Wir nehmen an, wir haben zur Laufzeit folgendes Array:

$products = array(
    array(                //  im echten Programmierumfeld wäre dies dann eher ein Objekt der Klasse Product und kein Array
        'id' => 123,      //  ID des Produkts
        'cost' => 500     //  Kosten des Produkts
    ),
    array(
        'id' => 456,
        'cost' => 100
    ),
    array(
        'id' => 789,
        'cost' => 200
    )
);

Aus diesen drei Produkten soll nun je nach Preis eines zufällig ausgewählt werden. Das Produkt mit der ID 456 soll dabei am häufigsten gewählt werden, da es am günstigsten ist.

Da müssen wir noch was machen...

Die Funktion dw_rand benötigt für die Häufigkeitsverteilung aber keine ganzzahligen Preise, sondern Fließkommazahlen zwischen 0.0 und 1.0 (für Prozentwerte von 0% bis 100%).

Ansatz

Was wir brauchen, ist eine Art Umkehrwert der Kostenangaben für die Produkte. Hier reicht es allerdings nicht, einfach nur 1/x zu rechnen, denn die resultierenden Wahrscheinlichkeiten sollen ja in Summe genau 1.0 (für 100%) ergeben. Ich möchte die Erläuterungen an dieser Stelle einfach abkürzen und sogleich die Lösung präsentieren (nicht zuletzt, weil ich den Quellcode bereits recht ausführlich dokumentiert habe).

Lösung

<?php

class RandomUtil {

	/**
	 * Gibt ein zufällig gewähltes Element aus einem Array zurück.
	 *
	 * Wird ausschließlich das Array übergeben, gibt es keine bevorzugten Elemente im Array.
	 * Es wird einfach ein Element nach Gleichverteilung aller Elemente ausgewählt.
	 *
	 * Wird neben dem Array zusätzlich der optionale Parameter $fieldWithRarity übergeben,
	 * so wird gewichtet ausgewählt. Je höher die Rarität, desto geringer die Gewichtung bei
	 * der zufälligen Auswahl und umgekehrt. Elemente im Array mit niedriger Rarität, also
	 * mit höherer Gewichtung, werden statistisch häufiger ausgewählt.
	 *
	 * @param $array Array mit Elementen, von denen eins zufällig gewählt werden soll
	 * @param $fieldWithRarity Feld mit Rarität/Seltenheitswert in den Elementen des Arrays
	 * @return zufälliges Element des übergebenen Arrays oder NULL, wenn das Array leer ist
	 */
	public static function getRandomArrayElement($anyArray, $fieldWithRarity = null) {
		if (empty($anyArray)) {
			return null;
		}

		if (empty($fieldWithRarity)) {
			$randKey = array_rand($anyArray);
		} else {
			$probArray = self::getProbabilitiesArray($anyArray, $fieldWithRarity);
			$randKey = self::dw_rand($probArray);
		}

		return $anyArray[$randKey];
	}

	/**
	 * Gibt ein Array mit Wahrscheinlichkeitswerten zurück. Diese werden von dw_rand benötigt.
	 */
	private static function getProbabilitiesArray(array $anyArray, $fieldWithRarity) {
		if (empty($fieldWithRarity)) {
			throw new Exception('Es muss das Feld mit dem Raritätswert angegeben werden');
		}

		//	Array mit Wahrscheinlichkeiten (wird gleich befüllt, zunächst Erläuterung an Beispiel)
		$probabilities = array();

		/*
			BEISPIEL:
			 Produkt-Objekte mit Gewichtung auf das Feld 'cost', das ist der Raritätswert

			Situation:
			 Wir haben Produkte, die Geld kosten. Von allen Produkten soll eines zufällig
			 ausgewählt werden. Allerdings sollen die günstigen Produkte wahrscheinlicher
			 und die teuren Produkte unwahrscheinlicher sein (bzw. häufiger und seltener).
			 Mit den Kosten der Produkte als Grundlage zum Ermitteln von Wahrscheinlich-
			 keiten ergibt sich folgender Rechenweg.

			(1) - Kosten
			 K_Prod1 = 30.000
			 K_Prod2 = 50.000
			 K_Prod3 = 40.000
			 SUMME = 120.000

			(2) - Kostenverhältnisse
			 R_Prod1 = 120k / 30k = 4
			 R_Prod2 = 120k / 50k = 2,4
			 R_Prod3 = 120k / 40k = 3

			(3) - Faktor
			 SUMME_VERHAELTNISSE = 4 + 2,4 + 3 = 9,4
			 FAKTOR = 1 / 9,4 = 0,106382979

			(4) - Wahrscheinlichkeiten
			 P_Prod1 = 0,106382979 * 4 = 0,425531916
			 P_Prod2 = 0,106382979 * 2,4 = 0,25531915
			 P_Prod3 = 0,106382979 * 3 = 0,319148937

			(-->) folgendes Array käme dabei heraus
			 array(
				'1' => 0.425531916,
				'2' => 0.25531915,
				'3' => 0.319148937
			 );
		*/

		//	(1) - Einzel- und Gesamtkosten ermitteln und in das Wahrscheinlichkeits-Array stecken
		$costSum = 0;
		foreach ($anyArray as $key => $element) {
			$rarity = $element[$fieldWithRarity];

			if ($rarity >= 0)  {
				$costSum += $rarity;
				$probabilities[$key] = $rarity;
			}
		}

		//	(2) - Verhältnis von Gesamtkosten zu Einzelkosten bilden und ins Wahrsch.-Array stecken
		foreach ($probabilities as $key => $cost) {
			$probabilities[$key] = $costSum / $cost;
		}

		//	(3) - Faktor ermitteln, mit dem wir die Kostenverhältnisse in Summe auf 1 bekommen (Dreisatz)
		$ratioSum = 0;
		foreach ($probabilities as $ratio) {
			$ratioSum += $ratio;
		}
		$factor = 1 / $ratioSum;

		//	(4) - die endgültigen Wahrscheinlichkeiten errechnen (Dreisatz Fortsetzung)
		foreach ($probabilities as $key => $ratio) {
			$probabilities[$key] = $factor * $ratio;
		}

		//	Wahrscheinlichkeiten zurück geben
		return $probabilities;
	}

	/**
	 * @see http://rotering-net.de/tut/php-dwrand.html
	 */
	private static function dw_rand($space, $errval = false) {
		$res = 1000000000;
		$rn = mt_rand(0, $res - 1);
		$psum = 0;

		foreach ($space as $element => $probability) {
			$psum += $probability * $res;
			if ($psum > $rn) return $element;
		}

		return $errval;
	}
}

Verwendung

Aus unserem Produkte-Array bestehend aus Product-Instanzen (bzw. aus Sub-Arrays zur Veranschaulichung) lässt sich nun wie folgt ein Element zufällig, gewichtet nach Kosten auswählen:

$randomProduct = RandomUtil::getRandomArrayElement($products, 'cost');

Einfach, oder? Ich hoffe, diese (für mich schon seit Längerem hilfreiche) Klasse kann auch euch da draußen dienlich sein. Mir war und ist sie es - in diesem Sinne auch meinen Dank an Thorsten Rotering für seine dw_rand-Funktion!

18Mrz/110

Farbschemen in gedit einbinden

Neben den vorinstallierten Farbprofilen für Code-Highlighting in gedit lassen sich weitere Farbprofile installieren. Diese, wie sie eigentlich genannt werden, Style Schemes sind nichts weiter als XML-Dateien und lassen sich entsprechend einfach in gedit einbinden.

Style Schemes installieren

Nach Herunterladen der entsprechenden XML-Datei für dein favorisiertes Farbschema kopierst du die XML-Datei in folgendes Unterverzeichnis in deinem Home-Verzeichnis:

~/.local/share/gtksourceview-2.0/styles

Einen Neustart von gedit vorausgesetzt, kannst du nun im Menü unter "Bearbeiten" > "Einstellungen" im Reiter "Schrift und Farben" dein neues Farbschema auswählen.

Style Schemes editieren

Da XML-Dateien mit einfachen Texteditoren (wie z.B. gedit ;-) editiert werden können, kannst du Farbschemen, die du auf diese Weise in gedit einbindest, spielend einfach bearbeiten. Damit kannst du die Farben für das Syntax Highlighting nach eigenem Belieben anpassen.

Screenshot von gedit mit Farbschema "Dueddel"

Mein eigenes Farbschema

Ich habe mir von der oben verlinkten Liste mit den downloadbaren Style Schemes den "Vibrant"-Style zu Herzen genommen, weil mir dieser am besten gefiel, allerdings habe ich ihn ein klein wenig editiert. Hier kannst du meine Version des Vibrant-Styles herunterladen.

~/.local/share/gtksourceview-2.0/style

17Mrz/110

Plugins in gedit einbinden

Dem geneigten Gnome-User dürfte der Texteditor gedit nicht gerade fremd sein. Um gedit ein wenig zu pimpen, lassen sich Plugins installieren, gedit bringt dafür bereits von Haus aus einige nützliche Plugins mit. Diese und weitere kannst du auf der Plugin-Seite von gedit einsehen.

Aber wie installiert man diese Plugins, die nicht Teil der Standardinstallation von gedit sind?
Das ist glücklicherweise einfach erklärt:

  1. Plugin(s) herunterladen
  2. Plugin(s) im Home-Verzeichnis ins entsprechende Unterverzeichnis kopieren
  3. Plugin(s) in gedit aktivieren

Fertig.

1. Plugins herunterladen

Von der genannten Plugin-Seite (Link siehe oben) kannst du nach einigem Herumstöbern die von dir gewünschten Plugins beziehen. Welche dir gefallen, solltest du wie jeder andere auch am besten selbst entscheiden. Ich persönlich mag das trailsave-Plugin, welches beim Speichern automatisch sämtliche überflüssige Whitespaces aus der Datei entfernt (also z.B. Leerzeichen und Tabs am Ende einer Zeile), darüber hinaus finde ich auch das Smart Highlighting-Plugin sehr praktisch, bei welchem durch Markieren eines Wortes sämtliche Vorkommen des selben Wortes in der geöffneten Datei hervorgehoben werden.

Wie auch immer - wichtig ist, dass das heruntergeladene Plugin zwei Bestandteile aufweist:

  1. eine Datei <pluginname>.gedit-plugin
  2. sowie eine Datei <pluginname>.py oder stattdessen ein Verzeichnis <pluginname>/

2. Plugins installieren

Die heruntergeladenen Plugin-Dateien sind in dein Homeverzeichnis zu kopieren, genauer gesagt nach:

~/.gnome2/gedit/plugins

Bei mir ist das z.B. das Verzeichnis /home/arvid/.gnome2/gedit/plugins.

3. Plugins aktivieren

Plugins-Einstellungen in gedit

Zu guter Letzt ist gedit neu zu starten und im Menü unter "Bearbeiten" > "Einstellungen" im Reiter "PLugins" das installierte Plugin zu aktiveren.

4Sep/100

SVN ignore

Da die vor wenigen Wochen beschriebene SVN-Integration in den Nautilus keine Möglichkeit bietet, Dateien oder Verzeichnisse zu ignorieren, hilft nur noch ein Kommandozeilenbefehl.

Angenommen dein Projekt befindet sich unter SVN-Kontrolle und du hast ein Verzeichnis file_cache/ (unterhalb eines Verzeichnisses application/) sowie ein Verzeichnis logs/, so kannst du den Inhalt beider Verzeichnisse von der SVN-Kontrolle wie folgt ausschließen:

[arvid@C64 ~]$ cd devel/meinprojekt
[arvid@C64 meinprojekt]$ svn propset svn:ignore "*" application/file_cache/
[arvid@C64 meinprojekt]$ svn propset svn:ignore "*" logs/

Der svn-propset-Befehl erwartet als Parameter also ein Muster der zu ignorierenden Datei/en (im Beispiel ein Sternchen "*" für alle Dateien) gefolgt vom Zielverzeichnis, in dem sich die zu ignorierenden Dateien befinden.

Schließlich muss die Änderung noch ins Repository eingecheckt werden.

[arvid@C64 meinprojekt]$ svn ci -m "kommentar"

Gefunden habe ich diese kurze Hilfe im Übrigen hier: devcha-Blog.

13Aug/100

SVN für eigenen Bedarf einrichten

SVN? Wofür?

Nicht nur für die Bewältigung größerer Aufgaben im Team, sondern auch für eigenes, lokales Arbeiten kann es manchmal sinnvoll sein, Dateien und Verzeichnisse zu versionieren.

Das trifft so ungefähr auf jede selbstständige Arbeit zu, bei der man Ergebnisse produziert, diese hin und wieder oder sogar stetig überarbeitet und für die man in der Lage sein möchte, den Entwicklungsprozess zu rekonstruieren und im Bedarfsfall Änderungen rückgängig zu machen. Das können z.B. Studienarbeiten, Programmierprojekte, aber auch gestalterische Tätigkeiten bei der Bildbearbeitung oder dergleichen sein.

Vor diesem Hintergrund möchte ich erläutern, wie man ein eigenes Subversion-Repository aufsetzt, mit dem Dateien unter Versionskontrolle gestellt werden können.

Vermerk zum Thema Sicherheit

Ich möchte ausdrücklich darauf hinweisen, dass die von mir geschilderte Vorgehensweise nur für den Gebrauch an einem Single-User-System geeignet ist, bei dem auf das SVN-Repository nicht über das Netzwerk zugegriffen werden soll. Ich belasse es bei Standard-Belegungen in den config-Files und Authentifizierungen lasse ich außen vor.

Mit anderen Worten gilt für diese Anleitung: Sicherheit weicht dem Komfort.

Warum? Ich hege derzeit kein Interesse, meine eigenen Entwicklungsarbeiten und meine Erzeugnisse für's Studium der Öffentlichkeit zugänglich zu machen. Deshalb beschränke ich mich beim Einrichten auf den Betrieb an einem lokalen System, auf das ohnehin nur ich Zugriff habe.

1. Installation

Zunächst solltest du SVN installieren (Paketname "subversion"). Zur Installation werden diverse Dienstprogramme mitgeliefert, von denen du auf jeden Fall auch svnadmin brauchen wirst. Im Terminal kannst du zum Beispiel mit den Befehlen

[arvid@C64 ~]$ which svn
/usr/bin/svn
[arvid@C64 ~]$ which svnadmin
/usr/bin/svnadmin

prüfen, ob die Installation erfolgreich war. Aber ich denke, da sollte es keine Probleme gegeben haben. Beide Befehle erzeugen im Übrigen lediglich eine kleine Ausgabe der Installationspfade für svn und svnadmin. Letzteres ist jenes Dienstprogramm, welches zum Konfigurieren des SVN-Repositories benötigt wird.

2. Vorüberlegungen

Bevor du loslegst, solltest du dir Gedanken darüber machen, wo du das SVN-Repository erstellen möchtest und vor Allem, wie du deine Verzeichnisstruktur organisieren möchtest. Einen kleinen Denkanstoß soll dir diesbezüglich der Abschnitt "Planung der Organisation Ihres Projektarchivs" aus dem SVN-Book bescheren.

Ich habe mich für folgendes Verzeichnislayout entschieden:

.
├── trunk
│   ├── devel
│   │   ├── meinprojekt
│   │   ├── nocheinrojekt
│   │   └── usw
│   ├── kreatief
│   │   ├── fotobearbeitung
│   │   ├── icons
│   │   └── usw
│   └── studium
│       └── usw
├── tags
└── branches

Unterhalb von trunk/ habe ich Verzeichnisse als Kategorien angelegt (devel/ für Develeopment bzw. Programmierung, kreatief/ für gestalterische Schaffensprozesse etc.). In die jeweiligen Kategorien pflege ich einzelne Projekte ein. Das kannst du allerdings vollkommen individuell halten, es gibt keine Vorschriften für die Ordnerstrukturen. Du kannst auch ganz auf die Einteilung in trunk, tags und branches sowie auf die Kategorien verzichten und zum Beispiel nur dein/e Projekt/e ins SVN-Repository stecken:

.
├── meinprojekt
├── nocheinrojekt
├── fotobearbeitung
├── icons
└── usw

Wie gesagt - das liegt ganz bei dir.

3. Verzeichnisstruktur anlegen

Um das Verzeichnislayout zu übernehmen, lege einfach ein paar Ordner in tmp/ an, wir werden sie in Kürze ins Repository übernehmen und dann wieder löschen.

[arvid@C64 ~]$ cd /tmp/
[arvid@C64 tmp]$ mkdir tmpsvn
[arvid@C64 tmp]$ mkdir tmpsvn/{trunk,tags,branches}
[arvid@C64 tmp]$ mkdir tmpsvn/trunk/{devel,kreatief,studium}

Die Projektverzeichnisse innerhalb der Kategorieverzeichnisse (wie zum Beispiel meinprojekt/ aus obiger Ordnerstruktur) lege ich nicht an, da das keine neuen Projekte sind, sondern welche, die bereits woanders gespeichert sind (das sind also schon bestehende Projekte, wir werden uns weiter unten darum kümmern). An dieser Stelle sollen uns die Kategorien genügen.

4. Repository anlegen und Struktur übernehmen

Auf ein Verzeichnis deiner Wahl erzeugst du nun mit svnadmin und dem Unterbefehl create ein neues SVN-Repository. Ich habe mich für ein Verzeichnis auf meiner externen USB-Festplatte entschieden:

[arvid@C64 tmp]$ svnadmin create /media/AUSSENPLADDE/SVN-REPO

Das Verzeichnis SVN-REPO/ wird automatisch angelegt und mit einigen notwendigen Ordnern und Dateien befüllt, die du auf keinen Fall löschen oder bearbeiten solltest (es sei denn, du weißt genau, was du tust). Jetzt kannst du die Verzeichnisstruktur übernehmen, die du eben noch unter tmp/ angelegt hast:

[arvid@C64 tmp]$ svn import tmpsvn file:///media/AUSSENPLADDE/SVN-REPO/ -m "initiales Verzeichnislayout für das SVN-Repository angelegt"
Hinzufügen     tmpsvn/trunk
Hinzufügen     tmpsvn/trunk/devel
Hinzufügen     tmpsvn/trunk/kreatief
Hinzufügen     tmpsvn/trunk/studium
Hinzufügen     tmpsvn/branches
Hinzufügen     tmpsvn/tags

Revision 1 übertragen.
[arvid@C64 tmp]$ rm -r tmpsvn/

Damit hast du bereits die allererste Revision erzeugt, als Kommentar (Parameter -m = message) hast du einen kurzen Text angegeben, der das Ganze beschreibt. Im Anschluss löscht du das temporär angelegte Verzeichnis tmpsvn/.

5. Repository auschecken

Wechsle in dein Home-Verzeichnis oder in ein anderes, wenn du deine Projektdaten nicht im Home-Verzeichnis haben möchtest und führe dann den SVN Checkout-Befehl aus:

[arvid@C64 tmp]$ cd ~
[arvid@C64 ~]$ svn co file:///media/AUSSENPLADDE/SVN-REPO/trunk/devel
Ausgecheckt, Revision 1.

Also ich beschränke mich demnach nur auf das Auschecken der Kategorie "devel" für meine Development-Projekte. Du solltest devel durch deine Kategorie oder den Namen deines Projektes austauschen, ggf. auch trunk/ weglassen (je nachdem, für welches Verzeichnislayout du dich entschieden hast). In deinem Homeverzeichnis wird nun devel/ angelegt, dort hinein kopieren wir gleich unsere Projekte aus den alten Devel-Verzeichnissen, in denen es noch keine Verionskontrolle gab.

6. Projektdaten kopieren und dem Repository hinzufügen

Wie ich schon geschrieben habe, möchte ich kein neues Projekt beginnen, sondern ein bestehendes ins SVN-Repository übertragen. Wenn du das genauso machen möchtest, dann kopiere einfach deinen Projektordner aus dem alten devel-Verzeichnis ins neue, welches wir gerade eben ausgecheckt haben.

[arvid@C64 ~]$ cp -R altes_devel_ohne_svn/meinprojekt/ devel/

Damit befindet sich meinprojekt/ jetzt unter devel/. Ein kurzer Test zeigt, dass der Kopiervorgang geklappt hat, aber dass SVN noch nichts damit anzufangen weiß (beachte das Fragezeichen an erster Stelle der Ausgabe):

[arvid@C64 ~]$ svn status devel/
?       devel/meinprojekt

Du musst also noch SVN mitteilen, dass da etwas Neues ist, was du gerne dem SVN-Repository hinzufügen möchtest:

[arvid@C64 ~]$ cd devel
[arvid@C64 devel]$ svn add meinprojekt/
A         meinprojekt
A         meinprojekt/public
A         meinprojekt/public/index.php
A         meinprojekt/public/js
A         meinprojekt/public/js/swfobject-2.2.js
A         meinprojekt/public/js/common.js
A         meinprojekt/public/js/jquery-1.4.min.js
A         meinprojekt/public/js/common.functions.js
A         meinprojekt/public/js/admin
       <..... usw. .....>

Es folgen je nach Projektgröße einige oder viele Dateien mit dem Präfix A, welcher nichts anderes bedeuted als "Added", also dem SVN hinzugefügt (aber noch nicht dem Repository!). Was nun noch fehlt, ist das Einchecken, auch Commit genannt:

[arvid@C64 devel]$ svn ci -m "meinprojekt ins Repository eingepflegt"
Hinzufügen     meinprojekt
Hinzufügen     meinprojekt/application
Hinzufügen     meinprojekt/application/Bootstrap.php
Hinzufügen     meinprojekt/application/configs
Hinzufügen     meinprojekt/application/configs/application.ini
Hinzufügen     meinprojekt/application/configs/preferences.ini
Hinzufügen     meinprojekt/application/controllers
       <..... usw. .....>
Hinzufügen     meinprojekt/public/js/swfobject-2.2.js
Übertrage Daten .........................................
Revision 2 übertragen.

Erst der Check-In-Befehl überträgt die Dateien zum Repository. Wieder hast du mit dem Parameter -m einen Kommentar angegeben. Und wieder folgt eine Liste mit all den Dateien, die du eben noch dem SVN bekannt gemacht hast. Durch das Einchecken hast du die nächste Revision erzeugt.

7. Prüfen

Du kannst nun testen, ob das Schiff wirklich sicher den Hafen erreicht hat, indem du dir einfach den SVN-Log ansiehst. Hierfür mache zuerst ein Update, anschließend lasse dir die Logs anzeigen:

[arvid@C64 devel]$ svn up
Revision 2.
[arvid@C64 devel]$ svn log
------------------------------------------------------------------------
r2 | arvid | 2010-07-20 10:44:53 +0200 (Di, 20. Jul 2010) | 1 Zeile

meinprojekt ins Repository eingepflegt
------------------------------------------------------------------------
r1 | arvid | 2010-07-20 10:07:16 +0200 (Di, 20. Jul 2010) | 1 Zeile

initiales Verzeichnislayout für das SVN-Repository angelegt
------------------------------------------------------------------------

Du siehst mit dem Log-Befehl die letzten Revisionen (neueste immer oben) inklusive der Kommentare. Offenbar verlief alles nach Plan.

8. Zusammenfassung

Die eigentliche "Magie" des Ganzen liegt in den Abschnitten 3 und 4, in denen gezeigt wird, wie du Verzeichnisstrukturen anlegst und diese in das neu erzeugte Repository übernimmst.

Alles andere danach sollte dir eventuell bereits vertraut sein, wenn du ein geübter SVN-User bist. Die Abschnitte 5 bis 7 lassen sich auch mit beliebigen Frontend-SVN-Clients erledigen, wie zum Beispiel mit einem im Nautilus integrierten SVN-Client.

Weitere Infos

Sieh dir die folgenden Seiten an, denen ich ebenfalls einige Informationen entnommen habe. Vielleicht helfen sie dir noch ein bisschen mehr für's Verständnis:

svnadmin create D:\repository\myproject
12Aug/100

SVN-Integration in den Nautilus

Im Windows-Bereich ist man sich mit ToroiseSVN seit jeher eine nahtlose Einbindung von Subversion in den Win-Explorer gewohnt.
Im Linux-Bereich muss man zumeist auf andere Tools zurückgreifen (die nicht im Dateiexplorer eingebunden werden) oder via Terminal mit dem svn-Kommando arbeiten.

Schritt 1: GnubVersion

Screenshot: GnubVersion ohne SVN Diff (Quelle: GnubVersion Projektseite)

Screenshot: GnubVersion ohne SVN Diff (Quelle: GnubVersion Projektseite)

Wer eine TortoiseSVN-ähnliche Lösung für Linux sucht und bislang noch nichts für seinen Gnome-Desktop gefunden hat, dem sei zunächst GnubVersion empfohlen. Ähnlich wie TortoiseSVN in den Windows-Dateiexplorer integriert sich GnubVersion nach dessen Installation in den Nautilus.

Also, erster Schritt: GnubVersion installieren.

Allerdings ist GnubVersion bei Weitem nicht so funktionsumfangreich wie das Windows-Pendant. Viele Funktionen sind im Grunde auch gar nicht nötig und das Wesentliche beherrscht GnubVersion ja auch, aber eine Sache fehlt: der SVN-Diff.

Schritt 2: Meld und die Nautilus-Aktionen

Um diesen Verlust auszugleichen, habe ich mir etwas Abhilfe verschafft. Folgendes wird dafür benötigt:

  • das Tool Meld (wenn ich mich recht entsinne, habe ich mir auch andere Diff-Betrachter angesehen, aber dieser gefiel mir am besten)
  • die Nautilus-Aktionen
  • und ein Skript, welches SVN mit Meld und den Nautilus-Aktionen verbinden soll (siehe Schritt 3)

Das sind die Zutaten - Wollen wir sie mal miteinander vermischen...

Die Nautilus-Aktionen lassen sich zum Beipiel in yum (dem Fedora-Paketmanager) unter dem Paketnamen "nautilus-actions" schnell finden und installieren. Das Gleiche gilt für Meld, der Paketname lautet schlicht und ergreifend "meld".

Schritt 3: Skript für Nautilus-Aktionen erstellen

Sind beide Tools installiert, folgt der spannende Teil: das Sktipt. Dieses ist zwar vergleichsweise simpel, dafür aber sehr nützlich. Zunächst der Inhalt des Skripts, eine kurze Erläuterung folgt darunter:

#!/bin/bash

# --------------------------------------------------------------------------
# Benutzung des Skripts prüfen

# Anzahl Parameter prüfen
if [ ! $# -eq 1 ]; then
 echo "svndiff benötigt genau einen Parameter, nämlich den Pfad zur Datei (wobei diese natürlich unter SVN-Kontrolle stehen muss)" | zenity --text-info --title="Fehler" --width=600 --height=400
 exit 1
fi

# --------------------------------------------------------------------------
# Variablen anlegen und Dateien prüfen

# Anzahl Wörter, die durch Slash getrennt sind
WORD_COUNT=`echo ${1} | sed 's/\// /g' | wc -w`
WORD_COUNT_PLUS_1=`expr $WORD_COUNT + 1`

# Pfad und Dateinamen ermitteln
FILE_PATH="`echo ${1} | cut -d/ -f1-${WORD_COUNT}`/"
FILE_NAME=`echo ${1} | cut -d/ -f${WORD_COUNT_PLUS_1}`

# Pfade für die SVN-Dateien zusammenstöpseln
# Vergleichsdatei aus SVN-Repository: <pfad>/.svn/text-base/<file>.svn-base
SVN_BASE_FILE="${FILE_PATH}.svn/text-base/${FILE_NAME}.svn-base"
# geänderte bzw. zu prüfende Datei <pfad><file> (kommt direkt aus dem Aufrufparameter, daher kein Zusammenstöpseln notwendig)
SVN_CHANGED_FILE=$1

# Pfade prüfen
# die SVN-Base-Datei
if [ ! -f $SVN_BASE_FILE ]; then
 echo "Datei existiert nicht: $SVN_BASE_FILE" | zenity --text-info --title="Fehler" --width=600 --height=100
 exit 1
fi
# die Datei
if [ ! -f $SVN_CHANGED_FILE ]; then
 echo "Datei existiert nicht: $SVN_CHANGED_FILE" | zenity --text-info --title="Fehler" --width=600 --height=100
 exit 1
fi

# --------------------------------------------------------------------------
# den eigentlichen Zweck des Skripts erfüllen

# meld (das Diff-Tool) starten
meld $SVN_BASE_FILE $SVN_CHANGED_FILE &

exit 0

Was passiert in dem Skript im Klartext...? Diese Frage möchte ich dir natürlich sofort beantworten.

1. Pfade ermitteln
Für die Verwendung des Skripts wird beim Aufruf ein Parameter benötigt, nämlich der Pfad zur Datei, für die ein SVN-Diff angezeigt werden soll. Die Angabe des Parameters wird im Block nach der ersten Trennlinie geprüft. Im nächsten Abschnitt wird aus dem Parameter der Verzeichnispfad vom Dateinamen getrennt. Daraus wird der Pfad erschlossen, der zur entsprechenden SVN-Base-Datei führt. Nun sind beide Pfade bekannt: Der Pfad zur eigentlichen Datei und der Pfad der zugehörigen SVN-Base.

2. Diff-Tool starten
Im letzten Block unter dem dritten Trennstrich wird das Tool Meld gestartet, dieses erhält als Parameter die Pfade der beiden Dateien. Das ist auch schon alles, was wir an dieser Stelle brauchen.

3. That's it.
Mehr passiert da auch gar nicht...

Screenshot: SVN Diff mit nautilus-actions

Screenshot: SVN Diff mit nautilus-actions

Schritt 4: Skript und Nautilus-Aktionen vermählen

Lege nun eine Textdatei an und speichere den Inhalt des oben abgebildeten Skripts darin. Pfad und Dateiname sind egal, denn Namen sind bekanntlich Schall und Rauch. Lege die Datei z.B. im Home-Verzeichnis unter ./.nautilus-actions/ ab und nenne sie svndiff (meinethalben auch "schall und rauch"). Starte dann das Programm nautilus-actions unter System > Einstellungen > Nautilus-Aktionen und füge diese Datei einfach hinzu.

Und fertig ist unser SVN-Diff.

Schritt 5: Have fun!

Wähle im Nautilus Dateien aus, die sich unter Versionskontrolle mit Subversion befinden und lasse dir via Rechtsklick auf "SVN-Diff" eben selbigen anzeigen.

Screenshot: Meld mit SVN-Diff

Screenshot: Meld mit SVN-Diff

5Aug/100

Viele Dateien auf einmal umbenennen mit pyRenamer

Hunderte von Dateien, die du umbenennen möchtest? Aber um Gottes Willen nicht einzeln, sondern in einer Art Batch-Verarbeitung?

Kein Problem! Nimm einfach den pyRenamer. Mit diesem Tool ist das gleichzeitige Umbenennen vieler Dateien möglich. Die Bedienung ist im Übrigen sehr einfach, deshalb schreibe ich dazu mal nichts weiter.

Zwei kleine Anwendungsbeispiele

Fotos von deiner Foto-Kamera kopiert, aber dir gefallen die automatisch gesetzten Dateinamen wie "p4200019.jpg" einfach nicht.
Du wünscht dir stattdessen Bezeichnungen wie "urlaub-ruegen-2010-001", "urlaub-ruegen-2010-002" etc. oder statt einer Durchnummerierung gar zufällige Zahlen.

Oder aber du bist Programmierer und in deinem Projekt gilt es, eine Vielzahl von Log-Dateien zur automatisierten Verarbeitung umzubenennen, weil der Log-Analyzer ein bestimmtes Dateinamenformat ewartet (warum auch immer er das sollte... aber das ist ja nur ein Beispiel)?

Der pyRenamer macht's möglich.