どこでも見れるメモ帳

とあるSEの備忘録。何かあれば気軽にコメントください〜

ローカルのシェルスクリプトをリモート実行する

はじめに

f:id:ni66ling:20160611155037p:plain

ローカルのスクリプトをリモートで実行したいけどどうやれば?という話.
どうやるのが定番なのか?は結局わかっていないが,一応できるにはできたのでメモ.

やりかた

sshコマンドにパイプでローカルのスクリプトを引き渡す.*1

$ cat [local_script.sh] | ssh [user]@[host] bash

補足: ローカルにおける変数をリモートスクリプト内で使うには

プロセス置換*2を用いる.

$ param="local"
$ cat <(echo -e "param=\"${param}\"\n") [local_script.sh] | ssh [user]@[host] bash

ただし,懸念点として,リモート実行するスクリプト内容が分散し,見通しが悪くなってしまう問題がある.
もっといい方法をご存知の方がいらっしゃったらお教えいただけると嬉しいです.

補足: cronやjenkinsからの自動実行するには

パスワード認証を自動突破するためにexpectを用いる.*3
ソースコードで示すと以下のとおり.

#!/bin/sh
# ローカルのスクリプト(リモート実行する)
remote_command_file="./local_script.sh"

# expectでSSH接続 ([hoge]は適宜読み替えて)
expect -c "
  set timeout -1
  log_file $log_file
  spawn bash -c \"cat $remote_command_file | ssh [user]@[host] bash\"
  expect \"password\" ; send \"[password]\n\"
  expect eof  # :interactとしないこと
  exit        # :interactとしないこと
"

expectに関する注意として,interactとはせず,expect eof; exitとすること.
ターミナルから実行する場合はinteractで良いが,cronやjenkinsから実行する場合はexpect eof; exitとする必要がある.*4*5

*1:注意点として,パイプで渡されるsshでは,bashを引数にすること.bashを引数にしなくても実行はできるが,標準エラー「Pseudo-terminal will not be allocated because stdin is not a terminal.」が出力されてしまう.また,さらに補足すると,この標準エラーは多段接続時に「-t」パラメータを指定しなかった場合にも出力される.

*2:http://sechiro.hatenablog.com/entries/2013/08/15

*3:公開鍵認証かつパスワードなしの設定であれば,expectを使う必要はない.$ ssh -i [鍵path] [user]@[host] bash, もしくは, sshのconfigファイルを読ませるなど

*4:http://yu-write.blogspot.jp/2013/11/shell-crontabexpect.html

*5:ここでかなりハマった…