SSHを使用してリモートマシンでシェルスクリプトを実行する方法


1223

リモートマシンでシェルスクリプト(windows / Linux)を実行する必要があります。

マシンAとBの両方でSSHを構成しています。スクリプトはマシンAにあり、リモートマシンであるマシンBでコードの一部を実行します。

ローカルコンピューターとリモートコンピューターは、WindowsまたはUnixベースのシステムのいずれかです。

plink / sshを使用してこれを実行する方法はありますか?


6
同じ質問がすでにserverfaultにあります:serverfault.com/questions/215756/…したがって、この質問を移行しても意味がありません。
sleske 2012

9
ただし、サーバー障害に関する質問の回答はそれほど多くありません。たぶん、この質問はそれを置き換える必要があります。
Big McLargeHuge 2013

5
私は個人的にこの答えが好きです:unix.stackexchange.com/questions/87405/…– mikevoermans '08
07/22

27
さらに、sshはソフトウェア開発の主要なツールであるため、明らかに話題になっているはずです。
static_rtti 2015

4
Coffeeとsshの質問は、SOについて同じ程度の話題から外れているわけではありません。再開に投票しました。
Vincent Cantin 2017年

回答:


1194

マシンAがWindowsボックスの場合、-mパラメーターを指定してPlink(PuTTYの一部)を使用すると、リモートサーバーでローカルスクリプトが実行されます。

plink root@MachineB -m local_script.sh

マシンAがUnixベースのシステムである場合は、以下を使用できます。

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

スクリプトをリモートサーバーにコピーして実行する必要はありません。


11
-sオプションを使用する利点はありますか? このmanページ-sは、使用されているかどうかにかかわらず、処理オプションが完了したときに標準入力を処理すると信じるようにします。
aeroNotAuto

79
を必要とするスクリプトの場合はsudo、を実行しssh root@MachineB 'echo "rootpass" | sudo -Sv && bash -s' < local_script.shます。
bradley.ayers 2012

6
@ bradley.ayersは、履歴をスキップするためにコマンドを「スペース」で開始することを忘れないでください(PSを機能HISTCONTROL=ignoreboth or ignorespaceさせるために必要なPS )
derenio

8
@ bradley.ayersすでにrootとしてログインしている場合、どのような状況でsudoが必要になりますか?
ブライアンシュレンカー

21
@Agostinoでは、次のようなパラメーターを追加できます。 ssh root@MachineB ARG1="arg1" ARG2="arg2" 'bash -s' < local_script.sh クレジットは以下の@chubbsondubsの回答に完全に移動します。
Yves Van Broekhoven 2014年

635

これは古い質問で、Jasonの答えはうまくいきますが、これを追加したいと思います。

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

