Wie kann ich Dateien mit einem Befehl in umgekehrter Zeitreihenfolge auflisten und als Argumente an einen anderen Befehl übergeben?

7

Ich habe ein Programm, das einige Dateien als Argumente in einer Befehlszeile aufnimmt. Ich möchte es mit allen Dateien in einem Verzeichnis aufrufen, das in umgekehrter Zeitreihenfolge aufgeführt ist.

Zum Beispiel:

Ich habe folgende Dateien in umgekehrter Zeitreihenfolge in einem Verzeichnis

$ ls -tr
 Introduction.pdf  'Object-Oriented Data Model.pdf' 

Ich kann mein Programm einfach ausführen,

myprogram Introduction.pdf  'Object-Oriented Data Model.pdf' 

aber ich möchte die Dateiliste mit einem anderen Befehl machen. Dies funktioniert aufgrund des Leerzeichens im Namen einer Datei nicht:

myprogram $(ls -tr)

Ich erinnere mich, dass das Parsen der lsAusgabe keine gute Praxis ist. Ich bin mir nicht sicher, ob ich findhelfen kann.

Was kann ich dann tun?

Vielen Dank.

Tim
quelle
Warum ist parsing lskeine gute Praxis? ls -rt | myprogramwürde funktionieren, würde der Dateiname, wenn er ein Leerzeichen hat, einfach object-oriented\ data\ model.pdfeinen Backslash vor jedem Leerzeichen verwenden. Oder wenn myprogrames in C geschrieben ist, könnten Sie es verwenden. popen("ls -rt", "r")Ich mache das die ganze Zeit und es funktioniert großartig.
Ron
@ron warum nicht eine Antwort machen?
Roaima
@Ron verwenden ls ... | myprogramwürde die Dateinamen zur Verfügung stellt (als Ausgang from` ls`) als stdin an das Programm, nicht als Argument, wie gewünscht. Beachten Sie auch, dass Dateinamen mit einer neuen Zeile als zwei separate Zeilen an das Programm gesendet werden, die sie nicht mehr von zwei separaten Dateinamen unterscheiden können.
Jeff Schaller
@roaima so dass du mich nicht sofort -1.
Ron
was ist myprogram? Ein Bash-Skript, csh / tcsh-Skript, ksh? oder ist es eine ausführbare Datei, die aus C-Code kompiliert wurde? oder Python, Java ...?
Ron

Antworten:

5

Wenn Sie einigermaßen aktuelle Versionen der GNU-Dienstprogramme haben, können Sie diese mit NULL-terminierten Daten verarbeiten lassen. Auf diese Weise können Pipelines erstellt werden, die nicht von Leerzeichen oder Zeilenumbrüchen in den Daten selbst betroffen sind.

Mein Testwerkzeug ist ein schnelles Skript namens /tmp/args:

#!/bin/bash
echo "This is args with $# value(s)"
for f in "$@"; do echo "> $f <"; done

Auf diese Weise können Sie eine Reihe von Dateinamen in der Befehlszeile eingeben, sortiert nach der zuletzt geänderten Dateizeit:

find -type f -printf "%T@ %p\0" | sort -zn | sed -z 's/^[0-9.]* //' | xargs -0 /tmp/args

Der findBefehl stellt jedem Datei- / Pfadnamen eine Darstellung in Sekundenbruchteilen des Datums / der Uhrzeit vor, die zuletzt geändert wurde. Dies wird von sortder Reihenfolge vom niedrigsten (ältesten) zum höchsten (neuesten) gehandhabt . Die sedStreifen von der führenden Nummer, nach der wir gerade sortiert haben, und der resultierende Satz von Dateinamen werden an übergeben xargs. Ersetzen Sie das %pdurch, %Pwenn Sie die führenden ./Dateinamen weglassen möchten .

Beispieldaten

# "c d" contains a space; "e f" contains a newline; "h" has leading whitespace
touch a 'e
f' g ' h ' 'c d' b

Beispiel Ergebnis

This is args with 6 value(s)
> ./a <
> ./e
f <
> ./g <
> ./ h  <
> ./c d <
> ./b <
Roaima
quelle
Da es keinen Hinweis darauf gibt, dass diese "Lösung" ironisch gemeint ist, kann ein Neuling zu dem Schluss kommen, dass Menschen auf diese Weise Dateien unter Unix sortieren - indem sie tausend Befehle verketten, von denen jeder zweifelhafte, nicht standardmäßige Erweiterungen verwendet.
Onkel Billy
4

Bash macht es noch nicht einfach, Dateien nach Änderungszeit zu sortieren. Hier ist die obligatorische zsh-basierte Antwort. Sie müssen nicht als Anmeldeshell zu zsh wechseln, um die Funktionen nutzen zu können.

