Существует ли простая команда для SCP, которая является последним измененным файлом в каталоге на удаленном хосте? Я могу понять, как сделать это с локального на удаленный ... что-то вроде:scp ``ls -Art | tail -n 1\`` usr@remote:/var/log/yeet Как я могу сделать то же самое, но с удаленного на локальный. (Так что получите последний измененный файл от yeet и скопируйте его на локальный хост)

1 ответ1

0

Здесь мало вопросов.

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

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .