sh cópia recursiva (cp -r) - Como excluir uma subpasta

8

Preciso executar um script remoto usando sshvia Ruby( net / ssh ) para copiar recursivamente uma pasta e excluir uma subpasta. Estou procurando a maneira mais rápida de fazê-lo, rsyncnão é bom. Além disso, eu entendo que sshusa she não bash.

No bash eu faço:

cp -r srcdir/!(subdir) dstdir

e funciona bem. No entanto, quando inicio o script via ssh, recebo o erro

sh: 1: Syntax error: "(" unexpected

porque está usando sh.

Eu verifiquei a shpágina de manual, mas não há opção para excluir arquivos.

É minha suposição de sshusar o shcorreto? Alguma sugestão alternativa?

EDIT 1: Caso seja útil, a saída de sudo cat /etc/shellsé a seguinte:

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

EDIT 2: OK. Portanto, o bash está disponível e esse não parece ser o problema. Eu verifiquei que o ssh está realmente usando bash. O problema parece estar relacionado ao escape de parênteses ou ponto de exclamação. Eu tentei executar o comando a partir do shell (macos) e este é o 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'

Dessa forma, recebo um erro diferente

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

EDIT 3: Com base nos comentários, mudei meu comando adicionandoextglob

Se eu usar

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'

Eu recebo o seguinte erro:

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

Se eu não escapar dos parênteses, recebo

