RW: ls Ausgabe begrenzen

Hi,

1. ) Wie kann ich die unix Ausgabe "ls -ltu  *.png" auf 10 Einträge begrenzen?

<?php  
$test =`ls -ltu  *.png 2> /dev/null;  echo $?`;  
echo $test;  
?>

-rw-r--r--  1 nobody   4294967295 2911 2009-09-04 15:30 8717.png
-rw-r--r--  1 nobody   4294967295 1901 2009-09-04 15:27 66690.png
-rw-r--r--  1 nobody   4294967295 1647 2009-09-04 15:21 29121.png
-rw-r--r--  1 nobody   4294967295 1535 2009-09-04 15:19 29122.png
-rw-r--r--  1 nobody   4294967295 1516 2009-09-04 15:19 20322.png
-rw-r--r--  1 nobody   4294967295 2125 2009-09-04 15:19 8716.png
-rw-r--r--  1 nobody   4294967295 2608 2009-09-04 15:19 20323.png
-rw-r--r--  1 nobody   4294967295 3014 2009-09-04 15:19 8718.png
-rw-r--r--  1 nobody   4294967295 1958 2009-09-04 15:19 29119.png
usw.

2.) Aus den Zeilen brauche ich nur die ([0-9]+) ID vom png bzw. die Zeit mit Formatierung d.m.y h:m

