sh copia recursiva (cp -r) - Cómo excluir subcarpetas

8

Necesito ejecutar un script remoto usando sshvia Ruby( net / ssh ) para copiar recursivamente una carpeta y excluir una subcarpeta. Estoy buscando la forma más rápida de hacerlo, así rsyncque no es bueno. Además, entiendo que los sshusos shy no bash.

En bash hago:

cp -r srcdir/!(subdir) dstdir

y funciona bien Sin embargo, cuando inicio el script vía ssh, recibo el error

sh: 1: Syntax error: "(" unexpected

Debido a que está utilizando sh.

He revisado la shpágina del manual, pero no hay ninguna opción para excluir archivos.

¿Es mi suposición de sshusar shcorrecto? ¿Alguna sugerencia alternativa?

EDITAR 1: en caso de que sea útil, la salida de sudo cat /etc/shellses la siguiente:

# /etc/shells: valid login shells
/bin/sh
/bin/dash
/bin/bash
/bin/rbash
/usr/bin/tmux
/usr/bin/screen

EDITAR 2: OK. Entonces bash está disponible y ese no parece ser el problema. He verificado que el ssh está usando realmente bash. El problema parece estar relacionado con el escape de paréntesis o signo de exclamación. He intentado ejecutar el comando desde el shell (macos) y este es el comando real:

ssh -i .ssh/key.pem ubuntu@X.X.X.X 'mkdir /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/N; cp -r /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/mesh/!\(constant\) /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/N; ln -s /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/mesh/constant /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/N/constant'

De esta manera recibo un error diferente

cp: cannot stat '/home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/mesh/!(constant)': No such file or directory

EDITAR 3: Basado en los comentarios, he cambiado mi comando agregandoextglob

Si yo uso

ssh -i .ssh/key.pem ubuntu@X.X.X.X 'shopt -s extglob; mkdir /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/N; cp -r /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/mesh/!\(constant\) /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/N; ln -s /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/mesh/constant /home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/N/constant'

Recibo el siguiente error:

cp: cannot stat '/home/ubuntu/OpenFOAM/ubuntu-4.1/run/LES_New-Area_residuals2/mesh/!(constant)': No such file or directory

Si no escapo del paréntesis me sale

