Aus mit tree erstellter Datei nur den Dateinamen (mit Endung) und die Dateigröße extrahieren

  • Hallo

    Mit Python würde ich das vermutlich über find() und split() erledigen.

    Anstatt `find` würde ich `in` verwenden. Weil ich denke dass das sonst so ein gefummel werden kann. Mit `in` bekomme ich mal meine gewünschte Zeile und kann die dann gemütlich formatieren und muss nicht auf einen Indexbereich zugreifen, der vielleicht etwas unverständlich ist. (Zumindest so wie ich mir das gerade vorstelle.


    Ergibt:

    Finde ich recht übersichtlich.


    Grüße

    Dennis

    🎧 Hate the jocks, the preps, the hippie fuckin' scumbags.
    Heavy-metalers with their awful, pussy hairbands.
    Counting seconds until we can get away.
    Ditching school almost every single day, oh, yeah 🎧

  • Aus mit tree erstellter Datei nur den Dateinamen (mit Endung) und die Dateigröße extrahieren? Schau mal ob du hier fündig wirst!

  • hyle Verzeichnisse kann man an der Grösse vermuten aber eben nicht *zuverlässig* erkennen. Es kann ja auch Dateien geben die genau 4 KiB gross sind.

    Ausgabe:

    Code
    │                   └── [       4096]  Houses Of The Mole [MYNCD023]
                            ^           ^                     ^
                            i           j                     k
    Dateigrösse (inklusive Klammern): [       4096]
    Dateiname >Houses Of The Mole [MYNCD023]<

    “The most likely way for the world to be destroyed, most experts agree, is by accident. That's where we come in; we're computer professionals. We cause accidents.”
    — Nathaniel Borenstein

  • hyle Zum besseren Verständnis des BASIC-Beispiels wollte ich die BASIC-Anweisungen/-Funktionen mal in Python umsetzen und zeigen, aber bei Deinem Benutzernamen dachte ich Hy wäre als Programmiersprache vielleicht passender. 🤡

    Hy ist ein Lisp-Dialekt der in Python programmiert ist und auf der Python-Bytecode-VM läuft und deshalb Python-Module verwenden kann.

    Ich finde es interessant wie nah die Hauptfunktion noch am BASIC-Code dran ist.

    “The most likely way for the world to be destroyed, most experts agree, is by accident. That's where we come in; we're computer professionals. We cause accidents.”
    — Nathaniel Borenstein

  • Okay, irgendwie sind dynamische, interpretierte Programmiersprachen ja voll überdimensionierte „bloat”-Software. Im Grunde nervt auch bei C schon diese massive Speicherverschwendung bei Code und Daten. Eigentlich braucht man ja nur den Kernel, und ein paar Bytes Code, ohne irgendwelche zusätzlichen Bibliotheken. AsSeMbLeR!!!

    Und da es auf dem Raspi laufen muss ARM Assembler. Der Thumb-Befehlssatz, damit es klein bleibt. 😇

    Das Programm liest von der Standardeingabe und schreibt auf die Standardausgabe.

    Übersetzt und ”gestripped” ein Binary mit gerade mal 512 Bytes:

    Bash
    $ as test.s -o test.o && ld test.o -o test && strip test
    $ ls -l test
    -rwxr-xr-x 1 osmc osmc 512 Mar 26 23:56 test
    $ ldd test
      not a dynamic executable

    “The most likely way for the world to be destroyed, most experts agree, is by accident. That's where we come in; we're computer professionals. We cause accidents.”
    — Nathaniel Borenstein

    Edited once, last by __blackjack__ (March 27, 2024 at 12:39 AM).

  • Hallo __blackjack__ ,


    was muss ich denn tun, damit ich den Code laufen lassen kann? Einen Compailer braucht man ja noch um das in Bitcode zu übersetzen.(?)

    Ist die nächste nähere Möglichkeit direkt auf die Register zuzugreifen, den Bitcode selbst zu schreiben?


    Grüße

    Dennis

    🎧 Hate the jocks, the preps, the hippie fuckin' scumbags.
    Heavy-metalers with their awful, pussy hairbands.
    Counting seconds until we can get away.
    Ditching school almost every single day, oh, yeah 🎧

  • Dennis89 Die Übersetzung von Assembler in ein ausführbares Programm steht doch unten im Beitrag #25. as ist der GNU Assembler, der übersetzt das in eine *.o-Datei, die dann mit dem Linker ld in ein ausführbares Programm umgewandelt wird. Beide Programme kommt automatisch auf's System wenn man GCC installiert. Ich habe das alles test.* (test.stest.otest) genannt. Also ausführbar mit, beispielsweise tree -s /etc | ./test. Getestet auf OSMC (32-Bit ARM Linux).

    strip entfernt unnötige Informationen (Debugger-Information, Symboltabelle) aus der ausführbaren Programmdatei. Das ist nicht notwendig, aber ich wollte ja was gegen diese furchtbare Ressourcenverschwendung machen. 😉

    Die nächst-masochistische Option wäre dann die Bytes für den Maschinencode von Hand zu schreiben und da von Hand die nötigen Informationen für das ELF-Dateiformat drumherum zu basteln. Das ist bei ARM aber sehr nervig weil dort sehr viel mit relativen Adressen gearbeitet wird, wie man am Disassembly-Listing sehen kann. Wo ”früher” bei 8-Bit-Heimrechnern (6502, Z80, …) jeder Sprung zu einer Unterroutine wie get_char mit den gleichen Bytes kodiert wird, ist das bei x86 16-Bit-Code und ARM relativ zur Adresse des aktuell ausgeführten Maschinenbefehls/dem Wert des pc-Registers. Das heisst jeder Aufruf ist mit leicht unterschiedlichen Bytes kodiert und man muss jedes mal ausrechnen wie weit die Zieladresse von der aktuellen Adresse entfernt ist und das in die entsprechende Bytefolge umrechnen. Aber auch Konstanten wie Adressen von Daten werden relativ zum pc-Register adressiert. Vorteil ist natürlich das man dadurch weniger feste Adressen im Maschinencode hat, wo man noch zusätzlich Tabellen zum verschieben für den Lader des Betriebssystems für die ”EXE” ausrechnen und anlegen muss.

    Das folgende Listing enthält grob gesprochen drei Spalten. Die erste Spalte enthält die Adresse (hexadezimal) wo der Code im Speicher liegt, die zweite Spalte die Bytes als 16-Bit Werte (hexadezimal) die an der Adresse im Speicher liegen, und der Rest ist der disassemblierte Maschinenbefehl (oder Pseudo-Opcode) der durch die Bytes kodiert wird. Die zweite Spalte ist das was man manuell erstellen/”ausrechnen” müsste.

    Und dann müsste man das Ergebnis noch in die Struktur einer ELF-Datei verpacken, die auch diverse Offsetwerte in die Datei enthält, die man ausrechnen muss. Die Zeiten wo man einfach ein Speicherabbild von Code und Daten ausführt, wie bei 8-Bit-Heimrechnern oder auch noch DOS *.COM, sind ja vorbei. Damals gab es noch etwas zwischen (Makro-)Assembler und alle Bytes manuell ermitteln: Programme mit denen man einzelne Assembler-Befehle interaktiv an Speicheradressen übersetzen kann. DOS bringt so etwas direkt mit: debug.com. Einige 8-Bit-Rechner haben so ein Programm ab Werk im ROM, bei anderen muss man das laden oder als ROM nachrüsten.

    “The most likely way for the world to be destroyed, most experts agree, is by accident. That's where we come in; we're computer professionals. We cause accidents.”
    — Nathaniel Borenstein

  • Danke für die Erklärungen.


    Habe das nicht erkannt, dass das unter #25 die Übersetzung des Codes ist. Auch wenn es da steht, dachte ich mir nur "Hm 💁" ^^


    Das ist jetzt weit vom eigentlichen Thema weg, aber ist das bei allen Sprachen die einen Compailer benötigen so, dass der Code direkt in Maschinencode übersetzt wird und direkt auf die Speicheradressen zugreift? Also ohne dass das Betriebssystem einen Einfluss auf die Programmausführung hat?

    Bei Python ist ja noch der Interpreter dazwischen und da wird alles zur Laufzeit übersetzt und die letztendliche Ausführung wird noch vom Betriebssystem beeinflusst. Wie ist das bei C oder Rust? Ist der compilierte Code von der Nähe zur Hardware vergleichbar mit deinem Assembler-Code?


    Von meiner Vorstellung her schon, man schreibt ja mit den genannten Sprachen auch Firmeware. Aber wie läuft das wenn man den Code unter einem Betriebssystem aussführt?


    Grüße

    Dennis

    🎧 Hate the jocks, the preps, the hippie fuckin' scumbags.
    Heavy-metalers with their awful, pussy hairbands.
    Counting seconds until we can get away.
    Ditching school almost every single day, oh, yeah 🎧

  • Compiler für nativ compilierte Sprachen, also wo Binärcode raus kommt, der direkt vom Prozessor ausgeführt wird, erzeugen manchmal direkt den Maschinencode aber manchmal auch erst Assembler-Quelltext als Zwischenformat.

    Das Betriebssystem gibt nach dem Laden üblicherweise die Kontrolle an den Maschinencode ab. Wobei bei modernen Betriebssystemen die Multitasking können, das System die Kontrolle auch regelmässig wieder weg nimmt um seine Arbeit zu erledigen. ”Früher”, beispielsweise beim Amiga und den ersten Windows-Versionen mussten die Programme die Kontrolle selbst regelmässig abgeben. Da konnten Programme, die das nicht gemacht haben, das System aufhängen bzw. die ganze Rechenzeit für sich selbst beanspruchen.

    Was beim laden eines nativen Programms passiert, variiert stark zwischen Systemen und Dateiformaten.

    In den simpelsten Fällen wird die Datei einfach 1:1 an eine feste Adresse in den Speicher geladen und dann zu einer festen Adresse gesprungen. Zum Beispiel bei CP/M und bis zu einem gewissen Grad bei DOS und .COM-Dateien.

    Wenn der Lader das Programm an eine unbekannte/unvorhersehbare Adresse laden können muss, zum Beispiel um mehr als ein Programm im Speicher zu haben, sind in der Programmdatei Tabellen enthalten welche Adressen im Speicher relativ zur Ladeadresse angepasst werden müssen. Beispiel dafür sind DOS .EXE-Dateien.

    Nächste Stufe sind Programme wo nicht mehr in jedem Programm alles enthalten ist was benötigt wird: dynamsiche Bibliotheken: Da sind in der Programmdatei auch noch Tabellen enthalten mit Symbolen und an welchen Adressen im Programm die verwendet werden, und die Namen von den Bibliotheken die benötigt werden. Der Lader lädt dann die Bibliotheken, sofern noch nicht um Speicher, und das Programm, und geht dann die Stellen im Programm durch wo Adressen aus Bibliotheken benutzt werden und setzt dort die passenden Werte ein. Da wären wir dann bei Windows & Linux.

    Das mit dem anpassen der Adressen im Code an die Adresse wo das Programm in den Speicher geladen wurde, kann teilweise wegfallen wenn die Hardware Adressvirtualisierung unterstützt. Dann kann man alle Programme für eine feste Startadresse übersetzen als wenn sie jeweils den ganzen Speicher für sich alleine hätten.

    “The most likely way for the world to be destroyed, most experts agree, is by accident. That's where we come in; we're computer professionals. We cause accidents.”
    — Nathaniel Borenstein

  • Vielen Dank für die ausführliche Erklärung.

    Ich habe jetzt kein Anwendungsbeispiel dafür, aber wenn ich das richtig verstehe, hat bei den heute üblichen Betriebssystem, das Betriebssystem selbst immer das letzte Wort, wann etwas ausgeführt wird? Egal ob Interpreter-Sprache oder compilierte Sprache? Die Interpreter-Sprache ist nur etwas langsamer, weil zur Laufzeit der Interpreter noch mit im Boot ist?

    Bei Mikrokkontroller gibt es ja `FreeRTOS` und wenn ich an einem Computer etwas echtzeitfähiges erreichen will, benötige ich ein anderes Betriebssystem wie Windows oder Linux?


    Ich versuche zum einen nur detaillierter den Unterschied zwischen Compailer- und Interpreter-Sprache zu verstehen, was die theoretische Geschwindigkeit angeht. Wenn beides mal das Betriebssystem das letzte Wort hat, dann würde das für mich bedeuten, dass "nur" der Interpreter dafür verantwortlich ist, dass Interpreter-Sprachen langsamer sind als Compailer-Sprachen.


    Grüße

    Dennis

    🎧 Hate the jocks, the preps, the hippie fuckin' scumbags.
    Heavy-metalers with their awful, pussy hairbands.
    Counting seconds until we can get away.
    Ditching school almost every single day, oh, yeah 🎧

  • Das Betriebssystem sollte im Idealfall das letzte Wort haben, denn dessen Aufgabe ist es ja die Ressourcen des Systems zu verwalten. CPUs/CPU-Zeit ist ja eine der Ressourcen die es verwaltet und den Prozessen zuteilt.

    Für Linux gibt es RTLinux für Echtzeitfähigkeit.

    Das interpretierte Sprachen langsamer abgearbeitet werden hat mit dem Betriebssystem nicht wirklich etwas zu tun. Das ist ja auch unter primitiven Systemen wie DOS oder sogar Mikrokontrollern ohne Betriebssystem genau so. C-Programme sind dort auch schneller als MicroPython-Programme, eben weil in einem Fall der Prozessor das Programm abarbeitet und im anderen Fall der Prozessor den Interpreter abarbeitet der das Programm interpretiert. Und dann spielt da natürlich auch noch rein, dass bei C der Compiler die Datentypen kennt wenn beispielsweise a + b übersetzt wird und dafür den direkt passenden Maschinencode generiert werden kann, während das bei Python erst zur Laufzeit festgestellt wird welcher Code für das + letztendlich ausgeführt werden muss.

    “The most likely way for the world to be destroyed, most experts agree, is by accident. That's where we come in; we're computer professionals. We cause accidents.”
    — Nathaniel Borenstein

  • Vielen Dank! :)


    Das mit den Datentypen festlegen ist mir bei `Rust` auch schon ab und zu auf die Füße gefallen, weil es doch ungewohnt ist, wenn man nur Python kennt. Ich war auch erst "überrascht" dass wenn ich eine leere Liste will, das ich die Anzahl der Elemente die mal in die Liste kommen und den Datentyp der Elemente gleich festlegen muss. Mittlerweile konnte ich dass dann verstehen.

    Ich finde es macht einen großen Unterschied, ob man die Syntax einer Sprache lernt oder ob man versteht, was denn im Hintergrund noch alles passiert. Wobei mir bewusst ist, dass das komplexer ist, aber wenigstens hat man mal eine grobe Vorstellung.


    Grüße

    Dennis

    🎧 Hate the jocks, the preps, the hippie fuckin' scumbags.
    Heavy-metalers with their awful, pussy hairbands.
    Counting seconds until we can get away.
    Ditching school almost every single day, oh, yeah 🎧

Participate now!

Don’t have an account yet? Register yourself now and be a part of our community!