Existem alguns problemas aqui.
Análise ls
Antes de tudo, você não deve analisarls
. Você ls -Art | tail -n 1
é defeituoso e não pode ser corrigido com segurança. Uma substituição confiável padrão é find
e / ou trabalha com linhas com terminação nula.
Além disso, suponho que você precise do arquivo modificado mais recentemente (não um diretório) diretamente no diretório atual (não em um subdiretório).
A capacidade de analisar itens de entrada em uma forma de lista terminada por nulo geralmente não é requerida pelo POSIX. Se suas ferramentas são ricas o suficiente com opções, este é um substituto robusto para o seu ls -Art | tail -n 1
:
find . -maxdepth 1 -type f -printf '%T@ %p\0' |
sort -zrn |
head -zn 1 |
cut -z -d " " -f 2-
Ele produz o resultado como um nome de arquivo terminado em nulo. Para fazer algo com isso, você precisa conectá-lo xargs -0 …
, por exemplo:
… | xargs -0r cp -t "/target/dir/" --
ou (se o seu cp
não suportar -t
):
… | xargs -0r -I {} cp -- {} "/target/dir/"
Nesses comandos, muitas coisas não são necessárias para o POSIX (portanto, não portável), incluindo o --
que faz com que a cp
análise de opções seja interrompida, por exemplo, um arquivo -R
não aciona a recursão em vez de ser copiado. Observe que os nomes de arquivos que saem do find . …
começo com .
certeza, portanto, --
podem ser omitidos com segurança. Usei-o apenas para apontar uma boa prática geral ao lidar com eles cp
. Outra boa prática é citar caminhos: o literal /target/dir/
funcionaria sem as aspas, mas depois de substituir este exemplo pelo seu caminho de destino específico, você poderá precisar de aspas, portanto, eu as estou usando de qualquer maneira.
No seu caso (local para remoto), você usaria em scp
vez de cp
. Pode ter suas próprias peculiaridades ao analisar argumentos.
Um comando compatível com POSIX mas com desempenho insatisfatório pode ser:
find . ! -name . -prune -type f -exec sh -c '
[ "$(find . ! -name . -prune -type f -newer "$1" -exec printf a \; |
wc -c)" -eq 0 ]' sh {} \; -print
Ele adapta uma abordagem dessa outra resposta para substituir (não POSIX) -maxdepth 1
. Isso gera um agrupamento sh
confiável de duas find … -exec …
cláusulas . O externo find
examina todos os arquivos e os fornece um a um para o interior find
. O interno find
localiza todos os arquivos mais recentes que o arquivo especificado, imprimindo um único caractere ( a
) para cada arquivo mais recente. wc -c
conta esses caracteres. Se não houver, significa que o arquivo fornecido foi o modificado mais recentemente; somente então o exterior o find
imprime.
Existem alguns cenários em que o externo find
pode imprimir mais de um arquivo:
- existem dois ou mais arquivos com o mesmo mtime "mais novo";
- os arquivos no diretório são modificados enquanto o comando está em execução;
- o interno
find
não pode declarar alguns arquivos.
Por esse motivo -quit
, a ação final do exterior find
seria útil (observe que também seria útil com o interior find
, mas por um motivo diferente). Infelizmente -quit
não é POSIX.
Eu usei -print
( -print0
não é POSIX), os nomes de arquivos ainda não padrão não são um problema, porque você não precisa canalizar a saída para outro comando. Basta usar o -exec
que lida com todos os nomes de arquivos possíveis; por exemplo, em vez de -print
você usar:
-exec yet_another_command {} \;
Agora você sabe como encontrar o arquivo modificado mais recentemente em um diretório local sem analisar ls
.
Localizando arquivos em um sistema remoto
Qualquer que seja a abordagem que você escolher (incluindo a falha ls … | tail …
), é necessário executar o comando no sistema remoto (ou não, chegarei a essa exceção mais tarde) para encontrar o arquivo desejado em um diretório remoto.
A abordagem mais óbvia é ssh
entrar no sistema remoto. No novo contexto, o sistema remoto é local e o computador local é remoto. (Aqui estou usando o comando acima compatível com POSIX como exemplo, mas você pode usar o nosso primeiro find … | sort -z … | head -z … | cut -z … | xargs -0 …
se apenas o sistema remoto suportar todas as opções necessárias).
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/" \;
Observe que se você deseja evitar cd
e usar find /source/dir …
, há mais .
itens que precisam ser substituídos por /source/dir
. É muito mais fácil com cd
.
Você precisa que seu sistema local esteja disponível via SSH a partir do sistema remoto. Se houver NAT a caminho, você pode ignorá-lo com um encaminhamento de porta remota, algo como isto:
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/" \;
Observe que faz com que a porta remota 12322
leve ao seu local sshd
e qualquer usuário do sistema remoto pode tentar usá-la incorretamente. Outro problema: o sistema remoto pode estar configurado para não permitir o encaminhamento de uma porta em primeiro lugar.
Você pode querer que um único comando seja chamado no sistema local. Nesse caso, é necessária uma citação adequada e fica ainda mais difícil:
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/" \;
'
Espero problemas se o controle remoto scp
precisar solicitar sua senha. Por esse motivo ou se você não puder executar / acessar seu local sshd
, poderá precisar de outra abordagem.
Este comando local imprimirá o nome do arquivo desejado obtido no sistema remoto:
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
'
Você pode usá-lo com ferramentas locais como xargs
e scp
. Observe que analisar o que -print
produz é apenas um pouco melhor do que analisar ls
. Use -print0
(não POSIX, pode não estar disponível) ou -exec printf "%s\0" {} \;
(deve funcionar) em vez de -print
obter o nome do arquivo desejado como uma sequência terminada por nulo. Agora cabe a você o que você fará com isso no lado local. Isso é útil se você precisar de um comando remoto compatível com POSIX, mas as ferramentas no sistema local são ricas em opções.
Exceção notavel: sshfs
sshfs
permite montar usr@remote:"/source/dir/"
como um /local/path/
. Veja esta minha resposta , não vou me repetir para cobrir todos os detalhes. No seu caso, o procedimento (local) com ferramentas ricas (não limitadas ao POSIX) é como:
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/"
Isso é ótimo. Tudo o que você faz com as ferramentas locais . Se você puder usar sshfs
as ferramentas no lado remoto e as opções disponíveis, não importam mais. Também não importa se você pode acessar seu sistema local de fora. E o método é o mesmo: remoto para local ou local para remoto, não importa.