Hier gibt es einige Probleme.
Parsing ls
Zuallererst solltenls
Sie nicht analysieren . Ihr ls -Art | tail -n 1
ist fehlerhaft und kann nicht zuverlässig behoben werden. Ein zuverlässiger Standardersatz ist find
und / oder das Arbeiten mit nullterminierten Leitungen.
Ich gehe außerdem davon aus, dass Sie die zuletzt geänderte Datei (kein Verzeichnis) direkt im aktuellen Verzeichnis (nicht in einem Unterverzeichnis) benötigen.
Die Möglichkeit, Eingabeelemente in einer Form mit nullterminierter Liste zu parsen, ist für POSIX normalerweise nicht erforderlich. Wenn Ihre Werkzeuge reich an Optionen sind, ist dies ein robuster Ersatz für Ihre ls -Art | tail -n 1
:
find . -maxdepth 1 -type f -printf '%T@ %p\0' | sort -zrn | head -zn 1 | cut -z -d " " -f 2-
Das Ergebnis wird als nullterminierter Dateiname angezeigt. Um etwas damit zu tun, müssen Sie es weiterleiten xargs -0 …
, zB:
… | xargs -0r cp -t "/target/dir/" --
oder (falls Ihr cp
nicht unterstützt -t
):
… | xargs -0r -I {} cp -- {} "/target/dir/"
In diesen Befehlen gibt es viele Dinge, die POSIX nicht benötigt (also nicht portabel). Dazu gehört auch --
, dass cp
das Parsing von Optionen beendet wird. Eine Datei -R
löst also keine Rekursion aus, anstatt kopiert zu werden. Beachten Sie, dass Dateinamen, die unseren Namen verlassen find . …
, mit .
Sicherheit beginnen, sodass --
sie sicher weggelassen werden können. Ich benutzte es nur, um auf eine gute allgemeine Praxis im Umgang mit hinzuweisen cp
. Eine andere bewährte Methode ist das Zitieren von Pfaden: Das Literal /target/dir/
würde ohne Anführungszeichen funktionieren, aber nachdem Sie dieses Beispiel durch Ihren spezifischen Zielpfad ersetzt haben, müssen Sie möglicherweise Anführungszeichen verwenden, daher verwende ich sie trotzdem.
In Ihrem (lokalen zu entfernten) Fall würden Sie scp
anstelle von verwenden cp
. Beim Parsen von Argumenten kann es seine eigenen Macken haben.
Ein POSIX-kompatibler, aber schlecht funktionierender Befehl kann Folgendes sein:
find . ! -name . -prune -type f -exec sh -c ' [ "$(find . ! -name . -prune -type f -newer "$1" -exec printf a \; | wc -c)" -eq 0 ]' sh {} \; -print
Es passt einen Ansatz aus dieser anderen Antwort an den Ersatz (Nicht-POSIX) an -maxdepth 1
. Es erscheint, sh
um zwei find … -exec …
Klauseln zuverlässig zu verschachteln . Das Äußere find
prüft alle Dateien und liefert sie einzeln an das Innere find
. Das Innere find
findet alle Dateien, die neuer als die angegebene Datei sind, und druckt a
für jede neuere Datei ein einzelnes Zeichen ( ). wc -c
zählt diese Zeichen. Wenn keine vorhanden ist, bedeutet dies, dass die angegebene Datei die zuletzt geänderte Datei ist. erst dann find
druckt es das Äußere .
Es gibt einige Szenarien, in denen die äußere find
Datei möglicherweise mehr als eine Datei druckt:
- es gibt zwei oder mehr Dateien mit der gleichen "neuesten" mtime;
- Dateien im Verzeichnis werden geändert, während der Befehl ausgeführt wird.
- Das Innere
find
kann einige Dateien nicht statisieren.
Aus diesem Grund wäre die Endaktion -quit
des Äußeren find
nützlich (beachten Sie, dass dies auch für das Innere nützlich wäre find
, jedoch aus einem anderen Grund). Ist leider -quit
nicht POSIX.
Ich habe verwendet -print
( -print0
ist nicht POSIX), dennoch sind nicht standardmäßige Dateinamen kein Problem, da Sie die Ausgabe nicht an einen anderen Befehl weiterleiten müssen. Verwenden Sie einfach das -exec
, was alle möglichen Dateinamen richtig behandelt; zB anstelle von -print
dir verwenden:
-exec yet_another_command {} \;
Jetzt wissen Sie, wie Sie die zuletzt geänderte Datei in einem lokalen Verzeichnis finden, ohne sie zu analysieren ls
.
Dateien auf einem Remote-System suchen
Welchen Ansatz Sie auch wählen (einschließlich der fehlerhaften ls … | tail …
), Sie müssen den Befehl auf dem Remote-System ausführen (oder nicht, ich komme später zu dieser Ausnahme), um die gewünschte Datei in einem Remote-Verzeichnis zu finden.
Der naheliegendste Ansatz ist ssh
der Einstieg in das Remote-System. In dem neuen Kontext ist das Remote-System lokal und Ihr lokaler Computer ist Remote. (Hier verwende ich den obigen POSIX-kompatiblen Befehl als Beispiel, aber Sie können unseren ersten verwenden, find … | sort -z … | head -z … | cut -z … | xargs -0 …
wenn nur das Remote-System alle erforderlichen Optionen unterstützt.)
ssh usr@remote # now on the remote system cd "/source/dir/" && find . ! -name . -prune -type f -exec sh -c ' [ "$(find . ! -name . -prune -type f -newer "$1" -exec printf a \; | wc -c)" -eq 0 ]' sh {} \; -exec scp {} usr@local:"/target/dir/" \;
Beachten Sie, wenn Sie es vermeiden cd
und verwenden möchten, dann müssen find /source/dir …
mehrere .
-s durch ersetzt werden /source/dir
. Mit geht es einfacher cd
.
Ihr lokales System muss über SSH vom Remote-System aus erreichbar sein. Wenn NAT unterwegs ist, können Sie es mit einer Remote-Portweiterleitung umgehen, etwa wie folgt:
ssh -R 12322:127.0.0.1:22 usr@remote # now on the remote system the same cd + find as above # only the scp part is different … -exec scp -P 12322 {} usr@127.0.0.1:"/target/dir/" \;
Beachten Sie, dass der Remote-Port 12322
zu Ihrem lokalen Anschluss führt, sshd
und jeder Benutzer des Remote-Systems kann versuchen, ihn zu missbrauchen. Ein weiteres Problem: Das Remote-System ist möglicherweise so konfiguriert, dass Sie überhaupt keinen Port weiterleiten können.
Möglicherweise möchten Sie, dass ein einzelner Befehl auf dem lokalen System aufgerufen wird. In diesem Fall ist ein korrektes Zitat erforderlich und es wird noch umständlicher:
ssh usr@remote ' cd "/source/dir/" && find . ! -name . -prune -type f -exec sh -c '"'"' [ "$(find . ! -name . -prune -type f -newer "$1" -exec printf a \; | wc -c)" -eq 0 ]'"'"' sh {} \; -exec scp {} usr@local:"/target/dir/" \; '
Ich erwarte Probleme, wenn die Fernbedienung jedoch scp
nach Ihrem Passwort fragen muss. Aus diesem Grund oder wenn Sie Ihr lokales Ziel nicht erreichen sshd
können, benötigen Sie möglicherweise einen anderen Ansatz.
Dieser lokale Befehl gibt den gewünschten Dateinamen aus dem Remote-System aus:
ssh usr@remote ' cd "/source/dir/" && find . ! -name . -prune -type f -exec sh -c '"'"' [ "$(find . ! -name . -prune -type f -newer "$1" -exec printf a \; | wc -c)" -eq 0 ]'"'"' sh {} \; -print '
Sie können es mit lokalen Tools wie xargs
und verwenden scp
. Beachten Sie, dass die Analyse der -print
Renditen nur geringfügig besser ist als die Analyse ls
. Verwenden Sie -print0
(non-POSIX, ist möglicherweise nicht verfügbar) oder -exec printf "%s\0" {} \;
(sollte funktionieren), anstatt -print
den gewünschten Dateinamen als nullterminierte Zeichenfolge zu erhalten. Nun liegt es an Ihnen, was Sie auf lokaler Seite damit tun. Dies ist nützlich, wenn Sie einen POSIX-kompatiblen Fernbefehl benötigen, die Tools in Ihrem lokalen System jedoch reich an Optionen sind.
Bemerkenswerte Ausnahme: sshfs
sshfs
erlaubt es Ihnen usr@remote:"/source/dir/"
als /local/path/
. Siehe meine Antwort, ich werde mich nicht wiederholen, um alle Details zu behandeln. In Ihrem Fall sieht die (lokale) Prozedur mit reichen (nicht auf POSIX beschränkten) Werkzeugen wie folgt aus:
sshfs usr@remote:"/source/dir/" "/local/path/" find "/local/path/" -maxdepth 1 -type f -printf '%T@ %p\0' | sort -zrn | head -zn 1 | cut -z -d " " -f 2- | xargs -0r cp -t "/target/dir/" -- fusermount -u "/local/path/"
Das ist toll. Alles was Sie mit lokalen Werkzeugen machen. Wenn Sie nur sshfs
die Tools auf der Remote-Seite verwenden können, spielen die verfügbaren Optionen keine Rolle mehr. Es ist auch egal, ob Sie Ihr lokales System von außen erreichen können. Und die Methode ist die gleiche: Remote zu Local oder Local zu Remote ist egal.