これは、ユーザー入力を必要とするsuおよびコマンドでも使用できます。('エスケープされたヒアドキュメントに注意してください)

編集:この答えはトラフィックのビットを取得し続けているので、私はこのヘレドキュメントの素晴らしい使用にさらに情報を追加します:

あなたはこの構文でコマンドをネストすることができます、そしてそれがネスティングが機能するように見える唯一の方法です(まともな方法で)

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

実際にはtelnet、ftpなどの一部のサービスと会話できます。ただし、ヒアドキュメントはstdinをテキストとして送信するだけで、回線間の応答を待機しないことに注意してください

編集:私はあなたが使用する場合、タブで内部をインデントできることがわかりました<<-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

(これはうまくいくと思います)

http://tldp.org/LDP/abs/html/here-docs.htmlも参照してください


4
次のような行を追加することで、ビットを一時化できます。#$(sleep 5)
Olivier Dulac '22 / 02/22

50
ターミネータ(<<'ENDSSH')を単一引用符で囲むと、文字列は展開されず、変数は評価されません。<<ENDSSHまたは<<"ENDSSH"拡張したい場合にも使用できます。
maackle 2013年

3
ExpectFTPなどの対話型コマンドを自動化する必要がある場合に使用できます。
programaths

5
Pseudo-terminal will not be allocated because stdin is not a terminal.メッセージがあったことに注意してください。これ-t -tを回避するには、sshとparams を使用する必要があります。SOの
Buzut

8
<<-'END'構文を使用する場合は、ヒアドキュメントの終了区切り文字がスペースではなくタブを使用してインデントされていることを確認してください。stackexchangeからのコピー/貼り付けはスペースを与えることに注意してください。それらをタブに変更すると、インデント機能が動作するはずです。
fbicknel 2016年

249

また、宛先ホストから変数を取得する場合は、変数をエスケープすることを忘れないでください。

これは私を過去に引っ張ってきました。

例えば:

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

/ home / user2を出力します

ながら

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

/ home / userを出力します

もう一つの例:

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

「hello」を正しく出力します。


2
しかし、次の点に注意してください ssh user2@host 'bash -s' echo $HOME /home/user2 exit
errant.info

1
セッションでfor実行しているループにそれを追加するためだけにssh、ループ変数をエスケープしてはいけません。
AlexeyDaryin 14

1
多くの状況で、最後の例を修正する正気な方法ssh user2@host2 'echo hello world' | awk '{ print $1 }'は、Awkスクリプトをローカルで実行することです。もちろん、リモートコマンドが膨大な量の出力を生成する場合は、それをすべてローカルサーバーにコピーして戻さないようにします。ちなみに、リモートコマンドを単一引用符で囲むと、エスケープする必要がなくなります。
tripleee

151

これは、YarekTの回答を拡張したもので、インラインリモートコマンドとローカルマシンからリモートホストにENV変数を渡すことで、リモート側でスクリプトをパラメーター化できます。

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

これをすべて1つのスクリプトにまとめ、非常に読みやすく、保守しやすくすることで、これは非常に役立つと思いました。

なぜこれが機能するのか。sshは次の構文をサポートしています。

ssh user @ host remote_command

bashでは、次のように1行でコマンドを実行する前に、定義する環境変数を指定できます。

ENV_VAR_1 = 'value1' ENV_VAR_2 = 'value2' bash -c 'echo $ ENV_VAR_1 $ ENV_VAR_2'

これにより、コマンドを実行する前に変数を簡単に定義できます。この場合、echoは実行中のコマンドです。echoの前のすべてが環境変数を定義します。

したがって、これら2つの機能とYarekTの答えを組み合わせて、次のことを実現します。

ssh user @ host ARG1 = $ ARG1 ARG2 = $ ARG2 'bash -s' << 'ENDSSH' ...

この場合、ARG1とARG2をローカル値に設定しています。user @ host以降のすべてをremote_commandとして送信します。リモートマシンがコマンドARG1とARG2を実行すると、ローカルサーバーの環境変数を定義するローカルコマンドライン評価により、ローカル値が設定され、それらの変数を使用してbash -sコマンドが実行されます。出来上がり。


1
-aのように引数を渡したい場合は、-を使用できます。例: 'ssh user @ host--a foo bar' bash -s '<script.sh'。また、引数はリダイレクトの後に移動することもできます(例: 'ssh user @ host' bash -s '<script.sh--a foo bar')。
2016年

8
もしVARの値はスペース、使用することを含んでENVのいずれか:ssh user@host "ARG1=\"$ARG1\" ARG2=\"$ARG2\"" 'bash -s' <<'ENDSSH'...
TalkLittle

別のコメントで書いたように、使用のポイントは-s、stdinを介して供給されるスクリプトに引数を適用できることです。つまり、使用しない場合は省略してもかまいません。あなたはそれを使用する場合、使用環境変数に理由はありません:ssh user@host 'bash -s value1 value2' <<< 'echo "$@"'
JOL

104
<hostA_shell_prompt>$ ssh user@hostB "ls -la"

hostAユーザーの公開鍵をユーザー.sshのディレクトリのホームにあるauthorized_keysファイルにコピーしていない限り、パスワードの入力を求められます。これにより、パスワードなしの認証が可能になります(sshサーバーの構成で認証方法として受け入れられた場合)


3
投票しました。これは有効なソリューションです。明らかに、キーは保護する必要がありますが、サーバー側を介してパスワードのように無効にすることもできます。
willasaywhat 2008年

8
これが質問に答えるとは思いません。この例では、リモートコマンドを実行する方法を示していますが、リモートマシンでローカルスクリプトを実行する方法は示していません。
Jason R. Coombs

確かではありませんが、このメソッドを使用してhostAでスクリプトをパイプしてhostBで実行できませんか?
nevets1219 2010

27

より洗練された操作のためにファブリックを使い始めました。ファブリックにはPythonと他のいくつかの依存関係が必要ですが、クライアントマシン上でのみ必要です。サーバーはsshサーバーである必要があります。このツールは、SSHに渡されたシェルスクリプトよりもはるかに強力であり、セットアップする手間(特にPythonでのプログラミングを楽しむ場合)に値するものです。ファブリックは、複数のホスト(または特定の役割のホスト)で実行中のスクリプトを処理し、べき等演算(設定スクリプトに行を追加するなど、行がすでにある場合はそうではない)を促進し、より複雑なロジック(Pythonなど)の構築を可能にします言語が提供できる)。


11

実行してみてくださいssh user@remote sh ./script.unx


8
これは、スクリプトがリモートのデフォルト(ホーム)ディレクトリーにある場合にのみ機能します。問題は、リモートにローカルに保存されているスクリプトを実行する方法だと思います。
metasim