bash: -c: line 0: syntax error near unexpected token `('
Rojj
fonte
3
ssh(well sshd) usa o shell de login do usuário remoto. Poderia ser qualquer coisa.
Stéphane Chazelas
O Unix não possui pastas, apenas diretórios. :)
tchrist
1
Em situações como essa, geralmente gosto de desenvolver o script no host remoto e, em seguida, 1) deixá-lo lá, ssh in (programaticamente, se necessário) e executá-lo ou 2) se ele muda sempre, scp-lo, executar via ssh e exclua-o. Um passo extra, talvez, mas você não acaba escapando de pesadelos e globs se expandindo localmente, em vez de remotamente e tudo mais. Caso contrário, eu sempre usaria o formato heredoc como o @ StéphaneChazelas usa abaixo.
Josh Rumbut

Respostas:

10

O SSH executa seu shell de login no sistema remoto, seja o que for. Mas !(foo)requer shopt -s extglob, o que você pode não ter definido no controle remoto.

Tente isso para verificar se o SSH executa o Bash no lado remoto:

ssh me@somehost 'echo "$BASH_VERSION"'

Se isso imprimir alguma coisa, mas os scripts de inicialização não estiverem configurados extglob, você poderá fazê-lo manualmente no comando passado para ssh:

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

extglob afeta a análise da linha de comando e só entra em vigor após uma nova linha; portanto, temos que colocar uma nova linha literal, um ponto e vírgula não é suficiente.

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

Além disso, se você escapar do parêntese com barras invertidas, elas perderão suas propriedades especiais, como qualquer outro caractere glob. Não é isso que você deseja fazer neste caso.

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

Não sei por que você acha que o rsync seria lento. A velocidade de uma cópia é determinada principalmente pela velocidade do disco. O Rsync tem muitas opções para especificar o que você deseja incluir e excluir, por isso oferece um controle muito melhor do que o shell globbing.

Como o manual do bash afirma, o !(patter)somente é reconhecido no bash se extglobestiver definido. No seu exemplo você não definiu extglob. Além disso, um bashiniciado como shainda está bash, mas desativará algumas extensões para compatibilidade.

O servidor SSH começará shell de login do usuário, conforme especificado no /etc/passwd. Você pode alterar o shell ou usá-lo para iniciar outro shell que atenda melhor às suas necessidades.

RalfFriedl
fonte
Eu testei com time. time cp -r mesh/!(constant) N-> 1.04s reais e time rsync -a mesh/ N --exclude=constant-> 1.8s reais
Rojj
7
@Rojj que compara maçãs com laranjas. Por um lado, você está usando -a para rsync, mas não para cp. Isso envolve a preservação de permissões e outros atributos, para que você não esteja fazendo a mesma coisa.
Wildcard
6

Algumas notas primeiro:

  • o servidor ssh não começa sha interpretar a linha de comando enviada pelo cliente, executa o shell de login do usuário no host remoto, como that-shell -c <the-string-provided-by-the-client>. O shell de login do usuário remoto pode ser qualquer coisa. Tenha em mente que algumas conchas gosta tcsh, fishou rctem sintaxe muito diferente da de sh.
  • é realmente uma linha de comando, ou mais exatamente uma string (que pode conter caracteres de nova linha, várias linhas). Mesmo se você fizer ssh host cmd arg1 'arg 2'onde cmd, arg1e arg 2são três argumentos passados para ssh, sshconcatena esses argumentos com espaços e realmente envia a cmd arg1 arg 2string para sshd, eo shell remoto iria dividir isso em cmd, arg1, arge 2.
  • !(subdir)é um operador glob (um kshoperador glob também suportado por zsh -o kshglobe bash -O extglob). Como todos os globs, ele exclui arquivos ocultos, portanto, cuidado, pode haver outros arquivos que ele exclui.

Aqui, para evitar o problema de descobrir a sintaxe correta para o shell remoto, você pode realmente dizer a esse outro shell para iniciar o shell desejado e alimentá-lo com o código via stdin (uma das opções listadas em Como executar um simples arbitrário comando sobre ssh sem conhecer o shell de login do usuário remoto? )

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

bash -O extglob -O dotglobé uma linha de comando que é entendido o mesmo por todas as grandes conchas, incluindo Bourne-like queridos, csh, rc, peixe ... O acima iria trabalhar enquanto bashestá instalado e está em do usuário $PATH(padrão $PATH, possivelmente modificadas pelo utilizador do shell de login como ~/.zshenvfor zsh, ~/.cshrcfor csh, ~/.bashrcfor bash).

POSIXly (embora, na prática, você possa achar que mais sistemas têm um bashcomando do que um paxcomando), você pode:

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

-saplica substituições aos caminhos que estão sendo transferidos. Quando essa substituição se expande para nada, o arquivo é excluído. O problema é que as substituições também se aplicam ao destino dos links simbólicos. É por isso que usamos .//.acima para reduzir a probabilidade de um link simbólico ser afetado.

Stéphane Chazelas
fonte
4

Eu não acho que sshé limitado a usar sh. Depende do que está instalado no sistema de destino, como o usuário está configurado e em quais shells são permitidos /etc/shells.

Você considerou o chshcomando?

RudiC
fonte
4

Se você deseja fazer isso de maneira rápida, pode ver rsynccom um algoritmo de criptografia diferente. Isso lhe dá a opção de excluir facilmente etc., sem sacrificar a velocidade.

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

juntamente com a adição da arcfourcriptografia à linha que começa com Ciphersin /etc/ssh/ssh_config, se ainda não estiver ativada, fornece uma velocidade aceitável.

AVISO: A arcfourcriptografia é insegura . NÃO execute isso em canais inseguros. Se você estiver preocupado com o acesso ao servidor a partir de canais inseguros usando arcfourcriptografia, altere etc/ssh/ssh_configcom uma parte específica do host para o host de origem - Crie uma Hostseção no ssh_config para o host de origem, use Ciphers arcfour-o para espelhar a -copção acima , que restringe a arcfourcriptografia apenas a este host.

Para detalhes, consulte as ssh_configpáginas de manual.

No entanto, se suas CPUs suportam o conjunto de instruções AES-NI, tente mudar para [email protected] (sim, esse é o nome da cifra, incluindo o @ stuff), que usará o incrivelmente rápido (com AES-NI) AES128 -GCM.

Portanto, com uma CPU compatível com AES-NI, mude "ssh -T -c arcfour -o Compression=no -x"para "ssh -T -c [email protected] -o Compression=no -x"para resultados mais seguros.

Explicação

rsync

  • (Não use -z, é muito mais lento)
  • a: modo de arquivamento - rescursivo, preserva o proprietário, preserva permissões, preserva os tempos de modificação, preserva o grupo, copia links simbólicos como links simbólicos, preserva arquivos do dispositivo.
  • H: preserva links físicos
  • A: preserva ACLs
  • X: preserva atributos estendidos
  • x: não cruze os limites do sistema de arquivos
  • v: aumentar a verbosidade
  • --numeric-ds: não mapeie valores uid / gid por nome de usuário / grupo
  • se você precisar sincronizar, adicione --delete: exclua arquivos estranhos dos diretórios de destino (limpeza diferencial durante a sincronização)
  • --progress: mostra o progresso durante a transferência

ssh

  • T: desative o pseudo-tty para diminuir a carga da CPU no destino.
  • c arcfour: use a criptografia SSH mais fraca, mas mais rápida. É necessário especificar "Cifras arcfour" em sshd_config no destino.
  • o Compression=no: Desative a compactação SSH.
  • x: desativa o encaminhamento do X se estiver ativado por padrão.

A carne está nas sshopções - se você usar apenas rsync -ava -e ssh -T -c arcfour -o Compression=no -x"peça, poderá obter essas velocidades também.


Comparação:

  • 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

Fontes :

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

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

emk2203
fonte
3
Bem, eles parecem estar em execução cp -rno sistema remoto, portanto a criptografia usada pela conexão SSH não é realmente relevante. De qualquer forma, arcfouré considerado um pouco quebrado e o OpenSSH o desativa junto com outros no servidor por padrão desde a versão 6.7 (06-10-2014) . De qualquer forma, ssh -o Ciphers='aes128-ctr'me fornece cerca de 90 MB / s, o que deve ser rápido o suficiente em um link de 1 Gbit / s.
precisa saber é o seguinte
Sim, o arcfour está quebrado, mas não deve ser um shell SEGURO para este caso, mas um 'shell confortável' sem ênfase na criptografia. Eu não usaria isso em conexões inseguras, está correto. Se 'aes128-ctr' for rápido o suficiente, ele pode e deve ser usado.
precisa saber é o seguinte
Veja também minha resposta estendida para uso com CPUs que suportam AES-NI.
emk2203
2

De acordo com meus cálculos, a cópia completa mais rápida está sempre usando 'tar' (aqui assumindo o GNU tarou compatível).

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

E tarpossui inúmeras opções para manipular atributos, permissões e seleção / exclusão de arquivos. Por exemplo, o comando acima exclui a subpasta de nível superior chamada .thumbcache durante a cópia.

Lam Das
fonte
Observe que --exclude=.thumbcacheexclui todos os .thumbcachearquivos, não apenas o de nível superior. Com o GNU tar(não bsdtar), você pode usar --exclude=./.thumbcachepara excluir apenas o .thumbcachearquivo de nível superior .
Stéphane Chazelas