bash: -c: line 0: syntax error near unexpected token `('
Rojj
fuente
3
ssh(bueno sshd) usa el shell de inicio de sesión del usuario remoto. Podría ser cualquier cosa.
Stéphane Chazelas
Unix no tiene carpetas, solo directorios. :)
tchrist
1
En situaciones como esta, a menudo me gusta simplemente desarrollar el script en el host remoto, luego 1) dejarlo allí, ssh adentro (programáticamente si es necesario) y ejecutarlo o 2) si cambia cada vez, scp encima, ejecutar a través de ssh y luego bórrelo. Tal vez un paso adicional, pero no terminas con pesadillas escapadas y globos que se expanden localmente en lugar de remotamente y todo eso. De lo contrario, siempre usaría el formato heredoc como @ StéphaneChazelas utiliza a continuación.
Josh Rumbut

Respuestas:

10

SSH ejecuta su shell de inicio de sesión en el sistema remoto, sea lo que sea. Pero !(foo)requiere shopt -s extglob, que puede que no haya configurado en el control remoto.

Pruebe esto para ver si SSH ejecuta Bash en el lado remoto:

ssh me@somehost 'echo "$BASH_VERSION"'

Si eso imprime algo, pero sus scripts de inicio no se configuran extglob, puede hacerlo a mano en el comando pasado a ssh:

ssh me@somehost 'shopt -s extglob
    echo srcdir/!(subdir)'                                 
 # or
ssh me@somehost $'shopt -s extglob\n echo srcdir/!(subdir)'   

extglob afecta el análisis de la línea de comando y solo tiene efecto después de una nueva línea, por lo que tenemos que poner una nueva línea literal allí, un punto y coma no es suficiente.

ssh me @ somehost 'shopt -s extglob; echo srcdir /! (subdir) '

Tampoco es que si escapas del paréntesis con barras invertidas, pierden sus propiedades especiales, como cualquier otro personaje global. Esto no es lo que quieres hacer en este caso.

$ touch foo bar; shopt -s extglob; set +o histexpand
$ echo *
bar foo
$ echo !(foo)
bar
$ echo \*
*
$ echo !\(foo\)
!(foo)
ilkkachu
fuente
10

No sé por qué piensas que rsync sería lento. La velocidad de una copia está determinada principalmente por la velocidad del disco. Rsync tiene muchas opciones para especificar lo que desea incluir y excluir, por lo que le brinda un control mucho mejor que el bloqueo de shell.

Como dice el manual de bash, !(patter)solo se reconoce en bash si extglobestá configurado. En tu ejemplo no lo estableciste extglob. Además, se bashinició como shtodavía bash, pero deshabilitará algunas extensiones por compatibilidad.

El servidor SSH iniciará el shell de inicio de sesión del usuario, como se especifica en /etc/passwd. Puede cambiar el shell o usar ese shell para iniciar otro shell que se adapte mejor a sus necesidades.

RalfFriedl
fuente
Lo probé con time. time cp -r mesh/!(constant) N-> real 1.04s y time rsync -a mesh/ N --exclude=constant-> real 1.8s
Rojj
77
@Rojj es una comparación de manzanas con naranjas. Por un lado, está utilizando -a para rsync pero no para cp. Eso implica la preservación de los permisos y otros atributos, por lo que en realidad no estás haciendo lo mismo.
Comodín el
6

Algunas notas primero:

  • el servidor ssh no comienza sha interpretar la línea de comando enviada por el cliente, ejecuta el shell de inicio de sesión del usuario en el host remoto, como that-shell -c <the-string-provided-by-the-client>. El shell de inicio de sesión del usuario remoto podría ser cualquier cosa. Tenga en cuenta que algunas conchas gustaría tcsh, fisho rctienen muy diferentes sintaxis de la de sh.
  • es realmente una línea de comando, o más exactamente una cadena (que puede contener caracteres de nueva línea, por lo tanto, varias líneas). Incluso si lo hace ssh host cmd arg1 'arg 2', donde cmd, arg1y arg 2son tres argumentos pasados a ssh, sshconcatena los argumentos con espacios y en realidad envía la cmd arg1 arg 2cadena a sshd, y el shell remoto se dividiría en que cmd, arg1, argy 2.
  • !(subdir)es un operador global (un kshoperador global también soportado por zsh -o kshgloby bash -O extglob). Al igual que todos los globs, excluye los archivos ocultos, así que tenga cuidado porque puede haber otros archivos que excluye.

Aquí, para evitar el problema de encontrar la sintaxis correcta para el shell remoto, puedes decirle a ese otro shell que inicie el shell que deseas y alimentar el código a través de stdin (una de las opciones enumeradas en Cómo ejecutar un simple arbitrario comando sobre ssh sin conocer el shell de inicio de sesión del usuario remoto? )

ssh host 'bash -O extglob -O dotglob' << 'EOF'
cp -r srcdir/!(subdir) dstdir/
EOF

bash -O extglob -O dotglobes una línea de comando que todos los shells principales entienden de la misma manera, incluidos los similares a Bourne, csh, rc, fish ... Lo anterior funcionaría mientras bashesté instalado y esté en el usuario $PATH(predeterminado $PATH, posiblemente modificado por el usuario) iniciar sesión como con ~/.zshenvfor zsh, ~/.cshrcfor csh, ~/.bashrcfor bash).

POSIXY (aunque en la práctica, puede encontrar que más sistemas tienen un bashcomando que un paxcomando), puede hacer:

ssh host sh << 'EOF'
cd srcdir && pax -rw -'s|^\.//\./subdir\(/.*\)\{0,1\}$||' .//. /path/to/destdir/
EOF

-saplica sustituciones a las rutas que se transfieren. Cuando esa sustitución se expande a nada, el archivo se excluye. El problema es que las sustituciones también se aplican al objetivo de los enlaces simbólicos. Es por eso que usamos lo .//.anterior para que sea menos probable que un enlace simbólico se vea afectado.

Stéphane Chazelas
fuente
4

No creo que sshse limite al uso sh. Más bien depende de lo que esté instalado en el sistema de destino, cómo está configurado el usuario y qué shells están permitidos /etc/shells.

¿Consideraste el chshcomando?

RudiC
fuente
4

Si desea hacerlo de manera rápida, puede mirar rsynccon un algoritmo de cifrado diferente. Esto le da la opción de excluir fácilmente, etc., a poca velocidad de sacrificio.

rsync -aHAXxv --numeric-ids --progress -e "ssh -T -c arcfour -o Compression=no -x" user@<source>:<source_dir> <dest_dir>

junto con agregar el arcfourcifrado a la línea que comienza Ciphersen /etc/ssh/ssh_config, si no está habilitado, le brinda una velocidad aceptable.

ADVERTENCIA: el arcfourcifrado es inseguro . NO ejecute esto sobre canales inseguros. Si le preocupa el acceso al servidor desde canales inseguros mediante el arcfourcifrado, cambie el etc/ssh/ssh_configcon una parte específica del host para su host de origen: Hostcree una sección en su ssh_config para su host de origen, puede usarla Ciphers arcfourpara reflejar el -cinterruptor anterior , que restringe el arcfourcifrado solo a este host.

Para más detalles, consulte las ssh_configpáginas de manual.

Sin embargo, si sus CPU son compatibles con el conjunto de instrucciones AES-NI, intente cambiar a [email protected] (sí, ese es el nombre del cifrado, incluido el @), que utilizará el AES128 increíblemente rápido (con AES-NI) -GCM.

Entonces, con una CPU que admita AES-NI, cambie "ssh -T -c arcfour -o Compression=no -x"a "ssh -T -c [email protected] -o Compression=no -x"para obtener resultados más seguros.

Explicación

rsync

  • (No lo uses -z, es mucho más lento)
  • a: modo de archivo: rescursivo, conserva el propietario, conserva los permisos, conserva los tiempos de modificación, conserva el grupo, copia los enlaces simbólicos como enlaces simbólicos, conserva los archivos del dispositivo.
  • H: conserva los enlaces duros
  • A: conserva las LCA
  • X: conserva los atributos extendidos
  • x: no cruce los límites del sistema de archivos
  • v: aumentar la verbosidad
  • --numeric-ds: no asigne valores uid / gid por nombre de usuario / grupo
  • si necesita sincronizar, agregue --delete: elimine archivos extraños de los directorios de destino (limpieza diferencial durante la sincronización)
  • --progress: muestra el progreso durante la transferencia

ssh

  • T: apague pseudo-tty para disminuir la carga de la CPU en el destino.
  • c arcfour: use el cifrado SSH más débil pero más rápido. Debe especificar "Ciphers arcfour" en sshd_config en el destino.
  • o Compression=no: Desactiva la compresión SSH.
  • x: desactiva el reenvío X si está activado de forma predeterminada.

La carne está en las sshopciones: si solo usa rsync -avla -e ssh -T -c arcfour -o Compression=no -x"parte, también puede obtener estas velocidades.


Comparación:

  • 13.6 MB / s rsync -az
  • 16.7 MB / s scp -Cr
  • 44.8 MB / s rsync -a
  • 59.8 MB / s sftp
  • 61,2 MB / s scp -r
  • 61,4 MB / s sftp -R 128 -B 65536
  • 62.4 MB / s rsync -a -P -e "ssh -T -c arcfour -o Compression=no -x"
  • 143,5 MB / s scp -r -c arcfour
  • 144,2 MB / s sftp -oCiphers=arcfour

Fuentes :

https://gist.github.com/KartikTalwar/4393116

http://nz2nz.blogspot.com/2018/05/rsync-scp-sftp-speed-test.html

emk2203
fuente
3
Bueno, parecen estar ejecutándose cp -rdentro del sistema remoto, por lo que el cifrado utilizado por la conexión SSH no es realmente relevante. En cualquier caso, arcfourse considera bastante roto y OpenSSH lo desactiva junto con otros en el servidor de forma predeterminada desde la versión 6.7 (06/10/2014) . En cualquier caso, ssh -o Ciphers='aes128-ctr'me da unos 90 MB / s, que deberían ser lo suficientemente rápidos en un enlace de 1 Gbit / s.
ilkkachu
Sí, arcfour está roto, pero no se supone que sea un shell SEGURO para este caso, sino un 'shell cómodo' sin énfasis en el cifrado. No usaría esto sobre conexiones inseguras, eso es correcto. Si 'aes128-ctr' es lo suficientemente rápido, puede y debe usarse en su lugar.
emk2203
Vea también mi respuesta extendida para el uso con CPU que admiten AES-NI.
emk2203
2

Según mis cálculos, la copia completa más rápida siempre usa 'tar' (aquí asumiendo GNU taro compatible).

mkdir -p photos2 &&
  tar -C photos -cf - --exclude=./.thumbcache . |
  tar -C photos2 -xpf -

Y tartiene un montón de opciones para manipular atributos, permisos y selección / exclusión de archivos. Por ejemplo, el comando anterior excluye la subcarpeta de nivel superior llamada .thumbcache durante la copia.

Lam Das
fuente
Tenga en cuenta que --exclude=.thumbcacheexcluye todos los .thumbcachearchivos, no solo el del nivel superior. Con GNU tar(no bsdtar), puede usar --exclude=./.thumbcachepara excluir solo el .thumbcachearchivo de nivel superior .
Stéphane Chazelas