• Willkommen im Linux Club - dem deutschsprachigen Supportforum für GNU/Linux. Registriere dich kostenlos, um alle Inhalte zu sehen und Fragen zu stellen.

[solved] bash: Datei editieren, best practice?

Moin Moin,

ich hätte gern mal ein kleines Problem mit bash :D Hintergrund ist folgender: Ich installiere Server automatisch, sprich per autoyast und dabei wird ein script als rpm mit installiert das hinterher einige Änderungen vornimmt. Quellen einbinden, Pakete installieren, individuelle Anpassungen halt. Unter Anderem soll es die boot.localfs ändern. Die Zeile tmpfs soll um den Eintrag usbfs erweitert werden. Eigentlich einfach. Oder auch nicht.
Zur Veranschaulichung meiner Probleme hier der Code:
Code:
#!/bin/bash

IFS=$'\n'
mv /test/boot.localfs /test/boot.localfs.orig
for g in `cat /test/boot.localfs.orig`
do
    if [[ $g =~ 'tmpfs=' ]]
    then
        aus_b=`echo "$g" | sed 's/mqueue/mqueue,usbfs/'`
        echo "$aus_b" >> /test/boot.localfs
    else
        echo "$g" >> /test/boot.localfs
    fi
done
chmod +x /test/boot.localfs
Soweit funktioniert es aber sämtliche Leerzeilen fehlen hinterher.

Ok, nächster Versuch:
Code:
#!/bin/bash

mv /test/boot.localfs /test/boot.localfs.orig
while IFS=$'\n' read line
do
    if [[ $line =~ 'tmpfs=' ]]
    then
        aus_b=`echo $line | sed 's/mqueue/mqueue,usbfs/'`
        echo "$aus_b" >> /test/boot.localfs
    else
        echo "$line" >> /test/boot.localfs
    fi
done < /test/boot.localfs.orig
chmod +x /test/boot.localfs
Hinsichtlich der Leerzeilen wesentlich besser, die sind nämlich noch da. Aber irgendwie sind Zeilen die ein \ enthalten umgebrochen oder sonst wie verändert. Hier mal ein diff:
Code:
36c36
< typeset -r tmpfs=tmpfs,ramfs,hugetlbfs,mqueue
---
> typeset -r tmpfs=tmpfs,ramfs,hugetlbfs,mqueue,usbfs
69c69
< 	mount:\ *) continue ;; # messages from mount
---
> 	mount: *) continue ;; # messages from mount
310c310
< 		""|\#*)    continue ;;
---
> 		""|#*)    continue ;;
381,382c381
< 	    cat $mounts | \
< 	    while read des fs type opts rest; do
---
> 	    cat $mounts | 	    while read des fs type opts rest; do

Hallo? Was passiert hier? Früher hab ich das Ganze mit einem Perl-script gemacht und nicht mal ansatzweise solche Probleme gekannt? Aber ich will ja nun mit bash arbeiten und daher die Fragen: Warum werden in der ersten Variante die Leerzeilen ignoriert? Warum verändert die zweite Variante den Code?
Und als entscheidende Frage: Wie mache ich das Ganze richtig? Bin ich evtl. durch meine Perl-Vergangenheit auf dem falschen Weg, mit zeilenweise einlesen, testen und die Zeilen wieder ausgeben?
 
A

Anonymous

Gast
Geier0815 schrieb:
Hallo? Was passiert hier? Früher hab ich das Ganze mit einem Perl-script gemacht und nicht mal ansatzweise solche Probleme gekannt? Aber ich will ja nun mit bash arbeiten und daher die Fragen: Warum werden in der ersten Variante die Leerzeilen ignoriert? Warum verändert die zweite Variante den Code?
Und als entscheidende Frage: Wie mache ich das Ganze richtig? Bin ich evtl. durch meine Perl-Vergangenheit auf dem falschen Weg, mit zeilenweise einlesen, testen und die Zeilen wieder ausgeben?

Waum dort Leerzeichen verschwinden kann ich auf den ersten Blick auch noch nicht erkennen, klar ist allerdings warum die "\" verschwinden.

Aber was du dort machst ist ein vollkommen falscher Ansatz, dort bekommst du mehr Probleme als dir lieb sind, und es ist nicht einmal sicher das du wirklich nicht für die Zukunft noch böse Probleme dort einbaust.

Wenn immer möglich, die Datei nicht auf der Bash zerpfücken und dann durch Unmengen von bash Einzelbefehlen bearbeiten. mit einem Rutsch über "sed" erledigen, auch wenn das dann hinterher vielleicht ein klein bisschen nach " nützlichen aber nicht intuitiven Ferkeleien" ;) aussieht, sobald du verstehst was dort genau steht und was es macht ist es klar, eindeutig und absolut unmissverständlich.

