Como scp a saída (enorme) de um comando diretamente para uma máquina remota?

50

Observe que não consigo armazenar primeiro o arquivo localmente - é muito grande.

Esta página (desagradável) (role até o fim) parece dar uma resposta, mas estou tendo problemas para desembaraçar a parte específica das unidades de fita:

http://webcache.googleusercontent.com/search?q=cache:lhmh960w2KQJ:www.experts-exchange.com/OS/Unix/SCO_Unix/Q_24249634.html+scp+redirect&cd=3&hl=pt_BR&ct=clnk&gl=us

Para tornar isso mais concreto, veja como você acha que isso pode funcionar:

Na máquina local:

% echo "pretend this string is a huge amt of data" | scp - remote.com:big.txt

(Isso está usando a convenção - que o scp na verdade não suporta - de substituir um traço pelo arquivo de origem para pedir para ele obter o stdin.)

dreeves
fonte
Você pode postar o URL do seu resultado do google? Experts Exchange só mostra a resposta na parte inferior se o seu referencial é o Google ...
Jon

Respostas:

76

Você pode canalizar para o ssh e executar um comando remoto. Nesse caso, o comando remoto é o cat > big.txtque copiará stdin no big.txtarquivo.

echo "Lots of data" | ssh [email protected] 'cat > big.txt'

É fácil e direto, desde que você possa usar o ssh para conectar-se à extremidade remota.

Você também pode usar nc(NetCat) para transferir os dados. Na máquina receptora (por exemplo, host.example.com):

nc -l 1234 > big.txt

Isso será configurado ncpara escutar a porta 1234 e copiar qualquer coisa enviada para essa porta para o big.txtarquivo. Em seguida, na máquina de envio:

echo "Lots of data" | nc host.example.com 1234

Este comando informará ncno lado de envio para conectar-se à porta 1234 no receptor e copiar os dados do stdin pela rede.

No entanto, a ncsolução tem algumas desvantagens:

  • Não há autenticação; qualquer um poderia conectar-se à porta 1234 e enviar dados para o arquivo.
  • Os dados não são criptografados, como seria com ssh.
  • Se qualquer uma das máquinas estiver protegida por um firewall, a porta escolhida deverá ser aberta para permitir a conexão e o roteamento corretamente, principalmente na extremidade receptora.
  • Ambas as extremidades devem ser configuradas independentemente e simultaneamente. Com a sshsolução, você pode iniciar a transferência de apenas um dos pontos de extremidade.
Barry Brown
fonte
Se houver algum consolo, estou inclinado a marcar o seu como aceito, uma vez que explica bem o que realmente está acontecendo. (Se você quiser realmente conquistar isso, você pode incluir as soluções de tubos e netcat FIFO com alguma orientação sobre por que você pode preferir um ou outro :)!
dreeves
Feito. O Netcat é um utilitário útil. :)
Barry Brown
Como em outro comentário, se você está canalizando através tar, você pode usar uma substituição processo:tar -cvzf >(ssh destination 'cat > file') huge_directory_tree
Taywee
1
SSH é realmente o caminho a percorrer. Comparado a ncele, também oferece criptografia e compactação de seus dados por padrão e mais importante: detecção de erros. Tive situações em que usei nccom um driver de rede com defeito e os dados corrompidos foram transmitidos sem serem detectados. O SSH falhará nessa situação, porque não pode descriptografar / descompactar dados defeituosos.
jlh
15

Usando ssh:

echo "pretend this is a huge amt of data" | ssh [email protected] 'cat > big.txt'
bpf
fonte
Ah linda, obrigada! Alguma razão para preferir esta solução de tubo ou FIFO?
dreeves
Eu acho que a abordagem mknod realiza a tarefa exatamente da mesma maneira, exceto pelo pipe nomeado.
bpf
4

Use nc (Net Cat), que não precisa salvar o arquivo localmente.

mcandre
fonte
Ah obrigada! Deseja incluir o equivalente ao meu exemplo "echo .. | scp .."? E alguma razão que você conhece para preferir isso às outras respostas?
dreeves
2
Eu recomendo fortemente contra o uso ncpara isso. Certa vez, joguei uma imagem de disco bruto de uma máquina para outra, apenas para descobrir muito mais tarde que meu driver de rede estava com defeito e transferi bits com defeito. Use scp, sshou qualquer outra coisa que lhe dirá quando há um erro de transmissão.
jlh
2

Use um tubo FIFO:

mknod mypipe p
scp mypipe destination &
ls > mypipe
Sjoerd
fonte
Não consegui fazer isso funcionar em um sistema Linux. scpreclamou que o mypipe não era um arquivo normal.
Barry Brown
1
Também não funcionou em um Mac, pelo mesmo motivo. (Eu tinha que usar mkfifopara criar o tubo, no entanto.)
Barry Brown
1
Também não funcionou aqui. Mas se ele não funciona para você, e você tem bash ou zsh, você poderia melhor conseguir isso com uma substituição processo, como por exemplo:scp <(ls) destination
Taywee
1

