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

321
Neil Philip

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

0

1 ответ на вопрос

0
Kamil Maciorowski

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

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

Похожие вопросы