gib mal ein bisschen Text und beschreibe was genau geändert werden soll und was nicht, und ich werde dir das in "sed" erklären.

robi
 
OP
Geier0815

Geier0815

Guru
Hmmpf, so langsam weiß ich warum Larry Perl erfunden hat...

Ok, trotzdem finde ich es nett das Du dich des Ganzen annehmen möchtest. Hier mal ein Code-Schnippsel der boot.localfs (auch wenn sie in ähnlicher Weise bei dir unter /etc/init.d zu finden sein wird):
Code:
#! /bin/sh
#
# Copyright (c) 2001-2002 SuSE Linux AG, Nuernberg, Germany.
# All rights reserved.
#
# /etc/init.d/boot.localfs
#
### BEGIN INIT INFO
# Provides:          boot.localfs
# Required-Start:    boot.udev boot.rootfsck
# Should-Start:      boot.md boot.lvm dasd_devfs_compat boot.multipath boot.evms boot.loadmodules
# Required-Stop:     boot.udev
# Should-Stop:       boot.md boot.lvm dasd_devfs_compat boot.multipath boot.evms
# Default-Start:     B
# Default-Stop:
# X-Interactive:     true
# Short-Description: check and mount local filesystems except /
# Description:       check and mount local filesystems except /
### END INIT INFO

. /etc/rc.status

# to get max number of parallel fsck processes
. /etc/sysconfig/boot
. /etc/sysconfig/kernel
. /etc/sysconfig/sysctl

test -z "$REDIRECT" && REDIRECT="$(showconsole 2>/dev/null)"
export REDIRECT
export FSCK_MAX_INST

#
# Special file systems (memory and network based)
#
typeset -r nomnt=autofs,bdev,rootfs,sockfs,pipefs,anon_inodefs,inotifyfs,eventpollfs,futexfs
typeset -r tmpfs=tmpfs,ramfs,hugetlbfs,mqueue
typeset -r netfs=nfs,nfs4,smbfs,cifs,afs,ncpfs

