Como usar o SSH para executar um script de shell em uma máquina remota?

1223

Eu tenho que executar um script de shell (windows / Linux) em uma máquina remota.

Eu tenho o SSH configurado nas máquinas A e B. Meu script está na máquina A, que executará parte do meu código em uma máquina remota, máquina B.

Os computadores locais e remotos podem ser do sistema Windows ou Unix.

Existe uma maneira de executar isso usando plink / ssh?

alpha_989
fonte
6
A mesma pergunta já está em serverfault: serverfault.com/questions/215756/… Portanto, provavelmente não há sentido em migrar esta pergunta.
sleske
9
A pergunta sobre falha do servidor não tem tantas respostas. Talvez essa pergunta deva substituir essa.
Big McLargeHuge
5
Eu gosto desta resposta pessoalmente: unix.stackexchange.com/questions/87405/...
mikevoermans
27
Além disso, obviamente deveria estar no tópico, já que o ssh é uma ferramenta fundamental para o desenvolvimento de software.
static_rtti
4
Perguntas sobre café e ssh não compartilham o mesmo grau de desinteresse no SO. Votou para reabrir.
Vincent Cantin

Respostas:

1194

Se a Máquina A for uma caixa do Windows, você poderá usar o Plink (parte do PuTTY ) com o parâmetro -m, e ele executará o script local no servidor remoto.

plink root@MachineB -m local_script.sh

Se a Máquina A for um sistema baseado em Unix, você poderá usar:

ssh root@MachineB 'bash -s' < local_script.sh

Você não precisa copiar o script para o servidor remoto para executá-lo.

Jason R. Coombs
fonte
11
existe uma vantagem em usar a -sopção? Essa página de manual me leva a acreditar que processará a entrada padrão quando terminar as opções de processamento, -susadas ou não.
aeroNotAuto
79
Para um script que requer sudo, execute ssh root@MachineB 'echo "rootpass" | sudo -Sv && bash -s' < local_script.sh.
Bradley.ayers
6
@ bradley.ayers lembre-se de iniciar o comando com um 'espaço' para saltar a história (PS você precisa ter HISTCONTROL=ignoreboth or ignorespacepara torná-lo trabalho)
derenio
8
@ bradley.ayers em que situações você precisaria do sudo se já estiver fazendo login como root?
Brian Schlenker
21
@Agostino, você pode adicionar parâmetros como este: Os ssh root@MachineB ARG1="arg1" ARG2="arg2" 'bash -s' < local_script.sh créditos vão totalmente para a resposta @chubbsondubs 'abaixo.
Yves Van Broekhoven
635

Esta é uma pergunta antiga e a resposta de Jason funciona bem, mas eu gostaria de acrescentar:

ssh user@host <<'ENDSSH'
#commands to run on remote host
ENDSSH

