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

1

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

Neil Philip
fonte

Respostas:

0

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 é finde / 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 cpnã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 cpanálise de opções seja interrompida, por exemplo, um arquivo -Rnã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 scpvez 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 shconfiável de duas find … -exec …cláusulas . O externo findexamina todos os arquivos e os fornece um a um para o interior find. O interno findlocaliza todos os arquivos mais recentes que o arquivo especificado, imprimindo um único caractere ( a) para cada arquivo mais recente. wc -cconta esses caracteres. Se não houver, significa que o arquivo fornecido foi o modificado mais recentemente; somente então o exterior o findimprime.

Existem alguns cenários em que o externo findpode 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 findnão pode declarar alguns arquivos.

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

Eu usei -print( -print0nã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 -execque lida 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 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 é sshentrar 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 cde 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 12322leve ao seu local sshde 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 scpprecisar 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 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 -printobter 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

sshfspermite 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 sshfsas 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.

Kamil Maciorowski
fonte