Thanx Denis Scherbakov!

Quando tentei seu script na nuvem Hetzner, obtive

debug1: Sending command: scp -v -t backup-20180420120524.tar.xz.enc
debug1: client_input_channel_req: channel 0 rtype exit-status reply 0
debug1: channel 0: free: client-session, nchannels 1
debug1: fd 0 clearing O_NONBLOCK
Transferred: sent 4168, received 2968 bytes, in 0.0 seconds
Bytes per second: sent 346786.6, received 246944.0

Mas apenas um arquivo sem conteúdo foi criado. Como o conteúdo real já está criptografado com o openssl, na verdade não precisamos do scp. O linux embutido ftptambém possui ótimos recursos de tubulação. Então aqui está a minha solução (ainda bastante manual):

#!/bin/bash

function join_e
{
  for word in $*; do
    echo -n "--exclude=$word "
  done
}


# Directory and file inclusion list
ILIST=(
  /home
)

# Directory and file exclusion list
ELIST=(
  var/lib/postgresql
)



export OPASS=fileencryptionpassword

nice -n 19 bash -c \
   "\
   tar $(join_e ${ELIST[@]}) -cpvf - -C / ${ILIST[*]} \
   | xz -c9e -T8 \
   | openssl enc -aes-256-cbc -pass env:OPASS \
   "

# decrypt with:
# cat backup.tar.xz.enc | openssl  aes-256-cbc -d  -pass env:OPASS | xz -dc | tar xv

# invocation procedure for ftp:
# $ ftp -np
# ftp> open storage.com
# ftp> user  storageuser storagepass
# ftp> put "| bash ~/backup.sh" backup.tar.xz.enc
Johannes Winter
fonte
1

Aqui está uma solução alternativa:

Todos os exemplos acima sugerindo ssh + cat assumem que "cat" está disponível no sistema de destino.

No meu caso, o sistema (backup Hetzner) tinha um conjunto muito restritivo de ferramentas que ofereciam sftp, mas não um shell completo. Portanto, não foi possível usar o ssh + cat. Eu vim com uma solução que usa sinalizador "scp -t" não documentado. O script completo pode ser encontrado abaixo.

#!/bin/bash

function join_e
{
  for word in $*; do
    echo -n "--exclude=$word "
  done
}

CDATE=`date +%Y%m%d%H%M%S`

# Make password available to all programs that are started by this shell.
export OPASS=YourSecretPasswrodForOpenSslEncryption

#-----------------------------------------------

# Directory and file inclusion list
ILIST=(
  var/lib
)

# Directory and file exclusion list
ELIST=(
  var/lib/postgresql
)

# 1. tar: combine all files into a single tar archive
#      a. Store files and directories in ILIST only.
#      b. Exclude files and directories from ELIST.
# 2. xz: compress as much as you can utilizing 8 threads (-T8)
# 3. openssl: encrypt contents using a password stored in OPASS local environment variable
# 4. cat: concatenate stream with SCP control message, which has to be sent before data
#      a. C0600 - create a file with 600 permissions
#      b. 107374182400 - maximum file size
#         Must be higher or equal to the actual file size.
#         Since we are dealing with STDIN, we have to make an educated guess.
#         I've set this value to 100x times my backups are.
#      c. stdin - dummy filename (unused)
# 5. ssh: connect to the server
#      a. call SCP in stdin (-t) mode.
#      b. specify destination filename

nice -n 19 bash -c \
   "\
   tar $(join_e ${ELIST[@]}) -cpf - -C / ${ILIST[*]} \
   | xz -c9e -T8 \
   | openssl enc -aes-256-cbc -pass env:OPASS \
   | cat <(echo 'C0600 107374182400 stdin') - \
   | ssh [email protected] "\'"scp -t backup-${CDATE}.tar.xz.enc"\'"\
   "

Atualização 2019.05.08:

Conforme a solicitação, abaixo está uma versão muito mais simples e mais curta.

#!/bin/sh

# WORKS ON LARGE FILES ONLY

cat filename.ext \
| cat <(echo 'C0600 107374182400 stdin') - \
| ssh [email protected] 'scp -t filename.ext'
Denis Scherbakov
fonte
Você pode melhorar isso removendo todas as coisas desnecessárias e reduzindo isso ao simples exemplo mínimo de como usar scp -t? No momento, você possui um script completo altamente personalizado / localizado em seu ambiente. Uma coisa boa para o wiki da Hetzner, mas não para o Superusuário, onde a maioria das pessoas está apenas procurando como canalizar a entrada pelo scp.
allquixotic