Comment scp la (énorme) sortie d'une commande directement sur une machine distante?

50

Notez que je ne peux pas d'abord stocker le fichier localement - il est trop gros.

Cette page (odieuse) (faites défiler l'écran jusqu'en bas) semble donner une réponse, mais je ne parviens pas à démêler la partie spécifique aux lecteurs de bande:

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

Pour rendre cela plus concret, voici comment cela pourrait fonctionner:

Sur la machine locale:

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

(Cela utilise la convention - ce que scp ne supporte pas en fait - de substituer un tiret au fichier source pour lui dire de l'obtenir de stdin à la place.)

dreeves
la source
Pouvez-vous publier l'URL de votre résultat Google? Experts Exchange ne montre la réponse au bas de l'écran que si votre parrain est Google ...
Jon Le

Réponses:

76

Vous pouvez diriger vers SSH et exécuter une commande à distance. Dans ce cas, la commande distante consiste à cat > big.txtcopier stdin dans le big.txtfichier.

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

C'est simple et facile, tant que vous pouvez utiliser ssh pour vous connecter à l'extrémité distante.

Vous pouvez également utiliser nc(NetCat) pour transférer les données. Sur la machine réceptrice (par exemple, host.example.com):

nc -l 1234 > big.txt

Ceci sera configuré ncpour écouter le port 1234 et copier dans le big.txtfichier tout élément envoyé à ce port . Ensuite, sur la machine d'envoi:

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

Cette commande indiquera ncau côté émetteur de se connecter au port 1234 du récepteur et de copier les données de stdin sur le réseau.

Cependant, la ncsolution présente quelques inconvénients:

  • Il n'y a pas d'authentification. n'importe qui peut se connecter au port 1234 et envoyer des données au fichier.
  • Les données ne sont pas cryptées, comme ce serait le cas avec ssh.
  • Si l'une des machines est derrière un pare-feu, le port choisi devra être ouvert pour permettre la connexion et être acheminé correctement, en particulier du côté du destinataire.
  • Les deux extrémités doivent être configurées indépendamment et simultanément. Avec la sshsolution, vous pouvez lancer le transfert à partir d'un seul des points d'extrémité.
Barry Brown
la source
Si cela peut vous consoler, je suis enclin à marquer le vôtre comme accepté, car cela explique bien ce qui se passe réellement. (Si vous voulez vraiment le remporter, vous pouvez inclure les solutions FIFO pipe et netcat avec quelques indications sur les raisons pour lesquelles vous préférez l'une ou l'autre! :)
dreeves
Terminé. Netcat est un utilitaire pratique. :)
Barry Brown
Comme dans l'autre commentaire, si vous passez au goudron, vous pouvez utiliser un processus de substitution:tar -cvzf >(ssh destination 'cat > file') huge_directory_tree
Taywee
1
SSH est en effet la voie à suivre. Par rapport à nccela, il offre également le cryptage et la compression de vos données par défaut et plus important encore: la détection d'erreur. J'ai eu des situations où j'ai utilisé ncavec un pilote réseau défectueux et des données corrompues ont été transmises sans être détectées. SSH échouera dans cette situation, car il ne peut pas décrypter / décompresser les données défectueuses.
jlh
15

Utilisation de ssh:

echo "pretend this is a huge amt of data" | ssh [email protected] 'cat > big.txt'
bpf
la source
Ah, magnifique, merci! Une raison de préférer ceci ou une solution de tuyau FIFO?
dreeves
Je pense que l'approche mknod accomplit la tâche exactement de la même manière, à l'exception du tube nommé.
bpf
4

Utilisez nc (Net Cat), qui n'a pas besoin de sauvegarder le fichier localement.

mcandre
la source
Ah merci! Voulez-vous inclure l'équivalent de mon exemple "echo .. | scp .."? Et avez-vous des raisons de préférer cela aux autres réponses?
dreeves
2
Je recommande fortement de ne pas utiliser ncpour cela. Une fois, j'ai transféré une image de disque brute d'une machine à une autre, mais j'ai appris beaucoup plus tard que mon pilote de réseau était défectueux et que des bits défectueux avaient été transférés. Utilisez scp, sshou toute autre chose qui vous dira quand il y a une erreur de transmission.
jlh
2

Utilisez un tuyau FIFO:

mknod mypipe p
scp mypipe destination &
ls > mypipe
Sjoerd
la source
Je ne pouvais pas que cela fonctionne sur un système Linux. scps’est plaint que mypipe n’était pas un fichier ordinaire.
Barry Brown
1
N'a pas fonctionné sur un Mac, non plus, pour la même raison. (J'ai dû utiliser mkfifopour créer le tuyau, cependant.)
Barry Brown
1
Je n'ai pas travaillé ici non plus. Mais si cela fonctionne pour vous, et que vous avez bash ou zsh, vous pourriez mieux le faire en utilisant un processus de substitution, comme pour cet exemple:scp <(ls) destination
Taywee
1

Merci Denis Scherbakov!

Quand j’ai essayé votre script sur le nuage Hetzner, j’ai eu

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

Mais seul un fichier sans contenu a été créé. Comme le contenu actuel est déjà chiffré avec openssl, nous n’avons pas besoin de scp. Le Linux intégré a ftpégalement d'excellentes capacités de tuyauterie. Alors voici ma solution (encore tout à fait manuelle):

#!/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
la source
1

Voici une solution alternative:

Tous les exemples ci-dessus suggèrent que ssh + cat suppose que "cat" est disponible sur le système de destination.

Dans mon cas, le système (sauvegarde Hetzner) avait un ensemble d'outils très restrictif offrant sftp, mais pas un shell complet. Donc, utiliser ssh + cat n'était pas possible. Je suis venu avec une solution qui utilise le drapeau "scp -t" non documenté. Le script complet peut être trouvé ci-dessous.

#!/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"\'"\
   "

Mise à jour 2019.05.08:

Selon la demande, vous trouverez ci-dessous une version beaucoup plus simple et plus courte.

#!/bin/sh

# WORKS ON LARGE FILES ONLY

cat filename.ext \
| cat <(echo 'C0600 107374182400 stdin') - \
| ssh [email protected] 'scp -t filename.ext'
Denis Scherbakov
la source
Pouvez-vous améliorer cela en supprimant toutes les choses inutiles et en le réduisant au strict minimum pour savoir comment utiliser scp -t? À l'heure actuelle, vous disposez d'un script complet hautement personnalisé / localisé dans votre environnement. Une bonne chose pour le wiki de Hetzner, mais pas pour le super utilisateur où la plupart des gens cherchent simplement comment canaliser les entrées via scp.
Allquixotic