Существует ли простая команда для 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 . …
Начнем с того .
наверняка так --
можно смело опускать. Я использовал это только для того, чтобы указать на хорошую общую практику при работе с 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
не может определить некоторые файлы.
По этой причине -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 {} usr@127.0.0.1:"/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
. Обратите внимание, что парсинг what -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
инструменты на удаленной стороне и их доступные параметры больше не имеют значения. Также не имеет значения, можете ли вы связаться с вашей локальной системой извне. И метод тот же: удаленный локальный или локальный удаленный, это не имеет значения.