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

45

Note que não posso primeiro armazenar o arquivo localmente - é muito grande.

Esta (desagradável) página (rola todo o caminho até o final) parece dar uma resposta, mas estou tendo problemas para desemaranhar a parte específica das unidades de fita:

link

Para tornar isso mais concreto, veja como você acha que 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 de fato não suporta scp - de substituir um traço para o arquivo de origem para dizer a ele para obtê-lo de stdin).

    
por dreeves 21.06.2010 / 21:48

6 respostas

72

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

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

É fácil e direto, contanto que você possa usar o ssh para conectar-se ao terminal remoto.

Você também pode usar nc (NetCat) para transferir os dados. No computador de destino (por exemplo, host.example.com):

nc -l 1234 > big.txt

Isso configurará nc para ouvir a porta 1234 e copiar qualquer coisa enviada para essa porta para o arquivo big.txt . Então, na máquina de envio:

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

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

No entanto, a solução nc tem algumas desvantagens:

  • Não há autenticação; qualquer um poderia se conectar à porta 1234 e enviar dados para o arquivo.
  • Os dados não são criptografados, como seria com ssh .
  • Se uma das máquinas estiver atrás de um firewall, a porta escolhida deve estar aberta para permitir a conexão e roteada de maneira adequada, especialmente no lado do recebimento.
  • Ambas as extremidades devem ser configuradas de forma independente e simultânea. Com a solução ssh , você pode iniciar a transferência de apenas um dos pontos finais.
por 21.06.2010 / 21:58
13

Usando o ssh:

echo "pretend this is a huge amt of data" | ssh [email protected] 'cat > big.txt'
    
por 21.06.2010 / 21:55
4

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

    
por 21.06.2010 / 21:51
3

Use um pipe FIFO:

mknod mypipe p
scp mypipe destination &
ls > mypipe
    
por 21.06.2010 / 21:52
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 oferecendo sftp, mas não um shell completo. Então, usar ssh + cat não foi possível. Eu criei uma solução que usa o flag "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"\'"\
   "
    
por 25.02.2018 / 09:47
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 atual já está criptografado com o openssl, na verdade não precisamos do scp. O linux incorporado em ftp também possui excelentes recursos de tubulação. Então aqui está 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
    
por 20.04.2018 / 15:19

Tags