Hier habe ich eine Wrapper-Funktion eingerichtet, die entweder ein oder zwei Argumente erwartet. das erste Argument ist das auszuführende Programm (zB myprogram); Das zweite optionale Argument ist das Verzeichnis mit den Dateien, die Sie an das Programm übergeben möchten. Wenn Sie kein zweites Argument angeben, wird standardmäßig das aktuelle Verzeichnis verwendet.

zeio() {
  # Zsh-based Execute with files In Order
  [ -d "${2:-.}" ] || return
  zsh -c "$1 \"${2:-.}\"/*(.om)"
}

Nennen Sie es natürlich wie Sie wollen. Nach einer kurzen Überprüfung des zweiten Arguments (standardmäßig .das aktuelle Verzeichnis) rufen wir zsh mit einer einzelnen Zeichenfolge in doppelten Anführungszeichen auf, die das Programm und einige Anfangsargumente (Argument 1) und das Verzeichnis (Argument #) enthält. 2) - standardmäßig .- mit einer Wildcard / Glob-Erweiterung, die zwei "Glob-Qualifizierer" in der nachfolgenden Klammer enthält:

  1. . - müssen reguläre Dateien sein (keine Verzeichnisse oder Symlinks usw.)
  2. om- oSortieren Sie die resultierende Liste nach Änderungszeit, zuletzt zuerst

Es ist dieses omGlob-Qualifikationsmerkmal, das die ganze eigentliche Arbeit hier erledigt.

Hier sind einige Beispielläufe. myprogist ein einfaches Shell-Skript, um die empfangenen Argumente in der folgenden Reihenfolge zu demonstrieren:

#!/bin/sh
for arg do
  printf 'Arg: ->%s<-\n' "$arg"
done

und go.shist die Datei, in der ich die Funktion gespeichert habe. Der Rest der Verzeichnisstruktur lautet:

$ tree .
.
├── dir1
   ├── 203142
   ├── 203143
   └── 203144
├── dir3
   ├── first\012filename
   ├── this is 3rd
   └── this is second
├── dir two
   ├── 203225
   ├── 203226
   └── 203227
├── go.sh
└── myprog

... wo ich die Sätze von drei Dateien in jedem Unterverzeichnis in der aufgelisteten Reihenfolge erstellt habe; Ich erwarte, sie in derselben Reihenfolge zu sehen, wenn ich die Funktion ausführe. Der erste Dateiname unter dir3 enthält eine neue Zeile, dargestellt durch treemit \012. Die Ergebnisse sind:

Demonstration des Verhaltens des Standardverzeichnisses im aktuellen Verzeichnis:

$ zeio ./myprog
Arg: ->./myprog<-
Arg: ->./go.sh<-

Normale Dateinamen-Demonstration

$ zeio ./myprog dir1
Arg: ->dir1/203142<-
Arg: ->dir1/203143<-
Arg: ->dir1/203144<-

Das Verzeichnis enthält ein Leerzeichen

$ zeio ./myprog "dir two"
Arg: ->dir two/203225<-
Arg: ->dir two/203226<-
Arg: ->dir two/203227<-

Dateinamen enthalten Leerzeichen

$ zeio ./myprog dir3
Arg: ->dir3/first
filename<-
Arg: ->dir3/this is second<-
Arg: ->dir3/this is 3rd<-
Jeff Schaller
quelle
3
(IFS=$'\n'; set -f; your_program $(ls -tr))

Angenommen, die Dateinamen enthalten keine Zeilenumbrüche.

Beispiel:

% touch 'a   b'
% touch 'c d'
% touch '*'
% (IFS=$'\n'; set -f; printf '%s\n' $(ls -tr))
e    f
a   b
c d
*

Variante für eine Standard-Shell, die keine $'...'Zeichenfolgen unterstützt:

(IFS='
' ; set -f; your_program $(ls -tr))

Einfacher Python-Wrapper, der auch mit Dateinamen funktioniert, die Zeilenumbrüche enthalten. Nutzung kann sein this_wrapper your_program *.

#! /usr/bin/python
import os
import sys

sys.argv[2:] = sorted(sys.argv[2:], key=os.path.getmtime)
os.execvp(sys.argv[1], sys.argv[1:])
Onkel Billy
quelle
Vielen Dank. Was ist, wenn der Dateiname Zeilenumbrüche enthält
Tim
Dann funktioniert das nicht ;-) - und du musst so etwas wie perloder verwenden python.
Onkel Billy
1
Ich bin kein Python-Programmierer, aber da niemand einspringt ... YMMV, ist das möglicherweise nicht der Stand der Technik ;-)
Onkel Billy
1
Tim, hast du wirklich Zeilenumbrüche in deinen Dateinamen?
Freddy
1
@ Freddy Selten. Aber alles ist möglich.
Tim