SCP-файл последнего измененного файла в удаленном каталоге

SCP-файл последнего измененного файла в удаленном каталоге

Есть ли простая команда для 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инструменты на удаленной стороне, и их доступные опции больше не имеют значения. Также не имеет значения, можете ли вы связаться с вашей локальной системой извне. И метод тот же: удаленный к локальному или локальный к удаленному, это не имеет значения.

Связанный контент