SCP fora do arquivo modificado mais recentemente em um diretório remoto

SCP fora do arquivo modificado mais recentemente em um diretório remoto

Existe um comando simples para SCP o arquivo modificado mais recentemente em um diretório em um host remoto? Posso descobrir como fazer isso do local para o remoto... algo como: scp ``ls -Art | tail -n 1\`` usr@remote:/var/log/yeet Como posso fazer a mesma coisa, mas do remoto para o local. (Portanto, pegue o arquivo modificado mais recentemente do yeet e copie-o para o host local)

Responder1

Existem alguns problemas aqui.

Análisels

Primeiro de tudo vocênão deveria analisarls. O seu ls -Art | tail -n 1é falho e não pode ser corrigido de forma confiável. Uma substituição confiável padrão é finde/ou funciona com linhas terminadas em nulo.

Além disso, presumo que você precise do arquivo modificado mais recentemente (não de 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 em nulo geralmente não é exigida pelo POSIX. Se suas ferramentas são ricas o suficiente em opções, este é um substituto robusto para 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 ele, você precisa canalizá-lo xargs -0 …, por exemplo:

… | xargs -0r cp -t "/target/dir/" --

ou (se cpnão for compatível -t):

… | xargs -0r -I {} cp -- {} "/target/dir/"

Nestes comandos há muitas coisas não exigidas pelo POSIX (portanto, não portáveis), incluindo --o que faz com que cpa análise de opções seja interrompida, por exemplo, um arquivo -Rnão acionará recursão em vez de ser copiado. Observe que os nomes de arquivos que saem do nosso find . …começo com .certeza --podem ser omitidos com segurança. Usei-o apenas para apontar uma boa prática geral ao lidar com arquivos cp. Outra boa prática é citar caminhos: o literal /target/dir/funcionaria sem as aspas, mas depois de substituir este exemplo pelo caminho de destino específico, você pode precisar de aspas, então estou usando-as de qualquer maneira.

No seu caso (local para remoto), você usaria scpem 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 deesta outra respostapara substituir (não POSIX) -maxdepth 1. Ele gera shde forma confiávelaninhar duas find … -exec …cláusulas. O externo findtesta todos os arquivos e os fornece um por um ao interno find. O interno findencontra todos os arquivos mais recentes que o arquivo fornecido, imprimindo um único caractere ( a) para cada arquivo mais recente. wc -cconta esses caracteres. Se não houver nenhum, significa que o arquivo fornecido foi o modificado mais recentemente; só então o exterior findimprime.

Existem poucos cenários em que o exterior findpode imprimir mais de um arquivo:

  • existem dois ou mais arquivos com o mesmo mtime "mais recente";
  • os arquivos no diretório são modificados enquanto o comando está em execução;
  • o interno findnão pode registrar alguns arquivos.

Por esta razão -quit, a ação final do exterior findseria útil (observe que seria útil findtambém com o interior, mas por um motivo diferente). Infelizmente -quitnão é POSIX.

Eu usei -print( -print0não é POSIX), mas nomes de arquivos fora do padrão não são um problema porque você não precisa canalizar a saída para outro comando. Basta usar -execisso para lidar com todos os nomes de arquivos possíveis; por exemplo, em vez de -printvocê usar:

-exec yet_another_command {} \;

Agora você sabe como encontrar o arquivo modificado mais recentemente em um diretório local sem analisar arquivos ls.

Localizando arquivos em um sistema remoto

Qualquer que seja a abordagem escolhida (incluindo a falha ls … | tail …), você precisa executar o comando no sistema remoto (ou não, abordarei essa exceção mais tarde) para encontrar o arquivo desejado em um diretório remoto.

A abordagem mais óbvia é sshentrar no sistema remoto. No novo contexto, o sistema remoto é local e o seu computador local é remoto. (Aqui estou usando o comando compatível com POSIX acima 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 cde usar find /source/dir …, há mais .-s 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 do sistema remoto. Se houver NAT no caminho, você poderá contorná-lo com um encaminhamento remoto de porta, 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 isso faz com que a porta remota 12322leve ao seu local sshde qualquer usuário do sistema remoto pode tentar usá-la indevidamente. Outro problema: o sistema remoto pode estar configurado para não permitir o encaminhamento de uma porta.

Você pode desejar que um único comando seja invocado no sistema local. Neste caso, é necessária uma cotação adequada e fica ainda mais complicado:

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 scpprecisar solicitar sua senha. Por esse motivo, ou se você não conseguir executar/alcançar seu local sshd, poderá precisar de outra abordagem.

Este comando local imprimirá o nome do arquivo desejado obtido do 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 comlocalferramentas como xargse scp. Observe que analisar o que -printproduz é 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 -printpara obter o nome do arquivo desejado como uma string terminada em nulo. Agora depende de você o que fará com isso no lado local. Isso é útil se você precisar de um comando remoto compatível com POSIX, mas as ferramentas em seu sistema local são ricas em opções.

Exceção notavel:sshfs

sshfspermite que você monte usr@remote:"/source/dir/"como um arquivo /local/path/. Veressa 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 que você faz você faz comlocalferramentas. Se ao menos você pudesse usar sshfsas ferramentas remotamente e as opções disponíveis não importassem 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.

informação relacionada