Danke RW

  1. Moin Moin!

    Hi,

    1. ) Wie kann ich die unix Ausgabe "ls -ltu  *.png" auf 10 Einträge begrenzen?

    head -n 10

    2.) Aus den Zeilen brauche ich nur die ([0-9]+) ID vom png bzw. die Zeit mit Formatierung d.m.y h:m

    Warum benutzt Du externe Programme für Dinge, die PHP von sich aus kann? Lese in der PHP-Doku nach: opendir, readdir, closedir, scandir, stat, lstat.

    Die Ausgabe von ls hat einige Besonderheiten (alte Dateien erscheinen mit dem Jahr, neue mit Stunde und Minute) und ist nicht für maschinelles Parsen ausgelegt. stat und readdir bzw. scandir liefern Dir alle Daten schneller und zuverlässiger.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
    1. HI Alexander!

      Dank Dir!

      ...stat und readdir bzw. scandir liefern Dir alle Daten schneller....

      bist Du Dir da sicher?
      PHP Parser + Speicherbedarf
      es handelt sich um ein Verzeichnis mit ca. 70.000 .png Datei'n

      Die Php Lösung ist natürlich einfacher umzusetzen

      RW

      1. Moin Moin!

        HI Alexander!

        Dank Dir!

        ...stat und readdir bzw. scandir liefern Dir alle Daten schneller....

        Ich schrieb außerdem: "und zuverlässiger."

        bist Du Dir da sicher?

        Ja.

        PHP Parser

        opendir, readdir, closedir sind leicht zu parsen und enden sehr schnell bei den libc-Aufrufen, bei denen auch ls endet. Außerdem muß der Code nur ein einzges mal geparst werden, während Du für ls einen Parser für ls-Ausgaben in PHP schreiben mußt, der jedes mal wieder läuft.

        • Speicherbedarf

        Minimal. Du liest Datei für Datei nur Meta-Daten ein: readdir liefert dir einen Dateinamen, der im Allgemeinen weniger als 1 KByte belegt und bei den wenigsten Systemen länger als 4 oder 8 KByte werden kann. stat liefert weniger als 100 Bytes. Dazu kommt noch der Directory-Handle, das ist ein schlichter Integer.

        Du willst die Top 10, macht im Worst Case ca. 9 KByte (8 KByte Dateiname + 1 KByte Stat, Laufvariable, Handle und Kleinkram) in der readdir-Schleife und weitere 8 KByte (ca.) pro Top-10-Eintrag im Ergebnis-Array, in der Spitze ca. 89 KByte, nach Ende der Schleife noch 80 KByte.

        es handelt sich um ein Verzeichnis mit ca. 70.000 .png Datei'n

        Dateien.

        Nehmen wir mal ganz entspannt 80 Zeichen pro Datei im ls-Listing an, das Du per Backtick-Operator einliest, und gehen wir von ASCII aus (1 Byte = 1 Zeichen), dann kommen wir auf 70.000 Dateien * 80 Bytes Metadaten / Datei = 5.600.000 Bytes. Damit Du damit etwas sinnvolles anstellen kannst, mußt Du den String noch in ein Array splitten, also im wesentlichen einmal umkopieren. Macht grob 10 MBytes in der Spitze, von denen Du gleich mal die Hälfte wegschmeißt, weil Du die Stringvariante der Daten nicht mehr brauchst. Nach viel Parserei und vielen Millionen unnütz verbrannter CPU-Zyklen kommst Du auf die selben 80 KByte wie oben.

        Die Php Lösung ist natürlich einfacher umzusetzen

        Nicht nur dass, sie erspart Dir auch, einen Subprozess zu forken, um ls auszuführen. Erschwerend kommt bei PHP hinzu, dass Backticks bzw. shell_exec() so implementiert sind, dass *IMMER* eine Shell aufgerufen wird, um das Kommando auszuführen (und in aller Regel die richtig fette Bash). Die wiederum muß auch erstmal geladen werden und ihrerseits einen Parser in Gang setzen, bevor sie ihrerseits einen Subprozess forkt, um ls auszuführen -- natürlich erst, nachdem sie ihre diversen Konfigurationsdateien durchverdaut hat. Wenn Du Dich mit Unix-artigen Systemen mal etwas näher auseinander gesetzt hast, weißt Du, dass fork() und exec() besonders "teuer" sind.

        fork bedeutet nämlich, dass der gesamte Prozess inklusive Meta-Daten komplett kopiert werden muß (natürlich mit Ausnahme der Prozess-ID). exec() ist auch nicht viel besser, im Erfolgsfall ersetzt es den aktuellen Prozess, dass muß aber so geschehen, dass der alte Prozess so lange wie möglich erhalten werden muß, damit exec() im Fehlerfall zurückkehren kann. Moderne Unixe wie z.B. Linux tricksen bei fork() etwas, damit fork() nicht ganz so teuer ist: Die Meta-Daten werden kopiert, aber Code und Daten bleiben erst einmal geteilt, aber sind als schreibgeschützt markiert. Erst wenn der abgeforkte Prozess die Daten ändert oder den Code überschreibt, wird wirklich kopiert (Copy-on-Write). Das funktioniert aber nur, wenn der Prozessor eine MMU hat, die den ersten Schreibzugriff per Exception verhindert und an das Betriebssystem meldet. Das kopiert dann, was eigentlich schon beim fork() hätte kopiert werden sollen, und führt den Schreibzugriff auf der nicht mehr schreibgeschützten Kopie aus.

        $bla=ls -bla -fasel erzeugt also zwei fork()s (PHP und Shell), zwei exec()s (PHP und Shell), ein paar Pipes zwischen PHP und der Shell, parst den String zwischen den Backticks in der Shell, parst die Kommandozeilenparameter von der Shell in ls, ruft die entscheidenden Funktionen opendir(), readdir(), closedir() und stat() auf und erzeugt daraus eine für Menschen geeignete Ausgabe, die PHP-Code dann mühsam wieder parsen muß.

        opendir/readdir/closedir/stat in PHP ruft die benötigten Funktionen direkt auf, ohne fork, ohne exec, ohne Pipes, ohne drei zusätzliche Parser, und mit der Sicherheit, dass keine Informationen durch die gestapelten Parser verloren gehen oder verfälscht werden.

        Wußtest Du, dass Dateinamen unter Unixoiden Betriebssystemen nur exakt zwei Zeichen NICHT enthalten dürfen? "/" ist das Trennzeichen für Verzeichnisnamen und innerhalb von Verzeichnis- und Dateinamen nicht erlaubt, ASCII NUL (0x00, "\0") ist das String-Ende-Kennzeichen in C und daher nicht erlaubt. Alle anderen Zeichen, inklusive Steuerzeichen wie CR, LF, Tabulator, Backspace, Leerzeichen, Stern, Fragezeichen und Backslash sind *ERLAUBT*. Außerdem gibt es nur zwei reservierte Namen, "." für das aktuelle Verzeichnis und ".." für das übergeordnete Verzeichnis. Alle anderen Kombinationen der erlaubten Zeichen sind erlaubt.

        Und genau an diesem Punkt kommt ls ins Spiel, denn es gibt je nach Implementation, Environment und Kommandozeilenparametern die Sonderzeichen unterschiedlich aus, dass muß der PHP-Code, der ls aufruft, berücksichtigen. Auch muß beim Datum berücksichtigt werden, dass ls mal Stunde und Minute (Dateien jünger als ein Jahr), mal Jahr (Dateien älter als ein Jahr) ausgibt. Natürlich beeinflußen auch noch die Spracheinstellungen die Ausgabe von ls, und auch das muß berücksichtigt werden. Übersiehst Du nur einen Punkt, liefert der Parser für ls nur noch Müll. Dein ls-Parser kommt doch mit Zeilenumbrüchen in Dateinamen und den per Backslash escapten Sonderzeichen zurecht, oder? Und er berücksichtigt auch die lustigen Eigenarten der Spracheinstellungen, Dateigrößen mal im Stück, mal mit Punkten, mal mit Kommata, mal mit Leerzeichen getrennt auszugeben, richtig?

        opendir/readdir/stat/closedir dagegen liefert die Namen und Meta-Daten immer im selben, maschinenlesbaren Format und benötigt keinen Parser, es kann nicht zu Parser-Fehlern kommen.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
  2. Hi,

    1. ) Wie kann ich die unix Ausgabe "ls -ltu  *.png" auf 10 Einträge begrenzen?

    [..]  |  head -n 10

    Hotte