tmp=""
while read tag name rest ; do
    test "$tag" = "nodev" || continue
    [[ "$name" =~ ^(${nomnt//,/|})$ ]] && continue
    [[ "$name" =~ ^(${tmpfs//,/|})$ ]] && continue
    [[ "$name" =~ ^(${netfs//,/|})$ ]] && continue
    tmp=${tmp:+$tmp,}$name
done < /proc/filesystems

[...]

Ok, in diesem speziellen Fall könnte ich wohl global auf "mqueue" matchen und es mit "mqueue, usbfs" ersetzen weil mqueue nur in diesem einen Fall überhaupt in der Datei vorkommt. Also ein
Code:
cat boot.localfs.orig | sed 's/mqueue/mqueue,usbfs/'g
auf die gesamte Datei ausführen. Wie sieht dann der bash-code aus? Und wie ist es wenn eindeutiger Match und Substitution in einer Zeile zu finden und nicht gleich sind?
 
A

Anonymous

Gast
Geier0815 schrieb:
Hmmpf, so langsam weiß ich warum Larry Perl erfunden hat...
weil ihm langweilig war ;)
Geier0815 schrieb:
Ok, in diesem speziellen Fall könnte ich wohl global auf "mqueue" matchen und es mit "mqueue, usbfs" ersetzen weil mqueue nur in diesem einen Fall überhaupt in der Datei vorkommt. Also ein
Code:
cat boot.localfs.orig | sed 's/mqueue/mqueue,usbfs/'g
auf die gesamte Datei ausführen. Wie sieht dann der bash-code aus? Und wie ist es wenn eindeutiger Match und Substitution in einer Zeile zu finden und nicht gleich sind?
im einfachsten Fall würde hier wirklich schon reichen. Ist aber riskant, wenn du nicht sicher sein kannst das die Datei nicht in der Zwischenzeit geändert ist.
Code:
sed 's/mqueue/mqueue,usbfs/'  boot.localfs.orig > boot.localfs

Du kommst in deinem Code auch nicht ohne sed aus. also mach es doch gleich richtig. ;)

Kurzeinweisung in sed
sed arbeitet immer Zeilenweise
sed kann aus Dateien lesen wenn sie als Optionen angegeben werden, ansonsten ließt es aus der Standardeingabe.
sed hat eine eigenen Befehlssatz, diesen musst du vor dem Zugriff der Bash schützen. deshalb steht üblicherweise dort hinter sed alles in ' eingeschlossen.
sed hat ein paar Optionen , die Wichtigsten für einen Anfänger sind "-n" und "-i" und "-e"
-e ist deshalb wichtig, da es auch weggelassen werden kann, es sagt nur das nächste sind dann die Sedbefehle. Man würde es benötigen wenn man mehrere sed-Befehle angeben möchte.

-n ist diese Option gesetzt, dann werden nur die Ergebniszeilen ausgegeben, die seperat oder impliziert "geprintet" werden, unbearbeitet Zeilen werden damit nicht mit ausgegeben.
ohne -n werden immer alle Zeilen wieder ausgegeben, auch wenn sie nicht bearbeitet und nicht verändert worden sind.

Die Ausgabe von sed erfolgt wenn nicht anders angegeben auf der Standardausgabe und werden von dort aus meistens in eine Datei geleitet.
Allerdings kann sed auch eine Eingabedatei direkt ändern. das heißt die Ausgabe wird in die selbe Datei geschrieben aus der gelesen wird.
Das macht die Option -i. ( Aber vorsicht erst alles ausprobieren und wenn es funktioniert dann kann man sich überlegen das eventuell so zu machen, aber wirklich erst wenn der sed-Befehl sauber funktioniert)

Also bis hier hin: ein Pseudo Beispiel
Code:
sed  -i 'SED_BEFEHE'   datei
sed soll die "datei" zeilenweise lesen die SED_BEFEHLE ausführen und das Ergebnis wieder in eine Datei "datei" schreiben.


Befehle
Meistens wird nur der "s" befehl benötigt , bedeutet weiter nichts als "suchen und ersetzten"

s ist der eigentliche Befehl
danach wird ein Trennzeichen erwartet meist wird "/" verwendet, kann aber jedes andere Zeichen genauso sein, das macht sich zB dann gut wenn dort Dateinamen bearbeitet werden müssen wo solche / drin sind. Dann kann man einfach ein anderes Trennzeichen wählen, es wird das genommen das hinter s steht. Sonnst müßte man in den regulären Ausdrücken jedes / als \/ schreiben

dahinter stehen jetzt die Suchoptionen in Form eines regulären Ausdruckes. hierbei ist zu beachten das es zeichen gibt die bei den regulären Ausdrücken Sonderfunktionen haben, wenn diese im gesuchten Text auftreten dann müssen sie mit einem \ entschärft werden

Dann kommt wieder das Trennzeichen(genau das selbe das hinter s festgelegt wurde)

Dann kommt der Ersetzungstext

Dann wieder das Trennzeichen

anschließend kann noch ein Flag folgen das bestimmt wie der Befehl arbeiten soll.
ohne Flag wird nur das erste Auffinden ersetzt ein g würde alle innerhalb der Zeile gefunden Ersetzungen vornehmen, und ein p würde explizied ein Print anstoßen
Zusammenfassung:
Code:
s/such/ersetze/g
ist der sedcode für durchsuche Zeile und ersetze jedes vorkommen von "such" durch "ersetze"
kann aber genauso gut mit dem selben Ergebnis auch geschrieben werden als
Code:
s~such~ersetze~g

in der Befehlszeile würde das dann vor der Bash geschützt werden und somit geschrieben werden müssen
Code:
sed 's/such/ersetze/g'   datei

Adresse
Soviel ist oftmals auch bekannt, aber jetzt kommt das interessante, vor jedem Befehl kann ein Suchmuster (eine Adresse) stehen, welches bestimmt auf welche Zeilen dieser Befehl arbeiten soll. steht vor dem Befehl also meist vor dem s nichts, werden alles Zeilen bearbeite also bei s durchsucht. Steht ein Adresse davor, dann muss erstmal die Adresse zutreffen und erst dann wird gesucht und ersetzt.

Die Adresse kann eine Zeilennummer (einfach eine Zahl) oder eine Zeilenbereich von bis (zahl,zahl) oder auch ein regulärer Ausdruck sein, dieser wird dann in Form "/Adresssuchmuster /" geschrieben.
Code:
sed '10,30s/such/ersetze/g'   datei
würde also nur die 10. bis 30. Zeile bearbeiten, alles andere wird einfach durchgereicht.
und
Code:
sed '/Auto/s/such/ersetze/g'   datei
würde nur die Zeilen bearbeiten in denen "Auto" vorkommt, und alle anderen Zeilen einfach 1zu1 passieren lassen.

in deinem speziellen Fall willst du nur diese eine Zeile bearbeiten
Code:
typeset -r tmpfs=tmpfs,ramfs,hugetlbfs,mqueue
Die Adresse dazu könnte jetzt als regulärer Ausdruck wie folgt aussehen
Code:
/typeset.*tmpfs=.*mqueue/
also in der Zeile soll "typeset" + "tmpfs=" + "mqueue" vorkommen, dazwischen kann stehen was will.
Das kannst du auch erst mal ausprobieren in dem du den sed-Befehl "print" p ausprobierst und die Option -n bei sed nimmst.
Code:
# sed -n '/typeset.*tmpfs=.*mqueue/p' boot.localfs.orig
typeset -r tmpfs=tmpfs,ramfs,hugetlbfs,mqueue


dann das Suchmuster, könntest du nehmen "mqueue" und als Ersetzung "mqueue,usbfs" wie gehabt.

Profis würden aber jetzt das Suchmuster in eine geschweifte Klammer setzen und die Klammer entwerten
Code:
s/\(mqueue\)/.............
dann kannst du im Ersetzungstext wieder auf den Inhalt diese Klammer zurückgreifen und sie als \1 wieder einfügen
Das hat den Vorteil du kannst hier verschiedes suchen und finden und an der richtigen Position wieder in den Ersetztext an der richtigen Position einsetzen.
Code:
sed '/typeset.*tmpfs=.*mqueue/s/\(mqueue\)/\1,usbfs/' boot.localfs.orig
das dann noch in eine neue Datei umgeleitet macht genau das was du willst, nur ohne Fehler

Die ganz Hasenwilden würden noch einen Schritt weiter gehen, sie würden sich nicht auf das "mqueue" verlassen, sondern würde das usbfs in der "typeset tempfs=" Zeile hinter dem = vor dem ersten Leerzeichen oder dem Zeilenende einfügen. Dort könnte dann auch noch ein Kommatar sein, oder auch nicht. Aber das würde dann schon einen Profilehrer verraten. ;) ;) ;)
Code:
sed '/typeset.*tmpfs=/s/^\(.*tmpfs=[^ ]*\)\(.*\)$/\1,usbfs\2/' boot.localfs.orig
noch besser wenn man sich nicht mal auf ein eventuelles Leerzeichen dahinter verlässt und auch ein Tab dort zulassen würde
Code:
sed  '/typeset.*tmpfs=/s/^\(.*tmpfs=[^[:space:]]*\)\(.*\)$/\1,usbfs\2/' boot.localfs.orig
aber das ist dann schon Oberliega soweit muss man es aber gar nicht treiben, aber wenn du dir die Zeile anschaust reguläre Ausdrücke ist nichts Neues für dich, also erhoffe ich, dass ich das genau genug erklärt habe, dass diese Zeile jetzt kein Böhmisches Dorf für dich ist.
Sonst gibts noch nachsitzen ;) ;) ;) ;) ;)
Alles was dort so verrückt aussieht sind nur die regulären Ausdrücke, die sind zwar enorm mächtig nur leider oft schwer lesbar.


robi
 

abgdf

Guru
Mein Tipp: Schreib's das in diesem Fall in Perl (kannst das ja auch vom bash-Skript aufrufen). Erspart Dir viel Mühe.
 
A

Anonymous

Gast
abgdf schrieb:
Mein Tipp: Schreib's das in diesem Fall in Perl (kannst das ja auch vom bash-Skript aufrufen). Erspart Dir viel Mühe.
Na dann zeig das mal in Perl, wetten das dort ne Menge Probleme zu erwarten sind und das ganze auf keinen Fall einfacher zu lesen ist als das sed. 10 Mal größer als die Sedzeile wird es in jedem Fall und erwartet auch noch ein installiertes Perl das nicht in jedem Fall immer Standard sein muss speziell wenn das script noch vor dem Ende der 100tigen Installation laufen soll.

Das Problem hier ist es handelt sich um eine Konfigurationsdatei die von der Bash eingelesen werden soll.
In dieser Datei können sich Konstrukte befinden die genau auf ein einmaliges Lesen durch die Bash ausgelegt sind. zB mit \ entwertete Zeichen Variablen und Kommentare und im dümmsten Fall sogar allerlei speziellen Schnick-Schnack mit Sonderzeichen die sich innerhalb und außerhalb von Klammern befinden können. Wird so eine Datei zerlegt und wieder zusammengebaut, kann es immer sein das dort mal was verloren geht oder verfälscht wird, mit den fatalen Folgen, das dann die bash beim einlesen Fehler sieht. Im frühem Bootvorgang ein sehr nettes Problem.

Es gibt hier eigentlich nur 2 vernünftige Wege, entweder mal 10 Minuten in den sauren sed-Apfel beißen oder sich hinsetzen und einen möglichst universellen Patch schreiben um die Datei mit patch zu ändern. Perl für solche Kleinigkeiten zu verwenden :schockiert: klar kann man auch Blatläuse mit Kanonen erledigen .... Entschuldigung ich verkneif mir jeden weiteren Kommentar. Aber heute muss man ja schon dankbar sein, wenn man überhaupt noch suggeriert bekommt, das Probleme die eigentlich noch nie zum Problem geworden sind noch ohne Cloud und ohne extra App überhaupt noch lösbar sind.

robi
 
OP
Geier0815

Geier0815

Guru
Code:
sed  '/typeset.*tmpfs=/s/^\(.*tmpfs=[^[:space:]]*\)\(.*\)$/\1,usbfs\2/' boot.localfs.orig
Hier darfst Du mir mal auf die Sprünge helfen.
Code:
/typeset.*tmpfs=/
ist der erste Match, sprich er soll nur Zeilen bearbeiten in denen dieser Term vorkommt.
Code:
s/^
bedeutet substituiere mir etwas was am Anfang der Zeile mit folgendem beginnt.
Code:
\(.*tmpfs=[^[:space]]*\)\(.*)$/

[EDIT]Viel geistigen Dünnpfiff entfernt[/EDIT]

Nun aber: Du suchst also beliebige Zeichen (auch Keine) gefolgt von tmpfs= und beliebigen Zeichen AUßER Whitespaces, auf die wieder beliebige Zeichen (auch Keine) bis zum Zeilenende folgen können und ersetzt dann per
Code:
\1,usbfs\2/' boot.localfs.orig
den ersten Ausdruck , also (tmpfs=mit beliebigen Zeichen bis zum ersten Whitespace) durch sich selbst mit angehängtem ,usbfs. Dann wird der Rest der Zeile (so vorhanden) noch dran gehängt und das Ganze in der boot.localfs.orig durchgeführt. Puuuh, da hatte ich jetzt dran zu Knabbern da ich das negierte Whitespace erst fehlinterpretiert hatte.

Aber mal eine andere Frage: Hast Du dir schon mal überlegt ein Buch zu schreiben? Zumindest ich kann mit deinen Erklärungen und Beispielen immer sehr viel anfangen und würde es wahrscheinlich kaufen. Ich Danke dir bis hierhin!
 
A

Anonymous

Gast
Geier0815 schrieb:
Puuuh, da hatte ich jetzt dran zu Knabbern da ich das negierte Whitespace erst fehlinterpretiert hatte.
Und im nach hinein, eigentlich ist es doch keine Hexerei, oder ;)
ein bischen Übung und noch ein paar Hinweise zu den Regex Besonderheiten von sed, und ähnliches und morgen solltest du solche Aufgaben selbst hinbekommen.
Geier0815 schrieb:
Aber mal eine andere Frage: Hast Du dir schon mal überlegt ein Buch zu schreiben? Zumindest ich kann mit deinen Erklärungen und Beispielen immer sehr viel anfangen und würde es wahrscheinlich kaufen. Ich Danke dir bis hierhin!
Ne das Bücherschreiben lasse ich lieber mal sein, einzelne Probleme zerlegen und versuchen Lösungswege so einfach wie irgend möglich zu beschreiben, ich glaube das kann ich nicht schlecht, aber doch nicht gleich in Buchform, in meiner Erklärwut kann ich bequem aus jedem noch so kleinem Problemchen ein eigenes Kapitel erschaffen und trau mich dann nicht auch nur ein einziges Wort zu streichen, da es ja in einem ganz seltenen Fall eventuell ja doch von Bedeutung sein könnte. ;) ;) ;) ;) ;)