1
ssh username @ ip "chmod + x script.sh" <br/> ssh username @ ip "リモートホストのshファイルへのパス"
mani deepak 14年


8

「リモート」マシンに手動でログインせずに、「ローカル」マシンからこれを自動的に実行したい場合、Expectと呼ばれるTCL拡張機能を調べる必要があります。このような状況のために設計されています。SSHを介してログイン/対話するためのスクリプトへのリンクも提供しました。

https://www.nist.gov/services-resources/software/expect

http://bash.cyberciti.biz/security/expect-ssh-login-script/


5

これを使用して、リモートマシンでシェルスクリプトを実行します(/ bin / bashでテスト済み)。

ssh deploy@host . /home/deploy/path/to/script.sh

3
ssh user@hostname ".~/.bashrc;/cd path-to-file/;.filename.sh"

環境ファイル(.bashrc / .bashprofile / .profile)を入手することを強くお勧めします。ターゲットホストとソースホストの環境変数が異なる可能性があるため、リモートホストで何かを実行する前。


これは、ローカルスクリプトをリモートホストに移動する方法を説明していません。
キレラギン

2

でこのコマンドのような temp=`ls -a` echo $temp コマンドを実行したい場合 は、エラーが発生します。

以下のコマンドはこの問題を解決します ssh user@host ''' temp=`ls -a` echo $temp '''


1

ここでの答え(https://stackoverflow.com/a/2732991/4752883)は、plinkまたはを使用してリモートのLinuxマシンでスクリプトを実行しようとしている場合に最適ですssh。スクリプトがに複数行ある場合に機能しますlinux

**ただし、ローカルlinux/windowsマシンにあるバッチスクリプトを実行しようとしていて 、リモートマシンがWindowsであり、それが**を使用する複数の行で構成されている場合

plink root@MachineB -m local_script.bat

動作しません。

スクリプトの最初の行のみが実行されます。これはおそらくの制限ですplink

解決策1:

複数行のバッチスクリプトを実行するには(特に、数行で構成される比較的単純な場合):

元のバッチスクリプトが次のとおりである場合

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

local_script.batファイルで次のように「&&」セパレータを使用して行を組み合わせることができ ます: https : //stackoverflow.com/a/8055390/4752883

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

この変更後、@ JasonR.Coombs:https : //stackoverflow.com/a/2732991/4752883によってここで指摘されているようにスクリプトを実行できます

`plink root@MachineB -m local_script.bat`

解決策2:

バッチスクリプトが比較的複雑な場合は、ここで@Martin https://stackoverflow.com/a/32196999/4752883によって指摘されているように、plinkコマンドをカプセル化するバッチスクリプトを使用することをお勧めします

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

このbashスクリプトは、ターゲットのリモートマシンにsshを実行し、リモートマシンでコマンドを実行します。これを実行する前に(macでbrew install expect)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
あなたがパスワードを使用する必要がある場合、これは素晴らしいです...しかし、自宅で任意のsshコマンドを見て、誰の利益のためではない、パスワード、公開+秘密鍵のペアを使用する必要があります...場所の更新ご使用のSSHサーバーは完全にパスワードを遮断するために一度
スコットステンスランド

-1

runoversshを使用できます。

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

-s ローカルスクリプトをリモートで実行する


便利なフラグ:
-gすべてのホストにグローバルパスワードを使用します(単一パスワードプロンプト)
-nsshpassの代わりにSSHを使用します。公開鍵認証に役立ちます


-24

まず、scpを使用してスクリプトをマシンBにコピーします。

[user @ machineA] $ scp / path / to / script user @ machineB:/ home / user / path

次に、スクリプトを実行します

[user @ machineA] $ ssh user @ machineB "/ home / user / path / script"

これは、スクリプトに実行権限を与えている場合に機能します。


こんにちは私は推奨される提案を適用しましたが、次のエラーが表示されます[oracle @ node1〜] $ ssh oracle @ node2:./ home / oracle / au / fs / conn.sh ssh:node2:./ home / oracle / au / fs / conn.sh:名前またはサービスが不明[oracle @ node1〜] $

'ssh oracle @ node2:./ home / oracle / au / fs / conn.sh'?コマンドラインが間違っている場合は、コマンド名をコロンではなくスペースでuser @ host部分から区切る必要があります。
bortzmeyer 2008

5
コピーしないと実行できないという主な主張が間違っているため、これに反対票を投じています。
ジェイソンR.クームス2010

Jasonが、事実を単に述べるのではなく、なぜそれが正しくないのかを付け加えれば、助かったでしょう。役に立たない。
クリス

[user @ machineA] $ ssh root @ MachineB 'bash -s' </ machinea / path / to / script
Oleksii Kyslytsyn
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.