Bash
Linux
SSH
Expect

NIS/NFSが設定されているサーバー間をカレントディレクトリを維持して行き来したい

やりたいこと

LAN内に複数台のLinuxサーバーがあって、それらがNIS/NFSでユーザー・ディスク(/home とか)を共有しています。
例えば各サーバーの計算リソースが取り合いになっていて、あるサーバーが使われているから別のサーバーで作業しようとなると、(少なくとも私は)こういうことがしたくなるのですが…

terminal
$ cd ~/foo/bar
$ pwd
/home/user/foo/bar
$ top
# 誰か計算回してるみたい。別のサーバーで作業しよう
$ ssh serverA
user@serverA's password:
Last login: Mon Jan  1 12:34:56 2018 from 192.168.1.1
$ ./do_something.sh
-bash: ./do_something.sh: そのようなファイルやディレクトリはありません
# さっきの場所が自動的にカレントディレクトリになったらいいのにorz
$ pwd
/home/user

まあ、そうなりますよね。
些細なことではありますが、回数が重なると精神的によろしくないので、今のうちに手を打ちましょう。
(ちなみに、ログイン時刻とかユーザー名とかサーバー名とかはすべて架空のものです)

ログイン後にコマンドを実行させたい

ログイン後に自動的にcdコマンドを実行してさっきの場所に飛べないかなー、というのは、(どうやって実現するかは別にして)割とすぐに思いつく方針だと思います。
では、どうすればログイン後にコマンドを実行させられるのでしょうか?

Expect

シェルで「何かが出力されたら自動的に何かを入力する」ということができるツールです。

Expect is a tool for automating interactive applications such as telnet, ftp, passwd, fsck, rlogin, tip, etc. Expect really makes this stuff trivial. Expect is also useful for testing these same applications. And by adding Tk, you can also wrap interactive applications in X11 GUIs.
(http://expect.sourceforge.net/)

  • パスワードプロンプトに対応して自動的にパスワードを入力するとか
  • yes/noを聞かれたら自動的にyesと答えるとか

そして用事が済んだ後は、ユーザーが普通に作業を続けられるように設定できます。
今回はこういうのを作ってみましょう。

  • パスワードを聞かれたら自動的にパスワードを入力
  • ログインに成功したら自動的に cd <ログイン前のカレントディレクトリ> を入力

本当は、パスワードを入力するときだけユーザーに制御を返してもらえるといいですが、その後にもう一度制御を預ける方法がよくわからなかった(できないかも?)なので、今回はパスワードも入力させるようにします。

Expectは正直今回の用途以外に使ったことがないので、詳しい解説とかはできません。
いろいろ試行錯誤してできた方法を載せてみます。
5行目の send "password\n" の部分に、実際のパスワードを入れます。正直あまりベタ書きしたくないので、パスフレーズなしの公開鍵を設定するとかのほうがいいかも。

sshwd
#!/bin/bash
expect -c '
        spawn ssh serverA
        expect "s password:"
        send "password\n"
        expect "\\\$"
        send "cd '${PWD}'\n"
        interact
'
  • s password: という文字列が見えたら password\n を入力する
  • その後 $ という文字列が見えたら cd <ログイン前のカレントディレクトリ>\n を入力する

ということをやってくれます。
あとはsshwdに実行権限を付けて、置いた場所にパスを通しておくと、こんな感じで使えるようになります。

terminal
$ cd ~/foo/bar
$ pwd
/home/user/foo/bar
$ top
# 誰か計算回してるみたい。別のサーバーで作業しよう
$ sshwd
spawn ssh serverA
user@serverA's password: # ここは自分で入力しなくても勝手に進む
Last login: Mon Jan  1 12:34:56 2018 from 192.168.1.1
$ cd /home/user/foo/bar # これも勝手に表示されて勝手に進む
# ここから自分で操作できる
$ pwd
/home/user/foo/bar

地味にこういうのが欲しかった!

ログイン先を外部から与えたい

さて、今回はログイン先のサーバーが決め打ちになっていますが、普通は多数のサーバーが存在していて、引数で指定したサーバーに入りたい、ということになるはずです。

sshwd2
#!/bin/bash
set -u # 引数が足りなかったらエラーにする
SERVER=$1
expect -c '
        spawn ssh '${SERVER}'
        expect "s password:"
        send "password\n"
        expect "\\\$"
        send "cd '${PWD}'\n"
        interact
'

シェルスクリプトで引数を取ってexpectの引数に渡すように変えただけです。
使い方は以下。

terminal
$ cd ~/foo/bar
$ pwd
/home/user/foo/bar
$ top
# 誰か計算回してるみたい。別のサーバーで作業しよう
$ sshwd2 serverA
spawn ssh serverA
user@serverA's password: # ここは自分で入力しなくても勝手に進む
Last login: Mon Jan  1 12:34:56 2018 from 192.168.1.1
$ cd /home/user/foo/bar # これも勝手に表示されて勝手に進む
# ここから自分で操作できる
$ pwd
/home/user/foo/bar

参考サイト