Gibt es einen einfachen Befehl, um die zuletzt geänderte Datei in einem Verzeichnis auf einem Remote-Host per SCP zu senden? Ich kann herausfinden, wie das von lokal auf remote geht ... so etwas wie:
scp ``ls -Art | tail -n 1\`` usr@remote:/var/log/yeet
Wie kann ich dasselbe tun, aber von remote auf lokal. (Also die zuletzt geänderte Datei von yeet holen und auf den lokalen Host kopieren)
Antwort1
Hier gibt es nur wenige Probleme.
Parsingls
Zunächst einmalsollte nicht analysiert werdenls
. Ihr ls -Art | tail -n 1
ist fehlerhaft und kann nicht zuverlässig repariert werden. Ein standardmäßiger zuverlässiger Ersatz ist find
und/oder das Arbeiten mit nullterminierten Leitungen.
Außerdem gehe ich davon aus, dass Sie die zuletzt geänderte Datei (kein Verzeichnis) direkt im aktuellen Verzeichnis (nicht in einem Unterverzeichnis) benötigen.
Die Fähigkeit, Eingabeelemente in Form einer nullterminierten Liste zu analysieren, wird von POSIX normalerweise nicht benötigt. Wenn Ihre Tools über genügend Optionen verfügen, ist dies ein robuster Ersatz für Ihr 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 ist ein nullterminierter Dateiname. Um damit etwas zu tun, müssen Sie es an weiterleiten xargs -0 …
, z. B.:
… | xargs -0r cp -t "/target/dir/" --
oder (falls Ihr Gerät cp
dies nicht unterstützt -t
):
… | xargs -0r -I {} cp -- {} "/target/dir/"
In diesen Befehlen gibt es viele Dinge, die POSIX nicht benötigt (und die daher nicht portierbar sind), einschließlich --
der Einstellung, die cp
die Analyse von Optionen stoppt, sodass beispielsweise eine Datei -R
keine Rekursion auslöst, anstatt kopiert zu werden. Beachten Sie, dass Dateinamen, die unser verlassen, mit Sicherheit find . …
mit beginnen und daher getrost weggelassen werden können. Ich habe es nur verwendet, um auf eine gute allgemeine Vorgehensweise beim Umgang mit hinzuweisen . Eine weitere gute Vorgehensweise ist das Anführen von Pfaden: Der Literalwert würde ohne Anführungszeichen funktionieren, aber nachdem Sie dieses Beispiel durch Ihren spezifischen Zielpfad ersetzt haben, benötigen Sie möglicherweise Anführungszeichen, daher verwende ich sie trotzdem..
--
cp
/target/dir/
In Ihrem Fall (von lokal zu remote) würden Sie scp
anstelle von verwenden cp
. Es kann beim Parsen von Argumenten seine eigenen Macken haben.
Ein POSIX-kompatibler, aber schlecht funktionierender Befehl könnte 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 adaptiert einen Ansatz ausdies ist eine andere Antwortzu ersetzen (nicht-POSIX) -maxdepth 1
. Es erzeugt sh
zuverlässigfind … -exec …
zwei Klauseln verschachteln. Der äußere Teil find
prüft alle Dateien und übergibt sie nacheinander an den inneren Teil find
. Der innere Teil find
findet alle Dateien, die neuer sind als die angegebene Datei, und gibt a
für jede neuere Datei ein einzelnes Zeichen () aus. wc -c
zählt diese Zeichen. Wenn keine vorhanden sind, bedeutet dies, dass die angegebene Datei zuletzt geändert wurde; nur dann find
gibt der äußere Teil sie aus.
Es gibt einige Szenarien, in denen das äußere Laufwerk find
möglicherweise mehr als eine Datei druckt:
- es gibt zwei oder mehr Dateien mit derselben „neuesten“ Mtime;
- Dateien im Verzeichnis werden während der Ausführung des Befehls geändert;
- das Innere
find
kann einige Dateien nicht stat.
Aus diesem Grund wäre -quit
die letzte Aktion des Äußeren find
nützlich (beachten Sie, dass dies auch bei dem Inneren nützlich wäre find
, aber aus einem anderen Grund). Leider -quit
ist dies kein POSIX.
Ich habe -print
( -print0
ist nicht POSIX) verwendet. Nicht standardmäßige Dateinamen sind dennoch kein Problem, da Sie die Ausgabe nicht an einen anderen Befehl weiterleiten müssen. Verwenden Sie einfach, -exec
das alle möglichen Dateinamen richtig behandelt. Beispielsweise -print
verwenden Sie statt:
-exec yet_another_command {} \;
Jetzt wissen Sie, wie Sie die zuletzt geänderte Datei in einem lokalen Verzeichnis finden, ohne sie analysieren zu müssen ls
.
Suchen von Dateien auf einem Remote-System
Egal für welchen Ansatz Sie sich entscheiden (einschließlich des fehlerhaften ls … | tail …
), Sie müssen den Befehl auf dem Remote-System ausführen (oder nicht, auf diese Ausnahme werde ich später eingehen), um die gewünschte Datei in einem Remote-Verzeichnis zu finden.
Der naheliegendste Ansatz ist, ssh
in das Remote-System zu wechseln. Im 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, dass es weitere -s gibt, die durch ersetzt werden müssen, wenn Sie vermeiden cd
und verwenden möchten . Mit ist es viel einfacher .find /source/dir …
.
/source/dir
cd
Ihr lokales System muss per SSH vom Remote-System aus erreichbar sein. Wenn NAT unterwegs ist, können Sie es mit einer Remote-Portweiterleitung umgehen, etwa so:
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 {} [email protected]:"/target/dir/" \;
Beachten Sie, dass der Remote-Port dadurch 12322
zu Ihrem lokalen Port führt sshd
und jeder Benutzer des Remote-Systems versuchen könnte, ihn zu missbrauchen. Ein weiteres Problem: Das Remote-System kann so konfiguriert sein, dass Sie einen Port überhaupt nicht weiterleiten können.
Möglicherweise möchten Sie einen einzelnen Befehl auf dem lokalen System ausführen. In diesem Fall ist eine korrekte Anführungszeichensetzung 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 jedoch Probleme, wenn die Remote scp
nach Ihrem Passwort fragen muss. Aus diesem Grund oder wenn Sie Ihren lokalen Computer nicht ausführen/erreichen können sshd
, benötigen Sie möglicherweise einen anderen Ansatz.
Dieser lokale Befehl druckt den gewünschten Dateinamen, der vom Remote-System abgerufen wurde:
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 verwenden mitlokalTools wie xargs
und scp
. Beachten Sie, dass das Parsen von -print
nur geringfügig besser ist als das Parsen von ls
. Verwenden Sie -print0
(nicht POSIX, möglicherweise nicht verfügbar) oder -exec printf "%s\0" {} \;
(sollte funktionieren) anstelle von -print
, um den gewünschten Dateinamen als nullterminierten String zu erhalten. Jetzt liegt es an Ihnen, was Sie damit auf der lokalen Seite machen. Dies ist nützlich, wenn Sie einen POSIX-kompatiblen Remote-Befehl benötigen, die Tools in Ihrem lokalen System jedoch viele Optionen bieten.
Bemerkenswerte Ausnahme:sshfs
sshfs
ermöglicht Ihnen die Montage usr@remote:"/source/dir/"
als /local/path/
. Siehediese Antwort von mir, ich werde mich nicht wiederholen, um auf alle Details einzugehen. In Ihrem Fall sieht das (lokale) Verfahren mit umfangreichen (nicht auf POSIX beschränkten) Tools 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 großartig. Alles, was Sie tun, tun Sie mitlokalTools. Wenn Sie die Tools nur auf der Remote-Seite verwenden können sshfs
, spielen ihre verfügbaren Optionen keine Rolle mehr. Es spielt auch keine Rolle, ob Sie Ihr lokales System von außen erreichen können. Und die Methode ist dieselbe: Remote zu lokal oder lokal zu Remote, es spielt keine Rolle.