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?


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.


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


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.


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.


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)


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).


11

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


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



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

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.


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 '''


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

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"

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


-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.


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.