robi
 

abgdf

Guru
robi schrieb:
Na dann zeig das mal in Perl, wetten das dort ne Menge Probleme zu erwarten sind und das ganze auf keinen Fall einfacher zu lesen ist als das sed.
Sicher?
Code:
perl -pe 'if (/tmpfs=/) {s/mqueue/mqueue,usbfs/}' /test/boot.localfs.orig >> /test/boot.localfs
Übrigens ist auch der "patch"-Befehl von Larry.
 
A

Anonymous

Gast
abgdf schrieb:
Code:
perl -pe 'if (/tmpfs=/) {s/mqueue/mqueue,usbfs/}' /test/boot.localfs.orig >> /test/boot.localfs
Übrigens ist auch der "patch"-Befehl von Larry.
Ist ja mal was ganz anderes als das was seit 35 Jahren dank http://senseis.xmp.net/?LeeMcMahon sed immer zuverlässig gemacht hat
Code:
sed -e '/tmpfs=/s/mqueue/mqueue,usbfs/' /test/boot.localfs.orig >> /test/boot.localfs
Wenn Apple, Google und Co. heute über so einen Zufall stolpern würden, die würden sich bestimmt die nächste 10 Jahre gegenseitig auf 100000000000000 Dollar verklagen. ;)

robi
 