Isso também pode ser usado com su e comandos que requerem entrada do usuário. (observe o 'heredoc escapado)

Edit: Como esta resposta continua recebendo bits de tráfego, eu adicionaria ainda mais informações a esse maravilhoso uso do heredoc:

Você pode aninhar comandos com esta sintaxe e é a única maneira que o aninhamento parece funcionar (de maneira sã)

ssh user@host <<'ENDSSH'
#commands to run on remote host
ssh user@host2 <<'END2'
# Another bunch of commands on another host
wall <<'ENDWALL'
Error: Out of cheese
ENDWALL
ftp ftp.secureftp-test.com <<'ENDFTP'
test
test
ls
ENDFTP
END2
ENDSSH

Você pode realmente ter uma conversa com alguns serviços como telnet, ftp, etc.

Edit: Acabei de descobrir que você pode recuar o interior com abas se você usar <<-END!

ssh user@host <<-'ENDSSH'
    #commands to run on remote host
    ssh user@host2 <<-'END2'
        # Another bunch of commands on another host
        wall <<-'ENDWALL'
            Error: Out of cheese
        ENDWALL
        ftp ftp.secureftp-test.com <<-'ENDFTP'
            test
            test
            ls
        ENDFTP
    END2
ENDSSH

(Eu acho que isso deve funcionar)

Veja também http://tldp.org/LDP/abs/html/here-docs.html

Yarek T
fonte
4
você pode temporizar um pouco adicionando linhas como: # $ (sleep 5)
Olivier Dulac
50
observe que, com aspas simples ao redor do terminador ( <<'ENDSSH'), as seqüências não serão expandidas, as variáveis ​​não serão avaliadas. Você também pode usar <<ENDSSHou <<"ENDSSH"se desejar expansão.
maackle
3
Expectpode ser usado quando você precisar automatizar comandos interativos como FTP.
Programads
5
Note que eu tive uma Pseudo-terminal will not be allocated because stdin is not a terminal.mensagem. É preciso usar ssh com -t -tparâmetros para evitar isso. Veja este segmento no SO
Buzut
8
Se você estiver tentando usar a sintaxe << - 'END', verifique se o delimitador final do heredoc é recuado usando TABs, não espaços. Observe que copiar / colar da stackexchange fornecerá espaços. Altere-os para guias e o recurso de recuo deve funcionar.
Fbicknel
249

Além disso, não esqueça de escapar das variáveis ​​se quiser buscá-las no host de destino.

Isso me chamou a atenção no passado.

Por exemplo:

user@host> ssh user2@host2 "echo \$HOME"

imprime / casa / usuário2

enquanto

user@host> ssh user2@host2 "echo $HOME"

imprime / casa / usuário

Outro exemplo:

user@host> ssh user2@host2 "echo hello world | awk '{print \$1}'"

imprime "olá" corretamente.

dogbane
fonte
2
No entanto, esteja ciente do seguinte: ssh user2@host 'bash -s' echo $HOME /home/user2 exit
errant.info 30/04
1
Apenas para adicionar que, em forloops em execução na sshsessão, a variável loop não deve ser escapada.
AlexeyDaryin
1
Em muitas situações, a maneira correta de corrigir seu último exemplo seria, por exemplo, ssh user2@host2 'echo hello world' | awk '{ print $1 }'executar o script Awk localmente. Se o comando remoto produzir uma imensa quantidade de saída, você evita copiar tudo de volta para o servidor local, é claro. Aliás, aspas simples ao redor do comando remoto evitam a necessidade de escape.
Tripleee
151

Esta é uma extensão da resposta do YarekT para combinar comandos remotos em linha com a passagem de variáveis ​​ENV da máquina local para o host remoto, para que você possa parametrizar seus scripts no lado remoto:

ssh user@host ARG1=$ARG1 ARG2=$ARG2 'bash -s' <<'ENDSSH'
  # commands to run on remote host
  echo $ARG1 $ARG2
ENDSSH

Achei isso extremamente útil, mantendo tudo em um script, para que seja muito legível e fácil de manter.

Por que isso funciona? O ssh suporta a seguinte sintaxe:

comando ssh user @ host remote_com

No bash, podemos especificar variáveis ​​de ambiente a serem definidas antes da execução de um comando em uma única linha, como segue:

ENV_VAR_1 = 'valor1' ENV_VAR_2 = 'valor2' bash -c 'eco $ ENV_VAR_1 $ ENV_VAR_2'

Isso facilita a definição de variáveis ​​antes da execução de um comando. Nesse caso, echo é nosso comando que estamos executando. Tudo antes do eco define variáveis ​​de ambiente.

Então, combinamos esses dois recursos e a resposta do YarekT para obter:

ssh usuário @ host ARG1 = $ ARG1 ARG2 = $ ARG2 'bash -s' << 'ENDSSH' ...

Nesse caso, estamos configurando ARG1 e ARG2 para valores locais. Enviando tudo após o usuário @ host como o comando remote_com. Quando a máquina remota executa o comando ARG1 e ARG2 são definidos os valores locais, graças à avaliação da linha de comando local, que define variáveis ​​de ambiente no servidor remoto, e executa o comando bash -s usando essas variáveis. Voila.

chubbsondubs
fonte
1
Observe que se você deseja passar argumentos como -a, pode usar -. por exemplo, 'ssh user @ host - -a foo bar' bash -s '<script.sh'. E os argumentos também podem seguir o redirecionamento, por exemplo, 'ssh user @ host' bash -s '<script.sh - -a foo bar'.
gaoithe
8
Se algum dos valores env var contiver espaços, use:ssh user@host "ARG1=\"$ARG1\" ARG2=\"$ARG2\"" 'bash -s' <<'ENDSSH'...
TalkLittle 24/16
Como escrevi em outro comentário, o objetivo de usar -sé poder aplicar argumentos a scripts originados pelo stdin. Quero dizer, você também pode omitir se não for usá-lo. Se você o usar, não há razão para usar variáveis ​​de ambiente:ssh user@host 'bash -s value1 value2' <<< 'echo "$@"'
JoL
104
<hostA_shell_prompt>$ ssh user@hostB "ls -la"

Isso solicitará a senha, a menos que você tenha copiado a chave pública do usuário hostA para o arquivo allowed_keys na casa do diretório do usuário .ssh. Isso permitirá autenticação sem senha (se aceito como um método de autenticação na configuração do servidor ssh)

Vinko Vrsalovic
fonte
3
Votei em você. Esta é uma solução válida. Obviamente, as chaves devem ser protegidas, mas também podem ser invalidadas como uma senha pelo lado do servidor.
willasaywhat
8
Eu não acho que isso responda à pergunta. O exemplo mostra como executar um comando remoto, mas não como executar um script local em uma máquina remota.
Jason R. Coombs
Não tenho certeza, mas você não pode canalizar seu script no hostA para executar no hostB usando esse método?
precisa
27

Comecei a usar o Fabric para operações mais sofisticadas. O Fabric requer Python e algumas outras dependências, mas apenas na máquina cliente. O servidor precisa ser apenas um servidor ssh. Acho que essa ferramenta é muito mais poderosa que os scripts de shell entregues ao SSH e vale a pena o esforço de ser configurada (principalmente se você gosta de programar em Python). O Fabric manipula scripts em execução em vários hosts (ou hosts de determinadas funções), ajuda a facilitar operações idempotentes (como adicionar uma linha a um script de configuração, mas não se já estiver lá) e permite a construção de lógica mais complexa (como o Python idioma pode fornecer).

Jason R. Coombs
fonte
11

Tente correr ssh user@remote sh ./script.unx.

Jeremy
fonte
8
Isso funciona apenas se o script estiver no diretório padrão (inicial) no controle remoto. Eu acho que a questão é como executar um script armazenado localmente no controle remoto.
metasim
1
ssh username @ ip "chmod + x script.sh" <br/> ssh username @ ip "caminho para o arquivo sh em host remoto"
mani deepak
10
cat ./script.sh | ssh <user>@<host>
cglotr
fonte
1
Muito interessante ...
Noah Broyles
5

Eu uso este para executar um script de shell em uma máquina remota (testada em / bin / bash):

ssh deploy@host . /home/deploy/path/to/script.sh
Is Ma
fonte
3
ssh user@hostname ".~/.bashrc;/cd path-to-file/;.filename.sh"

altamente recomendado para a origem do arquivo de ambiente (.bashrc / .bashprofile / .profile). antes de executar algo no host remoto, pois as variáveis ​​de ambiente dos hosts de destino e de origem podem ser adiadas.

Ankush Sahu
fonte
Isso não explica como mover um script local para o host remoto.
kirelagin 16/04
2

se você quiser executar comandos como este temp=`ls -a` echo $temp em `` causará erros.

O comando abaixo resolverá esse problema ssh user@host ''' temp=`ls -a` echo $temp '''

Jinmiao Luo
fonte
1

A resposta aqui ( https://stackoverflow.com/a/2732991/4752883 ) funciona muito bem se você estiver tentando executar um script em uma máquina Linux remota usando plinkor ssh. Funcionará se o script tiver várias linhas ativadas linux.

** No entanto, se você estiver tentando executar um script em lote localizado em uma linux/windowsmáquina local e sua máquina remota estiver Windows, ela consistirá em várias linhas usando **

plink root@MachineB -m local_script.bat

não vai funcionar.

Somente a primeira linha do script será executada. Esta é provavelmente uma limitação de plink.

Solução 1:

Para executar um script em lotes com várias linhas (especialmente se for relativamente simples, consistindo em algumas linhas):

Se o script em lote original for o seguinte

cd C:\Users\ipython_user\Desktop 
python filename.py

você pode combinar as linhas usando o separador "&&" da seguinte maneira no seu local_script.batarquivo: https://stackoverflow.com/a/8055390/4752883 :

cd C:\Users\ipython_user\Desktop && python filename.py

Após essa alteração, você poderá executar o script conforme indicado aqui por @ JasonR.Coombs: https://stackoverflow.com/a/2732991/4752883 com:

`plink root@MachineB -m local_script.bat`

Solução 2:

Se o seu script em lote for relativamente complicado, pode ser melhor usar um script em lote que encapsule o comando plink, bem como a seguir, conforme indicado aqui por @Martin https://stackoverflow.com/a/32196999/4752883 :

rem Open tunnel in the background
start plink.exe -ssh [username]@[hostname] -L 3307:127.0.0.1:3306 -i "[SSH
key]" -N

rem Wait a second to let Plink establish the tunnel 
timeout /t 1

rem Run the task using the tunnel
"C:\Program Files\R\R-3.2.1\bin\x64\R.exe" CMD BATCH qidash.R

rem Kill the tunnel
taskkill /im plink.exe
alpha_989
fonte
1

Esse script bash faz ssh em uma máquina remota de destino e executa algum comando na máquina remota; não esqueça de instalar o expect antes de executá-lo (no mac brew install expect)

#!/usr/bin/expect
set username "enterusenamehere"
set password "enterpasswordhere"
set hosts "enteripaddressofhosthere"
spawn ssh  $username@$hosts
expect "$username@$hosts's password:"
send -- "$password\n"
expect "$"
send -- "somecommand on target remote machine here\n"
sleep 5
expect "$"
send -- "exit\n"
Mohammed Rafeeq
fonte
1
isso é ótimo se você precisar usar senhas ... no entanto, para o benefício de quem estiver assistindo em casa, qualquer comando ssh deve usar um par de chaves públicas + privadas, não senhas ... uma vez no local, atualize seu servidor ssh para desligar completamente as senhas
Scott Stensland
-1

Você pode usar runoverssh :

sudo apt install runoverssh
runoverssh -s localscript.sh user host1 host2 host3...

-s executa um script local remotamente


Sinalizadores úteis:
-g use uma senha global para todos os hosts (prompt de senha única)
-nuse SSH em vez de sshpass, útil para autenticação de chave pública

agora em
fonte
-24

Primeiro, copie o script para a Máquina B usando scp

[user @ machineA] $ scp / path / to / script usuário @ machineB: / home / usuário / caminho

Em seguida, basta executar o script

[usuário @ máquinaA] $ ssh usuário @ máquinaB "/ home / usuário / caminho / script"

Isso funcionará se você der permissão executável ao script.

Baishampayan Ghose
fonte
oi eu apliquei sugestão recomendada, mas ele me deu o seguinte erro [oracle @ node1 ~] $ ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh ssh: node2: ./ home / oracle / au / fs / conn.sh: nome ou serviço desconhecido [oracle @ node1 ~] $
'ssh oracle @ node2: ./ home / oracle / au / fs / conn.sh'? Linha de comando incorreta, o nome do comando deve ser separado da parte do usuário @ host com um espaço, não dois pontos.
Bortzmeyer
5
Estou com o voto negativo porque é a principal reivindicação de que ele não pode ser executado sem a cópia incorreta.
precisa
Teria sido útil se Jason adicionou por que isso está incorreto, em vez de simplesmente declarar o fato. Inútil.
Kris
[user @ machineA] $ ssh root @ MachineB 'bash -s' </ machinea / caminho / para / script
Oleksii Kyslytsyn 30/06/19