¿Cómo scp la salida (enorme) de un comando directamente a una máquina remota?

50

Tenga en cuenta que no puedo almacenar primero el archivo localmente, es demasiado grande.

Esta página (desagradable) (desplazarse hasta el final) parece dar una respuesta, pero tengo problemas para desenredar la parte específica de las unidades de cinta:

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

Para hacer esto más concreto, así es como podría pensar que podría funcionar:

En máquina local:

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

(Eso está usando la convención, que scp no admite de hecho, de sustituir un guión por el archivo fuente para indicarle que lo obtenga de stdin).

dreeves
fuente
¿Puedes publicar la URL de tu resultado de Google? Experts Exchange solo muestra la respuesta en la parte inferior si su referente es google ...
Jon

Respuestas:

76

Puede canalizar a ssh y ejecutar un comando remoto. En este caso, el comando remoto es el cat > big.txtque copiará stdin en el big.txtarchivo.

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

Es fácil y directo, siempre que pueda usar ssh para conectarse al extremo remoto.

También puede usar nc(NetCat) para transferir los datos. En la máquina receptora (por ejemplo, host.example.com):

nc -l 1234 > big.txt

Esto se configurará ncpara escuchar el puerto 1234 y copiar todo lo enviado a ese puerto al big.txtarchivo. Luego, en la máquina de envío:

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

Este comando le indicará ncal lado emisor que se conecte al puerto 1234 en el receptor y copie los datos de stdin a través de la red.

Sin embargo, la ncsolución tiene algunas desventajas:

  • No hay autenticación; cualquiera podría conectarse al puerto 1234 y enviar datos al archivo.
  • Los datos no están encriptados, como lo estaría con ssh.
  • Si alguna de las máquinas está detrás de un firewall, el puerto elegido debería estar abierto para permitir la conexión y enrutarse correctamente, especialmente en el extremo receptor.
  • Ambos extremos deben configurarse de forma independiente y simultánea. Con la sshsolución, puede iniciar la transferencia desde solo uno de los puntos finales.
Barry Brown
fuente
Si te sirve de consuelo, me inclino a marcar el tuyo como aceptado, ya que explica muy bien lo que realmente está sucediendo. (Si usted quiere hacerse realmente, podría incluir las soluciones de tuberías y netcat FIFO y orientaciones sobre qué es posible que prefiera una u otra :)!
dreeves
Hecho. Netcat es una práctica utilidad. :)
Barry Brown
Como en el otro comentario, si está pasando por alquitrán, puede usar una sustitución de proceso:tar -cvzf >(ssh destination 'cat > file') huge_directory_tree
Taywee
1
SSH es de hecho el camino a seguir. En comparación nc, también ofrece cifrado y compresión de sus datos de forma predeterminada y, lo que es más importante: detección de errores. Tuve situaciones en las que lo usé nccon un controlador de red defectuoso y los datos corruptos se transmitieron sin ser detectados. SSH fallará en esa situación, porque no puede descifrar / descomprimir datos defectuosos.
jlh
15

Usando ssh:

echo "pretend this is a huge amt of data" | ssh [email protected] 'cat > big.txt'
bpf
fuente
Ah, hermosa, gracias! ¿Alguna razón para preferir esta o la solución de tubería FIFO?
Dreeves
Creo que el enfoque mknod lleva a cabo la tarea exactamente de la misma manera, excepto por la tubería con nombre.
bpf
4

Use nc (Net Cat), que no necesita guardar el archivo localmente.

mcandre
fuente
¡Ah gracias! ¿Desea incluir el equivalente de mi ejemplo "echo .. | scp .."? ¿Y alguna razón que conozca para preferir esto a las otras respuestas?
Dreeves
2
Recomiendo encarecidamente no usar ncpara esto. Una vez descargué una imagen de disco sin formato de una máquina a otra solo para descubrir mucho más tarde que mi controlador de red estaba defectuoso y transfería bits defectuosos. Uso scp, ssho cualquier otra cosa que le dirá cuando hay un error de transmisión.
jlh
2

Use una tubería FIFO:

mknod mypipe p
scp mypipe destination &
ls > mypipe
Sjoerd
fuente
No pude hacer que esto funcione en un sistema Linux. scpse quejó de que mypipe no era un archivo normal.
Barry Brown
1
Tampoco funcionó en una Mac por la misma razón. (Sin mkfifoembargo, tuve que usar para crear la tubería).
Barry Brown
1
Tampoco funcionó aquí. Pero si funciona para usted y tiene bash o zsh, podría lograrlo mejor con una sustitución de proceso, como en este ejemplo:scp <(ls) destination
Taywee
1

Gracias Denis Scherbakov!

Cuando probé tu script en la nube Hetzner obtuve

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

Pero solo se creó un archivo sin contenido. Como el contenido real ya está encriptado con openssl, en realidad no necesitamos scp. El Linux incorporado ftptambién tiene grandes capacidades de tubería. Así que aquí está mi solución (todavía 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
fuente
1

Aquí hay una solución alternativa:

Todos los ejemplos anteriores que sugieren ssh + cat suponen que "cat" está disponible en el sistema de destino.

En mi caso, el sistema (copia de seguridad de Hetzner) tenía un conjunto de herramientas muy restrictivo que ofrecía sftp, pero no un shell completo. Entonces, usar ssh + cat no fue posible. Se me ocurrió una solución que utiliza el indicador "scp -t" no documentado. El guión completo se puede encontrar a continuación.

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

Actualización 2019.05.08:

Según la solicitud, a continuación se muestra una versión mucho más simple y más corta.

#!/bin/sh

# WORKS ON LARGE FILES ONLY

cat filename.ext \
| cat <(echo 'C0600 107374182400 stdin') - \
| ssh [email protected] 'scp -t filename.ext'
Denis Scherbakov
fuente
¿Puede mejorar esto eliminando todas las cosas innecesarias y reduciendo esto al mínimo ejemplo de cómo usarlo scp -t? En este momento tiene un script completo altamente personalizado / localizado en su entorno. Una buena cosa para el wiki de Hetzner, pero no para Super User, donde la mayoría de las personas solo buscan cómo canalizar la entrada a través de scp.
allquixotic