abgdf

Guru
Klar, Perl hat sich jede Menge Syntax und Funktionalität von awk, sed und C "geliehen". Oder auch "übernommen". Schwer zu sagen, ob das die feine Art war.
Na ja, und dann wurden eben noch jede Menge Zusatzfunktionen eingebaut wie Module (CPAN), grafische Oberflächen (Perl/Tk, auch "übernommen", von Tcl), später (Perl 5) dann noch komplexe Datenstrukturen ("Array of Array") und objektorientierte Programmierung hinzugefügt.
Das alles machte Perl für Programmierer attraktiver als awk und sed allein, so daß viele dann eher Perl lernten und von dort aus wieder auf diese sed-Strukturen stießen.

Später kam Python (bzw. Guido), hat sich dieses Perl-Durcheinander angesehen und dort mal gründlich aufgeräumt (so jedenfalls meine Theorie). Dadurch sind die Leute inzwischen auch wieder ein bißchen von Perl abgekommen.

So ist halt der Lauf der Dinge, Sprachen entwickeln sich, neue werden entwickelt, und wenn sie gut sind, verdrängen sie die alten. Man kann natürlich auch noch in awk, sed, Turbo-Pascal, 6502-Assembler, Cobol und Fortran schreiben, wenn man will. Aber auf allzuviele Gleichgesinnte wird man da heute nicht mehr treffen.
 
