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