Mac Bash Script zum URL Schreiben / entfernen in /etc/hosts sed regex klappt nicht ganz

Hinweis: In dem Thema Mac Bash Script zum URL Schreiben / entfernen in /etc/hosts sed regex klappt nicht ganz gibt es 11 Antworten auf 2 Seiten. Der letzte Beitrag () befindet sich auf der letzten Seite.
  • Hallo Leute,


    ich bastle schon den ganzen Nachmittag an einem Shell Script, dass mir etwas Arbeit erleichtern soll. Auf meinen Entwicklungsrechner (MacBook mit Mac OS X) gibt es keinen DNS-Server. Ich editiere jedes mal händisch die /etc/hosts Datei. Um das ganze etwas bequemer zu gestalten, wollte ich nun die Schritte in ein Bash Script auslagern:

    Das Hinzufügen von Einträgen (Funktion doAdd) klappt, das Löschen (Funktion doRemove) jedoch nicht.
    Ich hab es einzeln getestet und ich kann definitiv sagen, dass es am Regulären Ausdruck liegt, der anscheinend noch falsch formuliert ist.


    Es soll im Prinzip folgendes beim Löschen der Zeilen aus /etc/hosts tun:
    - Suche nach der Domain mit oder ohne führendes www.
    - ersetze den mehrzeiligen String bei Vorkommnissen durch nichts (= regex Löschen)
    - schreibe die generierte Ausgabe via sed wieder zurück in die selbe Datei (/etc/hosts)


    Ich bin mir auch nicht sicher, ob das mit dem String Concat so passt. Soviel ich weiß sorgen einfache Anführungszeichen für keine Variableninterpretation und doppelte schon. Deswegen habe ich mehrere hintereinander so geschrieben beim eigentlichen Ausführungskommando. Bei einem kurzen Test funktionierte das jedoch wie so dargestellt.


    Über Tipps, Verbesserungsempfehlungen wäre ich sehr dankbar. Ich bin schließlich noch Bash Scripting Anfänger.



    MFG


    derwunner

    Diese Signatur ist derzeit nicht verfügbar.

    Für den Inhalt des Beitrages 107602 haftet ausdrücklich der jeweilige Autor: derwunner

  • Das ist unlesbar.
    Ich werde das nicht genauer angucken.
    Achte auf anständige Einrückung.


    Vom Überfliegen:
    Du scheinst mit [www] nach einem String www zu suchen. Dazu verwendest du eine Zeichenklasse []
    Dein Regulärer Ausdruck [www] lautet übersetzt : Suche an genau dieser Stelle ein Zeichen "w", oder ein Zeichen "w", oder ein Zeichen "w". Also redundanter Unsinn.
    Du willst den Regulären Ausdruck www, der übersetzt lautet: Suche an genau dieser Stelle ein Zeichen "w", gefolgt von einem Zeichen "w", gefolgt von einem Zeichen "w".
    Willst du das mit einer Zeichenklasse suchen, brauchst du einen Quantifikator: [wW]{3},was übersetzt lautet: Suche an genau dieser Stelle drei Zeichen hintereinander, die entweder ein "w" oder ein "W" sind. (Deine URLs könnten auch mit Großbuchsaben aufgerufen werden).
    Und du wirst bei sed meist die Option -r für "verwende erweiterte RegEx" wollen.


    Außerdem ist dir ein grundlegendes Konzept der Shellprogrammierung nicht klar:
    Es gibt keine Globalen Variablen! Es gibt keine lediglich in Funktionen locale Variablen.
    Variablen können in Subshells exportiert werden, oder nicht, sie können BASHintern sein, oder nicht und
    sie können readonly oder nicht sein. Aber es gibt KEINE globalen Variablen. Erst recht nicht bei zur Aufrufzeit übergebene Optionen oder Argumenten!


    (Das Forum ist auch ziemlich schlecht gewählt. Sollte ein Mod verschieben) --> verschoben ... Alero

  • @Berichtigung: Die sed Option -r kann man nicht auf STDIN anwenden, musste deswegen -e nehmen. Ich wollte nach dem String "www." am Zeilenanfang suchen. Der darf einmal oder keinmal vorkommen. Wenn man ein ganzes Wort matchen will, dann muss das denke ich als Set ausgedrückt werden (findet man jedenfalls überall im Internet so). Würde ich nach einem einzelnen Character "w" suchen am Zeilenanfang, dann würde ich Dir recht geben. Das brauche ich aber nicht.
    Wegen der Forenauswahl: Es gab nur "andere Linux Distributionen", nicht "andere *NIX Systeme", deswegen habe ich es unter Offtopic gepostet. Ich erachtete das für das passenste Forum.
    Zu dem "Es gibt keine globalen Variablen": Mag sein, das Internet hatte mir jedoch gesagt, dass die Funktionsparameter keine Namen mehr haben, sondern $1, $2, $3 sind. Das mag hier vielleicht verwirrend erscheinen, weil ich $2 zwei mal verwende, das aber jedoch unterschiedliche Variablen Kontexte sind, also zwei völlig unterschiedliche Variablen, obwohl sie gleich heißen.
    Ich hatte für so ziemlich alles komplexere gegoogelt. Das Skript ist das Ergebnis davon.

    Diese Signatur ist derzeit nicht verfügbar.

    Für den Inhalt des Beitrages 107605 haftet ausdrücklich der jeweilige Autor: derwunner

  • Deine Argumentation ist ziemlich schräg.


    Erstens gibt es ein Forum für Shellprogrammierung
    Dass man für STDIN kein -r nehmen könne, ist nackter Blödsinn.
    Es widerspricht allen Grundsätzen von Linux.


    Probier das hier:

    Bash
    echo "ich ging so doof" | sed -rn 's/(.*)([g]{1}[i][ng]{2})(.*)/\Ende:\3; Anfang: \1; Mitte: \2; auf deutsch: \1 bin \3/p'


    Die Option -e(xpression) sagt lediglich, dass der folgende String ein Sedausdruck ist. Damit kann man, da diese Option mehrfach erlaubt ist, schlicht mehrere solcher Miniscripte hintereinander ausführen lassen. Meist schreibt man da lieber ein mehrzeiliges sed- Script, was wesentlich leichter les- und wartbar ist.


    Ich habe dein Ansinnen am Anfang den String www zu finden erkannt, und alle korrekten Möglichkeiten genannt, wie man das korrekt formulieren kann. Dein RegEx ist falsch. Er findet w.halligalli.de aber nicht ww.halligalli.de oder gar http://www.halligalli.de
    Dass das bei dir klapprig dennoch zu klappen scheint, liegt an dem vorangehenden .+. Das frisst greedy die w's weg.
    Es funktioniert eher zufällig.


    Die Bash, ist die Bash. Ob die auf einem BSD Derivat, einem echten Unix oder einem Linux läuft, ist ziemlich egal.
    Und eine Funktion wird auch NICHT in einer Subshell ausgeführt. Es gibt einige Anweisungen, die ein Subshell brauche. Eine Pipe um Beispiel.
    Die Bash Variable SHLVL enthält das SHellLeVeL, die Aufruftiefe.
    Und die Variable BASH_SUBSHELL das Level der Subshell(s).


    Gib einfach mal in einer Konsole das hier ein echo $SHLVL Und rufe danach in dieser Instanz das folgende Script auf:



    Und wenn du jetzt dieses kleine Script ausführst, statt im Internet selbstständig irgendwelche Gegenthesen zu googlen, könntest du dich von der Richtigkeit meiner Aussagen überzeugen. Ich bin nämlich hier das richtige Internet.


    In den Shells waren die Optionen und Argumente, die man beim Aufruf eines Scriptes, oder Funktionen in einem Script angeben kann, schon immer positionelle Parameter und wurden schon immer mit $1 bis $9 und ${10} usw. angesprochen.
    In allen Systemen, in allen Shells.
    Das war schon immer so.
    Schon länger, als du Laufen kannst.


    Es wäre übrigens sinnvoller, wenn du einfach das Ziel des Scriptes beschreiben würdest. Dann könnte man dir auch effektive Lösungen nennen. Alleine die ParameterExpansions der Bash selbst könnten dir einen Großteil der vermuteten Absichten locker ausführen, ohne erst externe Programme, wie sed, oder grep bemühen zu müssen.


    Aber du fabulierst ja lieber Unsinn, statt wenigstens den unlesbaren Code anständig zu formatieren.


    Insgesamt glaube ich eh nicht, dass so ein Script wirklich Sinn macht. Was willst du denn wirklich auf welchem Webserver testen?
    Ich denke, dass eine vernünftige Konfig von vhosts locker reichen würde.

  • Sorry, das Ausprobieren dauert noch etwas, weil mir der Mac nicht immer zur Verfügung steht.


    Ich habe jedenfalls schonmal den Regex zum Suchen nach www. angepasst, und bereits erfolgreich getestet: Online regex tester and debugger: PHP, PCRE, Python, Golang and JavaScript


    Und ja, @Berichtigung Du hast recht, die Formatierung sieht nicht gut aus. Das muss wohl beim Copy & Paste verloren gegangen sein. Werde ich versuchen zu berichtigen.

    Diese Signatur ist derzeit nicht verfügbar.

    Für den Inhalt des Beitrages 107787 haftet ausdrücklich der jeweilige Autor: derwunner

  • Ich denke es ist besser, wenn ich nicht meinen Thread Eröffnungs-Post bearbeite, sondern hier den Fortschritt in einem eigenen Post schreibe, damit es chronologisch nachvollziehbar bleibt. Sonst checkt es vielleicht der ein andere nicht.


    Also, wie gesagt habe ich den Regex angepasst. Die sed Option -r gibts bei mir nicht. Dafür scheint aber -E das selbe zu tun. Großes E ist bei mir erweiterter Regex und kleines e normaler Regex. Leider funktioniert das so nach aktuellem Stand immer noch nicht ganz. Er sagt mir, dass es nichts zu tun gäbe beim Löschen Fall, wenn ich sicher bin, dass die Zeile(n) in der /etc/hosts vorhanden ist (sind):


    Jetzt sollte auch die Einrückung gut aussehen, das heißt wie bei mir. Das Skript nennt sich chhosts.sh und wird folgendermaßen aufgerufen:

    Code
    ./chhosts.sh rm www.example.com

    Oder zum Hinzufügen:

    Code
    ./chhosts.sh add www.example.com


    Für Lazy Tipper:

    Code
    ./chhosts.sh add example.com


    Oder:


    Code
    ./chhosts.sh rm example.com

    Diese Signatur ist derzeit nicht verfügbar.

    Für den Inhalt des Beitrages 107788 haftet ausdrücklich der jeweilige Autor: derwunner

  • Es ist einfach nicht sonderlich sinnvoll zu glauben, ein GNU- Befehl würde auf einem angebissenem Apfel andere Optionen verstehen, als auf einem Linux.
    Diese GNU-Befehle sind überall gleich.


    In diesem Fall verwechselst du zwei Optionen von zwei Befehlen.
    grep kennt -E und schaltet damit die erweiterten RegExes ein.
    sed macht das mit -r


    Früher waren es tatsächlich verschiedene greps, die installiert waren. Es gab ein grep, ein egrep und ein fgrep.
    Heute macht das alles grep. Deshalb findet sich diese Option auch nicht in man grep, sondern in man fgrep
    Das ist eine historische Altlast und hat nichts mit der Platform zu tun.


    Und bei openSUSE sind diese Option kaum zu finden, wenn man deutsche Manpages dafür installiert hat.
    Ich habe bei mir extra dafür sogar einen Alias definiert, der Manpages immer mit LANG=C aufruft. Die Locale C steht für ASCII, der Muttersprache aller Rechner, die außer ASCII halt einfach Englisch ist.

    Bash
    # -E bei grep
    LANG=C man grep | sed -rn '/^[[:blank:]]+-E/,/^$/p'
    
    
    # -r bei sed
    LANG=C man sed | sed -rn '/^[[:blank:]]+-r/,/script/p'

    Das Script selbst ist immer noch völlig wirr.
    Soweit ich es verstanden habe -und es sind dieselben Fragen offen, die ich längst stellte-, habe ich das kurzerhand in einer lesbare Form geschrieben. Das kannst du dir einfach mit curl -k https://www.krauttranslate.de/~kalle/url2hosts > meinKomischesScript runterladen.
    Natürlich können Leute, die in der Konsole nicht so firm sind, auch einfach den Link anklicken, im Browser dann das sicher auch völlig überflüssig vorhandene Downloadtool anwerfen, in den Downloadordner wechseln, es an die richtige Stelle kopieren und dann mit chmod ausführbar machen. GUIs sind ja so bequem.


    Das Script ist jetzt brav eingerückt. Fast.
    Die read Zeile gehört auch eingerückt und die Zeilen bis zum EOD Marker inclusive der Markerzeile selbst, könnte man mit Tabs auch einrücken, ohne die Syntax zu verletzen. Da Tabs im Browser/Forum (is PHP Unsinn) nicht sonderlich gut wollen, mag ich das stehen lassen, obwohl natürlich die read Zeilen trotzdem einzurücken wären.


    Du schreibst diverse -wie ich finde- völlig überflüssige Funktionen und verwendest richtig schlimm mehrfache mehrfach sinnlose sed Ausdrücke.
    sed und awk verstehen immer eine Kombi von Adresse gefolgt von einem Befehl.
    Und man kann mehrere Befehle hintereinander getrennt durch Semikola eingeben, wie bei jeder Shell auch.


    Beide lesen auch Dateien. Man muss also niemals ein cat bemühen. Tatsächlich gab es sogar den "Useless use of cat" Wettbewerb, indem solche Konstrukte gegeißelt wurden. Google mal danach: uuoc useless use of cat


    Und beide können Zeilen auch einfach löschen. Bei sed heißt dieser Befehl d.


    Eine ganz schlichte Zeile löscht also eine solche Zeile aus der Datei:
    sed -ri '/'"$2"'/d' /pfad/zur/hosts
    Wir sedieren (==StreamEDitieren)
    mit den Optionen -ri also mit den ExtendedRegexes direkt Inline in der Datei (sed legt intern eine Tempdatei an, führt das Script aus und löscht daraufhin die Temp-Datei wieder.) names /pfad/zur/hosts, indem wir das sed - Script
     '/'"$2"'/d' auführen.
    Dieses Script steht innerhalb von einfachen Hochkommata. Das ist die beste Methode, da keine Shell auch nur irgendetwas innerhalb der Hochkommata ändert.
    Dieses Script besteht aus einem einzigen delete - Befehl, dem ein Adressausdruck vorangestellt ist: /irgendwas/d.
    Dieser Ausdruck löscht also alle Zeilen in der Datei /pfad/zu/hosts, die irgendwo die Zeichenkette irgendwas enthalten.


    Da wir die Zeilen löschen wollen, die als Eintrag das zweite Aufrufargument des Scriptes selbst haben, lassen wir uns genau das von der Shell selbst einsetzen in unser Script. Dazu escapen wir das mit '/ begonnene Script sofort wieder, sind damit wieder in der Shell, die jetzt die gequotete Variable "$2" expandiert und an dieser Stelle einsetzt. Sofort danach geht das Script selbst wieder weiter mit '/d' also dem Ende / des Adresssuchoperators und dem Befehl delete und demfolgend das Ende des Scriptes.
    Wenn in $2 w w w . b a b b e l . d e steht (die wilden Leerzeichen sind der Dummheit der PHP Zoffware geschuldet, von jeder Zeichenfolge, die wie eine URL aussieht anzunehmen, dass der User sie klicken können muss. PHP halt.) ,
    dann wird beim Parsen durch die Shell aus sed -ri '/'"$2"'/d' /pfad/zur/hosts ein sed -ri '/www.babbel.de/d' /pfad/zur/hosts.
    sed selbst merkt nichts von dieser Ersetzung, es glaubt ganz fest an die letzte Form, weil es nur die zu sehen kriegt.


    Es wären nun noch ein paar andere Seltsamkeiten zu bemängeln.
    Hab jetzt aber weder Lust noch Zeit weiterzufabulieren.
    Ich mag nur nochmal anmahnen, dass ich den Zweck des Scriptes immer noch nicht kenne.
    Beschreibe doch bitte präzise, was es genau machen soll und was du erwartest. In Worten, nicht in Shellcodeversuchen. Das Verkürzen der URLs scheint mir einfach sinnlos dumm.
    Ich stelle solche Fragen nicht umsonst.

  • @Berichtigung Dein Engagement in allen Ehren, aber Deine Datei ist doch rein eine CLI Hilfe?!
    Und sed -r gibts wirklich nicht:

    Bash
    echo "ich ging so doof" | sed -rn 's/(.*)([g]{1}[i][ng]{2})(.*)/\Ende:\3; Anfang: \1; Mitte: \2; auf deutsch: \1 bin \3/p'
    sed: illegal option -- r
    usage: sed script [-Ealn] [-i extension] [file ...]
           sed [-Ealn] [-i extension] [-e script] ... [-f script_file] ... [file ...]

    Für die richtig coolen Kids würde ich jetzt sowas schreiben:


    Bash
    curl -k https://www.krauttranslate.de/~kalle/url2hosts > meinKomischesScript.sh && .


    Aber leider weiß ich nicht mehr, wie das mit dem "nimm dem Dateinamen/ String vom letzten Befehl und wende ihn auf den nächsten an" ging. Also er soll prinzipiell das meinKomischesScript.sh vor dem && nehmen und es erneut danach ausgeben. Irgendwie ging das mal, das weiß ich. Google war nicht sehr hilfreich dazu. Vielleicht weißt du es noch?

    Diese Signatur ist derzeit nicht verfügbar.

    Für den Inhalt des Beitrages 107996 haftet ausdrücklich der jeweilige Autor: derwunner

  • Mag sein, dass auf einem angebissenen Apfel tatsächlich diese Option nicht verfügbar ist. Kann ich nicht prüfen. Ich habe ausschließlich GNU/Linux, was ich auch empfehle.
    (Man sollte aber auch auf dem Mac ein anständiges GNU- sed haben können. Selber suchen)


    Das Script tut alles, was du möchtest.
    Es löscht Zeilen, bei Aufruf mit remove und fügt welche hinzu bei add.
    Nur halt in zwei Zeilen, der Rest ist übliches Brimborium, wie es sein sollte.


    Es ist außerdem komplett durchkommentiert.


    Da aber nicht einmal das Script sorgfältig gelesen wird, verzichte ich künftig darauf.
    Is mir dann meine Zeit doch zu schade.


    Natürlich müsste man das -r auf dem Mac nun entsprechend ändern. Was eigentlich ziemlich leicht wäre, wenn man aufmerksam lesen würde. Dazu ist längst alles gesagt.


    Deine Optionen, um einen Output- Dateinamen beim curl - Aufruf mit anzugeben, finden sich ebenfalls -Wunder, welch Wunder!- ebenso in man curl
    Man kann sie sich einfach mit sed rausholen:


    Bash
    man curl | sed -rn '/^[[:blank:]]+-o/,/^[[:blank:]]+--o/{/--oa/d;p}' | less

    Auch hier ist für den halbfaulen Apfel das -r zu ersetzen.
    Vielleicht doch noch mal in Ruhe lesen?
    Hinweis: Es ist ein einziges sed- Script/Ausdruck, das für einen durch zwei RegEx angegebenen Zeilenbereich zwei Befehle ausführt. Einmal löscht es die Zeile des Bereiches, die ein --o enthält ( /--oa/ == Suche Zeile mit --oa ; und delete this line. Und alles andere wird mit print ausgegeben. Das ganze gilt für den Zeilenbereich, deren erste Zeile: Beginn Suchmuster / Am ^ Anfang [[:blank:]]+ eines oder mehrere Blanks enthält, Ende Suchmuster / für die erste Zeile getrennt durch den , Bereichsoperator bis hin zur Zeile die Beginn Suchmuster.....
    Und danach kommen in einem {} Block die zwei Befehle durch den ; Befehlstrenner getrennt.


    Vielleicht bist du ja jetzt doch so neugierig, dass du alles noch mal in Ruhe liest.
    Es geht um die Option ExtendedRegExesEinschalten und um 1 (in Worten: EINEN) Ausdruck, der einem Zeilenbereich definiert für den zwei Befehle ausgeführt werden.

  • Ok, also die Schleife um alle Parameter abzufragen, verstehe. Aber warum muss da der Rest auch in der Schleife sein?


    Vermute mal das hängt mit der starken Kürzung des sed Befehls zusammen. Verstehs aber nicht ganz.


    Außerdem hatte ich das tee da mal drin gehabt, weil das ein Kommando ist, mit den man mit sudo in eine Datei schreiben kann. Das brauche ich auch. Schreiben in /etc/hosts erfordert root Rechte.

    Diese Signatur ist derzeit nicht verfügbar.

    Für den Inhalt des Beitrages 108013 haftet ausdrücklich der jeweilige Autor: derwunner