A

Anonymous

Gast
abgdf schrieb:
So ist halt der Lauf der Dinge, Sprachen entwickeln sich, neue werden entwickelt, und wenn sie gut sind, verdrängen sie die alten. Man kann natürlich auch noch in awk, sed, Turbo-Pascal, 6502-Assembler, Cobol und Fortran schreiben, wenn man will. Aber auf allzuviele Gleichgesinnte wird man da heute nicht mehr treffen.

Ein Assembler Programmierer der einen grafischen Texteditor schreiben soll, würde das nie versuchen in Assembler zu machen, sondern sich eine passende Sprache suchen und diese lernen bevor er anfängt. Umgekehrt bin ich mir da nicht so sicher ob ein Java Programmierer nicht versuchen würde erstmal mit Java auf die CPU-Register zuzugreifen.
Meine, und nur meine ganz persönliche Erfahrung und Meinung (das liegt wohl an den Leuten mit denen ich zu tun habe und das stimmt so wahrscheinlich allgemein überhaupt nicht, und Anwesende sind natürlich wie immer vollkommen ausgeschlossen ;) ),
Einige Programmierer sind der Meinung sie beherrschen jetzt eine Sprache die einfach Alles kann, also machen sie jetzt auch Alles damit und merken oftmals gar nicht das das manchmal (schon mit einem etwas kritischen Blick erkennbar) vollkommen absurd ist, was sie dort machen.

Bleiben wir hier bei dieser einfachen Aufgabenstellung wie sie oben beschrieben wurde und spinnen diese mal ein bisschen weiter.
Es wird ein Postscript benötigt für ein Paket (welches sei mal dahingestellt). Darin ist eine Aufgabe enthalten die sed lösen kann. Perl hat eine sed Emulation die das aber genauso gut auch macht.
Wird das Paket mit diesem Script jetzt gepackt muss sicher sein, das alle Befehle die für die Abhängigkeiten des Paket Inhaltes und zur Ausführung der Scripte während des Installationsprozesses vorhanden sind. Also müsste perl oder sed in den Paketabhängigkeiten dieses Paketes mit aufgenommen werden vollkommen unabhängig ob es vom Inhalt des Paketes benötigt wird.

Wenn man sich jetzt nur mal den Unterschied von
"rpm -qR sed" und "rpm -qR perl" anschaut, ist sehr schön im Ansatz zu erkennen das Perl hier selbst auch noch mal eine Unmenge an zusätzlichen Abhängigkeiten mitbringt, die letztlich dazu führen würden die Abhängigkeiten für dieses Paket enorm aufzublähen, wenn in dem Paketinhalt nicht zufällig Perl sowieso verwendet würde.

