bash: function die eine Zeile modifiziert

  • steffen_b


    ...ziemlich genau dieser Code ging mir heute Morgen auch durch den Kopf. Ich habe ihn dann aber nicht aufgeschrieben. Wie ich später festgestellt habe, würde er jedoch die Anforderungen nicht erfüllen. Aber das liegt an meiner schlechten Beschreibung der Anforderung.


    Deswegen möchte ich die Anforderung noch etwas präzisieren:
    Die Variabel $2 beinhaltet die gesamte neue Zeile !!
    D.h. wird $1 in einer Zeile gefunden, so wird die betreffende Zeile gegen $2 ausgetauscht !!
    Imho geht das mit $( / / ) nicht !!


    Aber ich habe die Funktion $(variable//old/new) dennoch sinnvoll einsetzen können:


    Verbesserte Version der Funktion, bei der das Zeichen / nicht mehr geschützt werden muss:



    oder auch kürzer (ungetestet):

    Code
    function modLine ()
    # $1 search string
    # $2 new line
    # $3 path and filename
    {
            sed -i "/^[ tab]*\#/!{/^.*${1//\//\\/}/s/\(.*\)/${2//\//\\/}/}" ${3}
    
    
    }


    Mir fehlt momentan noch die Phantasie, wie man es bewerkstelligen sollte, dass man ein " nicht schützen muss. Ich werde es aber auch noch mal probieren. Vielleicht funktioniert die gleiche Methode hier auch.


    Gruß
    Wicky

  • Er ersetzt schon die ganze Zeile dank der * davor und dahinter, das setzen des IFS muss an den Anfang des Skripts nicht in die Funktion, dann kann er auch das quoting besser verdauen. Einzig bei der Übergabe müsste man so das Quoting schützen, aber da hilft dir auch das beste Skript nicht, da die Parameter bei der Übergabe von der Bash ausgewertet werden, und somit verschwinden. Das quoting is IMHO das komplizierteste an der Bash überhaupt ;)


    Wie gesagt, vielleicht kann cooper hier sich nochmal melden , bezgl der Kommentarzeilen.

    VDR User: 87 - LaScala LC14B - LG/Phillipps 6,4" VGA Display | Asrock H61/U3S3 | G630T | 1x 16GB Mobi Mtron 3035 1x WD 750GB 2,5" |1x L4m DVB-S2 Version 5.4

  • Zitat

    Original von steffen_b
    Er ersetzt schon die ganze Zeile dank der * davor und dahinter...


    sehr schön.

    Zitat

    .., das setzen des IFS muss an den Anfang des Skripts nicht in die Funktion, dann kann er auch das quoting besser verdauen.


    ...so wie ich es z.Z. sehe, ist das eventuell eine Einschränkung, die man nicht akzepiteren kann.

    Zitat


    Einzig bei der Übergabe müsste man so das Quoting schützen, aber da hilft dir auch das beste Skript nicht, da die Parameter bei der Übergabe von der Bash ausgewertet werden, und somit verschwinden. Das quoting is IMHO das komplizierteste an der Bash überhaupt ;)


    Zweimal Jepp !!


    Zitat


    Wie gesagt, vielleicht kann cooper hier sich nochmal melden , bezgl der Kommentarzeilen.


    ...jepp, würde mich auch freuen.


    Kommt deine Funktion eigentlich mit der neuen Zeile "hallo /etc/blabla/" klar?


    Eigentlich sollten doch die / hier Probleme machen !!


    Gruß
    Wicky

  • ... es ist doch eigentlich gar nicht so schwer, wenn man die Bash ein kleinwenig kennt:


    Durch Einsatz der IFS ist das ganze Problemlos mit Leerzeichen & Co. zu verwenden, externe Programmaufrufe gibt's ebenfalls nicht, und wir brauchen auch keine temporäre Datei, weil wir erst einlesen und dann ausgeben.


    Solltet ihr Erklärungen brauchen, warum das funktioniert, müsst ihr euch nochmal melden.


    Viele Grüße, Mirko

  • cooper


    Die von dir vorgeschlagene Funktion funktioniert nicht so wie gedacht.


    Hier die Implementation:


    Hier zwei Beispiele für einen Aufruf:


    Aufruf1:

    Code
    modLine2 "COMMANDLINE=" "COMMANDLINE=\"-C /etc/nvram-wakeup.conf --directisa\"" "$file"
    modLine2 "FORCE_REBOOT=" "FORCE_REBOOT=\"yes\"" "$file"


    Aufruf2:

    Code
    modLine2 "COMMANDLINE=" "COMMANDLINE="-C /etc/nvram-wakeup.conf --directisa"" "$file"
    modLine2 "FORCE_REBOOT=" "FORCE_REBOOT="yes"" "$file"


    Mit der von mir vorher geposteten Version lässt sich der Aufruf1 nutzen.
    Dein Code ändert die Zeile, die COMMANDLINE enthält gar nicht, und bei der Zeile, die FORCE_REBOOT= enthält, wird nur ein yes angehängt.


    Gruß
    Wicky


    P.S. Ich hoffe ich habe mich nicht vertestet. :versteck

  • Code
    modLine2 'COMMANDLINE=' 'COMMANDLINE="-C /etc/nvram-wakeup.conf --directisa"' "$file"
    modLine2 'FORCE_REBOOT=' 'FORCE_REBOOT="yes"' "$file"

    So sollte es auch gehen. IFS wirkt halt nur auf das Skript.


    Zitat

    2. sed-Komando: Hier sind doch die Variablen gequotet !!

    Nein, es ist alles gequotet bis auf die Variablen! So sollten sie es sein (ungetestet).

    Code
    sed -i '/^[[:blank:]]*#/!{/^.*'"${arg[1]}"'/s/\(.*\)/'"${arg[2]}"'/}' ${arg[3]}

    Gruss
    SHF


    Einmal editiert, zuletzt von SHF ()

  • Zitat

    Original von SHF

    Nein, es ist alles gequotet bis auf die Variablen! So sollten sie es sein (ungetestet).

    Code
    sed -i '/^[[:blank:]]*#/!{/^.*'"${arg[1]}"'/s/\(.*\)/'"${arg[2]}"'/}' ${arg[3]}


    ...das hatte ich auch schon mal getestet, aber dann wird halt ${arg[2]} eingesetzt und nicht der Inhalt der Variablen.


    Aber sicherheitshaber werde ich es die Tage nochmals ausprobieren.


    OT:
    Wäre es nicht vielleicht eine gute Idee, wenn man im VDR-wiki eine Rubrik für Code-Schnipsel aufmachen würde?


    Aus diesen Schnipseln könnte man sich dann bedienen und seine Skripte im Baukastenprinzip aufbauen. Ich habe mittlerweile auch schon so manche Funktion, die ich immer wieder einsetze.
    Das wären z.B. checkRoot oder checkVDRrunning.


    Was haltet ihr davon?


    Gruß
    Wicky

  • Zitat

    ..das hatte ich auch schon mal getestet, aber dann wird halt ${arg[2]} eingesetzt und nicht der Inhalt der Variablen.


    Aber sicherheitshaber werde ich es die Tage nochmals ausprobieren.

    Bei den doppelten Anführungszeichen(") sollte die Variabelen-Ersetzung eigentlich funktionieren. Bei den Einfachen(') nicht.


    Zitat

    Aus diesen Schnipseln könnte man sich dann bedienen und seine Skripte im Baukastenprinzip aufbauen. Ich habe mittlerweile auch schon so manche Funktion, die ich immer wieder einsetze.
    Das wären z.B. checkRoot oder checkVDRrunning.


    Was haltet ihr davon?

    Coole Idee, programmieren mit der mittleren Maustaste so zu sagen :D.
    So eine Sammelung kann einem bei Standard-Sachen ne Menge Arbeit und Fehlerquellen ersparen, besonders dann, wenn man nur ab und an mit Shell-Skripten umgeht. Man muss das Rad ja nicht jedes mal neu erfinden.

    Gruss
    SHF


  • Zitat

    Original von SHF


    Coole Idee, programmieren mit der mittleren Maustaste so zu sagen :D.
    So eine Sammelung kann einem bei Standard-Sachen ne Menge Arbeit und Fehlerquellen ersparen, besonders dann, wenn man nur ab und an mit Shell-Skripten umgeht. Man muss das Rad ja nicht jedes mal neu erfinden.


    ...genau so dachte ich es mir 8)


    Klar, man wir niemals einen Baukasten haben, der auf für alle Probleme eine Antwort parat hält. Und wenn man ihn hätte, dann würde das Suchen länger dauern als das neu schreiben.


    Aber bestimmte Dinge macht man immer wieder, und dann ist ein Baukasten doch recht nützlich.


    Ach da fällt mir noch ein Schnipsel ein, den ich nutze: getVDRversion und getVDRapiVersion


    Gruß
    Wicky

  • Dann wäre der Baukasten nicht so interessant, sondern eher eine "Bash-Lib", also eine Sammlung von Funktionen die immer wieder gebraucht werden und die man in jedem Skript includen kann, wenn man es braucht, fraglich ob man sich dann nicht selbst etwas zusammenschreibt bevor man sich es raussucht und einem dann die Implementierung nicht gefällt ;) Aber Standardlösungen für immer wiederkehrende Probleme ist mit Sicherheit nicht schlecht, zumindest für BASH einsteiger, nach einer bestimmten Zeit hilft nur benutzen, benutzen , benutzen und trotzdem immer wieder was dazulernen :D



    Um zum Topic zurück zu kommen: Mit einem kleinen Kunstgriff müsste auch dem Slash Problem beizukommen sein. Ich denke dabei an sowas zB:


    Code
    Pattern=${1//\/\\\/}
    Replacement=${2//\/\\\/}


    Also jeden Slash bei der Zuweisung Escapen. Erklärung:
    ${1 - Bis hierher klar denke ich
    // - alle Vorkommnisse ersetzen
    \/ - Escapter slash (so das der slash gefunden wird als Zeichen)
    / - ersetzen mit
    \\ - escapter backslash
    \/ escapter slash
    } - wieder klar ;)


    Alles klar ? ;)


    Das ganze lässt sich mit Sicherheit auch über awk, sed , perl, python und Ruby , $PROGRAMMIERSPRACHE erledigen :D. Ich fands jetzt nur spannend das mal mit bash only zu machen, da ich sonst auch vorschnell zu awk und sed bzw PERL greife. Ist mir bisher noch nicht aufgefallen das das so einfach möglich ist.

    VDR User: 87 - LaScala LC14B - LG/Phillipps 6,4" VGA Display | Asrock H61/U3S3 | G630T | 1x 16GB Mobi Mtron 3035 1x WD 750GB 2,5" |1x L4m DVB-S2 Version 5.4

  • Hi,


    ich versteh' das net so recht,
    Cooper's Script funzt doch;
    durch

    Code
    echo ${Line/"$Pattern"/$Replacement} >> ${Filename}


    geht das auch z.B. mit "\" im Suchstring.


    Nur "*" als Zeile sollte nicht im File sein :D

  • Hallo,
    das Skript von Mirko funktioniert schon. Es muss nur richtig benutzt werden.


    file:

    Code
    COMMANDLINE=
    FORCE_REBOOT="no"


    Aufruf:

    Code
    modLine2 'COMMANDLINE=' 'COMMANDLINE="-C \/etc/nvram-wakeup.conf --directisa"' "$file"
    modLine2 'FORCE_REBOOT="no"' 'FORCE_REBOOT="yes"' "$file"



    Alternativ geht bei mir aber auch so was, das Problem mit dem Asterix ist da aber noch nicht gelöst:


    Gruss
    Marc

  • Zitat

    Original von steffen_b
    Dann wäre der Baukasten nicht so interessant, sondern eher eine "Bash-Lib", also eine Sammlung von Funktionen die immer wieder gebraucht werden und die man in jedem Skript includen kann, wenn man es braucht, fraglich ob man sich dann nicht selbst etwas zusammenschreibt bevor man sich es raussucht und einem dann die Implementierung nicht gefällt ;) Aber Standardlösungen für immer wiederkehrende Probleme ist mit Sicherheit nicht schlecht, zumindest für BASH einsteiger, nach einer bestimmten Zeit hilft nur benutzen, benutzen , benutzen und trotzdem immer wieder was dazulernen :D


    ...zulu hat sich eine solche Bash-Lib für sein x-vdr gebastelt. Ich habe da noch nicht so intensiv reingeschaut, aber da dürfte sich so manche Anregung finden lassen.



    ...ja, es ist alles klar, denn genau so habe ich doch das / Problem bereits vor Tagen mit der sed Zeile gelöst. Siehe weiter oben mein Post. :haehaehae


    Ich glaube, ich werde mir aber Mirkos Vorschlag nochmals genauer anschauen müssen, da ich wohl den Ansatz lediglich falsch verwendet habe. Außerdem muss auch noch die / Behandlung integriert werden.


    Gruß
    Wicky


  • Die Funktion muss aber auch funktionieren, wenn lediglich folgender Aufruf verwendet wird:

    Code
    modLine2 'FORCE_REBOOT=' 'FORCE_REBOOT="yes"' "$file"


    Ich werde es die Tage testen.


    zulu
    Du hast doch nichts dagegen, wenn ich und andere Ideen bei x-vdr aufgreifen. Ich suche z.B. noch nach einer schönen Backup function. Vermutlich wird man hierfür wohl tar und eine Liste von Dateien verwenden, die zu sichern sind. Aber das ist ein neues Thema.


    Gruß
    Wicky

  • Servus Wicky,


    die Parameter bei deinem Aufruf meines Scripts waren halt nicht OK, deswegen hat es nicht so funktioniert wie du wolltest.


    Der erste Parameter ist ein Pattern, jedenfalls hab ich das deiner Beschreibung entnommen. Wenn du also nach einer Zeile suchst, die mit "FORCE_REBOOT=" anfängt und dann beliebiges folgen kann, musst du das Pattern '#FORCE_REBOOT=*' verwenden: Das Pattern beginnt am Anfang der Zeile und nach dem Gleichheitszeichen kann beliebiges folgen.


    Verwendest du hingegen nur 'FORCE_REBOOT=*' als Pattern, passt das auch auf MY_FORCE_REBOOT und setzt auch diesen Config-Eintrag neu. Noch schlimmer ist es, wenn du das Sternchen weg lässt, also nur 'FORCE_REBOOT=' als Pattern benutzt -- weil dann fügt das Script den neuen Wert einfach vor die vorhandenen Werte ein (insert at first), anstatt die Zeile komplett neu zu setzen.


    Sollte dir das momentane Verhalten des Scripts nicht gefallen, weil z.B. das Pattern immer am Anfang der Zeile beginnen und bis zum Ende gehen soll, kannst du einfach folgendes schreiben:

    Code
    echo ${Line/#${Pattern}*/${Replacement}} >>${Filename}


    Wenn du zudem das Gleichheitszeichen sparen willst, tutst du das noch vor das Sternchen und kannst in Zukunft "FORCE_REBOOT" schreiben.


    Und zum Abschluss nochmal ein korrekter Aufruf für COMMANDLINE bei meinem unveränderten Script:

    Code
    name '#COMMANDLINE=*' 'COMMANDLINE="-C /etc/nvram-wakeup.conf --directisa"' $file


    Viele Grüße, Mirko

  • Hallo,

    Code
    Du hast doch nichts dagegen, wenn ich und andere Ideen bei x-vdr aufgreifen.


    Nö, haut rein :)


    @ Mirko


    Zitat

    echo ${Line/#${Pattern}*/${Replacement}} >>${Filename}


    Cool, kann ich auch gut gebrauchen. Kannst du bitte noch kurz erklären, was dabei die geschweiften Klammern machen?



    Gruss
    Marc

  • Zitat

    Original von zulu
    Kannst du bitte noch kurz erklären, was dabei die geschweiften Klammern machen?


    Klar:

    Code
    ${Variable} = $Variable
    ${Variable}ABC = $Variable"ABC" != $VariableABC


    Kurzum: In den geschweiften Klammern stehen die Variablennamen, und sie sind gegen weiterführende Zeichen geschützt, die danach folgen. Es ist eben ein Unterschied, ob die Variable "Variable" heißt und danach "ABC" folgt, oder ob es sich um die Variable "VariableABC" handelt.


    Man sollte sich die Klammer-Schreibweise grundsätzlich angewöhnen, das spart einem einiges an Debugging-Arbeit.


    Viele Grüße, Mirko

  • Zitat

    Originally posted by steffen_b

    Code
    Pattern=${1//\/\\\/}
    Replacement=${2//\/\\\/}


    Da ist ein kleiner typo drin: Es muss ${1//\//\\/} sein, dh. ein / muss ein \ werden. Die Erklärung danach ist aber richtig.


    Gruß,


    Udo

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!