Есть ли простая команда для SCP последнего измененного файла в каталоге на удаленном хосте? Я могу понять, как сделать это с локального на удаленный... что-то вроде:
scp ``ls -Art | tail -n 1\`` usr@remote:/var/log/yeet
Как я могу сделать то же самое, но с удаленного на локальный. (Так что возьмите последний измененный файл с yeet и скопируйте его на локальный хост)
решение1
Здесь есть несколько проблем.
Разборls
Прежде всего, выне следует анализироватьls
. Ваш ls -Art | tail -n 1
имеет изъян и не может быть надежно исправлен. Стандартная надежная замена — это find
и/или работа с линиями с нулевым завершением.
Кроме того, я предполагаю, что вам нужен последний измененный файл (не каталог) непосредственно в текущем каталоге (а не в подкаталоге).
Возможность анализа элементов ввода в виде списка с нулевым окончанием обычно не требуется POSIX. Если ваши инструменты достаточно богаты опциями, это надежная замена для вашего ls -Art | tail -n 1
:
find . -maxdepth 1 -type f -printf '%T@ %p\0' |
sort -zrn |
head -zn 1 |
cut -z -d " " -f 2-
Это дает результат в виде имени файла с нулевым окончанием. Чтобы что-то с ним сделать, нужно передать его по конвейеру xargs -0 …
, например:
… | xargs -0r cp -t "/target/dir/" --
или (если ваш cp
не поддерживает -t
):
… | xargs -0r -I {} cp -- {} "/target/dir/"
В этих командах есть много вещей, не требуемых POSIX (и, следовательно, непереносимых), включая --
то, что останавливает cp
синтаксический анализ для опций, так что, например, файл -R
не вызовет рекурсию вместо копирования. Обратите внимание, что имена файлов, выходящие за пределы нашего find . …
begin with, .
наверняка, поэтому их --
можно смело опускать. Я использовал это только для того, чтобы указать на хорошую общую практику при работе с cp
. Еще одна хорошая практика — это заключение путей в кавычки: литерал /target/dir/
будет работать без кавычек, но после замены этого примера на ваш конкретный целевой путь вам могут понадобиться кавычки, поэтому я все равно их использую.
В вашем случае (локально-удалённо) вы бы использовали scp
вместо cp
. Он может иметь свои особенности при разборе аргументов.
Совместимая с POSIX, но плохо работающая команда может быть:
find . ! -name . -prune -type f -exec sh -c '
[ "$(find . ! -name . -prune -type f -newer "$1" -exec printf a \; |
wc -c)" -eq 0 ]' sh {} \; -print
Он адаптирует подход изэто еще один ответдля замены (не POSIX) -maxdepth 1
. Он порождает sh
надежногнездо из двух find … -exec …
предложений. Внешний find
проверяет все файлы и по одному передает их внутреннему find
. Внутренний find
находит все файлы, более новые, чем указанный файл, печатая один символ ( a
) для каждого более нового файла. wc -c
подсчитывает эти символы. Если их нет, это означает, что указанный файл был изменен последним; только тогда внешний find
печатает его.
Существует несколько сценариев, когда внешний принтер find
может распечатать более одного файла:
- имеется два или более файлов с одинаковым «новейшим» mtime;
- файлы в каталоге изменяются во время выполнения команды;
- внутренний
find
не может stat некоторые файлы.
По этой причине -quit
в качестве конечного действия внешнего find
было бы полезно (заметьте, это было бы полезно и с внутренним find
, но по другой причине). К сожалению, -quit
это не POSIX.
Я использовал -print
( -print0
не POSIX), но нестандартные имена файлов не являются проблемой, поскольку вам не нужно передавать вывод в другую команду. Просто используйте, -exec
которая имеет дело со всеми возможными именами файлов правильно; например, вместо -print
вы используете:
-exec yet_another_command {} \;
Теперь вы знаете, как найти последний измененный файл в локальном каталоге без анализа ls
.
Поиск файлов на удаленной системе
Какой бы подход вы ни выбрали (включая некорректный ls … | tail …
), вам необходимо выполнить команду на удаленной системе (или нет, я вернусь к этому исключению позже), чтобы найти нужный файл в удаленном каталоге.
Самый очевидный подход — ssh
в удаленную систему. В новом контексте удаленная система является локальной, а ваш локальный компьютер — удаленным. (Здесь я использую приведенную выше команду, совместимую с POSIX, в качестве примера, но вы можете использовать нашу первую, find … | sort -z … | head -z … | cut -z … | xargs -0 …
если только удаленная система поддерживает все требуемые опции).
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/" \;
Обратите внимание, если вы хотите избежать cd
и использовать, find /source/dir …
то есть больше .
-s, которые нужно заменить на /source/dir
. С . Это намного проще cd
.
Вам нужно, чтобы ваша локальная система была доступна через SSH с удаленной. Если на пути есть NAT, вы можете обойти его с помощью удаленного перенаправления портов, что-то вроде этого:
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/" \;
Обратите внимание, что это делает удаленный порт 12322
ведущим к вашему локальному, sshd
и любой пользователь удаленной системы может попытаться использовать его не по назначению. Другая проблема: удаленная система может быть настроена так, чтобы запретить вам пересылать порт в первую очередь.
Вам может понадобиться одна команда для вызова в локальной системе. В этом случае требуется правильное кавычивание, и это становится еще более громоздким:
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/" \;
'
Я ожидаю проблем, если удаленный scp
должен запросить ваш пароль. По этой причине или если вы не можете запустить/достучаться до своего локального sshd
, вам может понадобиться еще один подход.
Эта локальная команда выведет желаемое имя файла, полученное из удаленной системы:
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
'
Вы можете использовать его сместныйинструменты вроде xargs
и scp
. Обратите внимание, что разбор того, что -print
выдает, лишь немного лучше разбора ls
. Используйте -print0
(не POSIX, может быть недоступно) или -exec printf "%s\0" {} \;
(должно работать) вместо , -print
чтобы получить желаемое имя файла в виде строки с нулевым завершением. Теперь вам решать, что вы будете делать с ним на локальной стороне. Это полезно, если вам нужна удаленная команда, совместимая с POSIX, но инструменты в вашей локальной системе богаты опциями.
Примечательное исключение:sshfs
sshfs
позволяет монтировать usr@remote:"/source/dir/"
как /local/path/
. Смотритеэтот мой ответ, я не буду повторяться, чтобы охватить все детали. В вашем случае (локальная) процедура с богатыми (не ограниченными POSIX) инструментами выглядит так:
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/"
Это здорово. Все, что вы делаете, вы делаете сместныйинструменты. Если только вы можете использовать sshfs
инструменты на удаленной стороне, и их доступные опции больше не имеют значения. Также не имеет значения, можете ли вы связаться с вашей локальной системой извне. И метод тот же: удаленный к локальному или локальный к удаленному, это не имеет значения.