Wenn dieses jetzt bei einer Distribution bei 100 Paketen gemacht würde, (Also irgendwo mal Perl verwenden obwohl es primitive Tools genauso könnten), haben bestimmt 95 davon im Inhalt keinen weiteren Bezugspunkt zu perl. Das währe wohl überhaupt kein Problem weil Perl ja dann mit seinen Abhängigkeiten nur ein mal installiert wird.
Ist aber etwas sehr kurz gedacht. :???:

Ändert sich nur ein einzelnes Paket das in den Abhängigkeitszweig von Perl aufgeführt ist, dann muss Perl auch neu kompiliert und gepackt werde und alle Pakete die eine Abhängigkeit zu Perl haben müssen danach auch neu kompiliert und gepackt werden. Sprich die 95 Pakete bei denen das "moderne" allmächtige perl nur deshalb in den Abhängigkeiten steht, weil es eben mal so "modern" ist, müssen sinnloser weise auch diese neu kompiliert und neu gepackt werden. Sind noch weitere Pakete von diesen Paketen abhängig, geht der ganze Spaß natürlich weiter. Dieser Prozess läuft heutzutage vollkommen automatisch auf den Build Servern der Distributionen ab.

Jetzt kommen 1 Million User in den Genuss das es 1000 Updates gibt, die die Pakete des halben Systems updaten. Und wo gibt es zB bei Opensuse erfahrungsgemäß und regelmäßig die schönsten Probleme? Doch wohl mehr oder weniger ausgelöst durch die Paketverwaltung und Paket Versionen wenn die User zusätzliche Paketquellen ohne vernünftige Priorität konfiguriert haben und somit gewollt oder ungewollt mehrere Alternativen ein und des selben Paketes in verschiedenen Paketquellen haben, und nach irgendwelchen updates dann plötzlich sich irgendwo ein Kuddel-Muddel eingestellt hat. Und wer würde denn jetzt schon vermuten, das ein Programm das so rein überhaupt nichts mit Perl zu tun haben kann, nach einem Perl Sicherheitsupdate plötzlich den Anbieter gewechselt hat.

Jetzt könnte ich genauso gut auch mal die Laufzeiteigenschaften der beiden in ihrer Wirkung vollkommen identischen Befehle vergleichen, Will ich mal darauf verzichten. CPU-Leistung und Speicher sind heute so was von billig, dass sie hier in diesem Fall bei der Installation und der winzigen Aufgabe absolut vernachlässigbar sind.

Aber ich sage ganz ehrlich, ich währe echt froh wenn es noch ein paar mehr Gleichgesinnte geben würde, die nicht leichtsinnig der Meinung sind das überaus mächtige und universelle Programme das alleinige Allheilmittel und einzig Wahre auch für die winzigste Aufgaben ist, und bei der Wahl der Waffen durchaus öfter gezielt mal auf harmlosere Alternativen zurückgreifen würden. Und das nicht nur aus diesem hier geschilderten Grund von unnötigen aufgequollenen Paketabhängigkeiten.

robi
 

abgdf

Guru
Sicher, überflüssige Paketabhängigkeiten sind grundsätzlich nicht gut.
Aber Perl ist doch eigentlich auf jeder Linux-Distribution verfügbar. Sogar auf Puppy (seit 2006), das insgesamt nur ca. 125 MB groß ist.
Ich hab' halt Perl gelernt, und das hat mir viel Spaß gemacht, jedenfalls mehr als bash, weil man da nicht dauernd auf die blöden Leerzeichen Rücksicht nehmen muß und Arrays so funktionieren, wie man sich das vorstellt.
Ein schöner Grundsatz von Perl ist z.B. die Toleranz (TMTOWTDI). Wenn Du die Aufgabe mit sed gut gelöst bekommst, gut. Wenn ich von einer Perl-Sicht her komme und das Problem so lösen kann, auch gut.
Es ist doch nur ein kleines Skript. Davon werden schon keine Distributionen in Paketabhängigkeiten versinken und dadurch untergehen. ;)
 

framp

Moderator
Teammitglied
Interessante OT Diskussion ...

Aus PaketBauerSicht ist es sicherlich erstrebenswert möglichst wenig Paketabhänngigkeiten zu haben. Dass Perl in jeder Distro drin ist löst das angesprochene Bauproblem nicht. Aus Tool/ApplikationsschreiberSicht ist er erstrebenswert eine möglichst mächtige Sprache für das Problem einzusetzen, so dass es schnell eine korrekt funktionierende Lösung gibt die auch über eine längere Zeit wartbar ist. Aus dieser Sicht ist es von Vorteil Perl in jeder Distro zu haben (sofern Perl die richtige Sprache für das Problem ist - es gibt ja noch eine Menge andere :roll: ).

