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 é find
e/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 cp
nã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 cp
a análise de opções seja interrompida, por exemplo, um arquivo -R
nã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 scp
em 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 sh
de forma confiávelaninhar duas find … -exec …
cláusulas. O externo find
testa todos os arquivos e os fornece um por um ao interno find
. O interno find
encontra todos os arquivos mais recentes que o arquivo fornecido, imprimindo um único caractere ( a
) para cada arquivo mais recente. wc -c
conta esses caracteres. Se não houver nenhum, significa que o arquivo fornecido foi o modificado mais recentemente; só então o exterior find
imprime.
Existem poucos cenários em que o exterior find
pode 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
find
não pode registrar alguns arquivos.
Por esta razão -quit
, a ação final do exterior find
seria útil (observe que seria útil find
também com o interior, mas por um motivo diferente). Infelizmente -quit
não é POSIX.
Eu usei -print
( -print0
nã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 -exec
isso para lidar 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 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 é ssh
entrar 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 cd
e 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 12322
leve ao seu local sshd
e 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 scp
precisar 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 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
para 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
sshfs
permite 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 sshfs
as 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.