Kurzum: So wie ich es sehe habt Ihr zwei verschiedene Brillen auf und beide haben ihre Daseinsberechtigung.

robi2 schrieb:
...Umgekehrt bin ich mir da nicht so sicher ob ein Java Programmierer nicht versuchen würde erstmal mit Java auf die CPU-Register zuzugreifen...
Glaube ich nicht. Der weiss ja gar nicht was ein CPU-Register ist :fies:
 
OP
Geier0815

Geier0815

Guru
Zwei Anmerkungen von mir dazu: Die Frage nach der "best practice" hat sich eindeutig geklärt, nämlich: Suchen und ersetzen macht man mit sed, es sei denn etwas tabellenartiges wird verarbeitet, dann dürfte awk besser sein. Man muß auch in bash nicht Alles per Hand machen wenn Tools dies auch können. Wie jetzt meine Versuche eine Schleife zum Suchen zu bauen.

Zweitens: Unter Perl wäre ich über eine Variable im Listenkontext und einer anschließenden foreach-Schleife an das Problem heran gegangen, was, wie hier bewiesen wurde, auch nicht "best practice" gewesen wäre, aber zumindest funktioniert hat.

Also nicht nur über bash sondern gleichzeitig über perl etwas gelernt. Ihr seid die Besten!
Zum Problem bash oder perl noch die Erklärung: Ich bin bei uns der Einzige der mit perl gebastelt hat. Von daher ist in diesem Fall bash die bessere Wahl, da die Jungs bei uns die mit meinem Code überhaupt etwas anfangen könnten die Klappe runter fallen lassen wenn ein script mit #!/bin/perl anfängt.
 

framp

Moderator
Teammitglied
Geier0815 schrieb:
... da die Jungs bei uns die mit meinem Code überhaupt etwas anfangen könnten die Klappe runter fallen lassen wenn ein script mit #!/bin/perl anfängt.
Das hatte ich im obig angesprochenen Thema Wartbarkeit vergessen. Der beste wartbare Code nützt nichts wenn dazu nur eine Person in der Lage ist bzw gewillt ist.
 

abgdf

Guru
Geier0815 schrieb:
Zweitens: Unter Perl wäre ich über eine Variable im Listenkontext und einer anschließenden foreach-Schleife an das Problem heran gegangen, was, wie hier bewiesen wurde, auch nicht "best practice" gewesen wäre, aber zumindest funktioniert hat.
Vielleicht als Ergänzung noch, was es mit diesem Perl-Einzeiler auf sich hat, denn das ist nicht offensichtlich.
Zunächst kann man mit der "-e"-Option einen Perl-Befehl ausführen:
Code:
perl -e 'print "Hallo Welt!\n";'
Das "-p" (in Verbindung mit "-e") ist sehr viel eigenartiger: Es erwartet am Ende eine Datei, geht diese Zeile für Zeile durch und gibt diese aus. Dabei wird auf jeder Zeile der Datei der Code ausgeführt, der mit "-e" übergeben wurde. Also
Code:
perl -pe ' ' file.txt
gibt den Inhalt von "file.txt" aus. (Dazu wird von "-p" intern etwas Perl-Code verwendet, der in "perldoc perlrun" dargestellt ist.)
Während "-pe" die Datei Zeile für Zeile durchgeht, wird jede Zeile in der Standardvariablen "$_" abgelegt. Gleichzeitig beziehen sich reguläre Ausdrücke, für die keine besondere Variable angegeben ist, ebenfalls auf "$_".
Ich hätte also eigentlich schreiben müssen:
Code:
if ($_ =~ "tmp=") {
    $_ =~ s/dies/das/;
}
Wie gesagt, wird das "foreach() {}" drum herum von "-pe" impliziert (Edit: Genaugenommen ist es ein "while(<>) {}").
So kommt am Ende nur noch
Code:
perl -pe 'if(/tmp=/){s/dies/das/}' file.txt
heraus. Was dann wiederum sed entspricht.

Diese Einzeiler können allerhand. Man braucht aber wohl einige Übung, um gute, komplexe zu schreiben. Meist fange ich auch mit einem längeren Perl-Skript an, und kürze das dann Schritt für Schritt auf den Einzeiler zusammen. Das geht (bei mir) nicht gerade schnell, auch wenn am Ende das Ergebnis schön kurz aussieht. ;)
 
Oben