1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

VNC越しに起動したアプリとの間でクリップボードを共有したい(tmuxでもOK)

Last updated at Posted at 2020-05-02

今回は、リモートサーバーにSSH接続したマシンとクライアント間のクリップボードを共有する時に詰まった部分があったので、まとめてみたいと思います。

やりたいことをざっくりとイメージ図にしました。

image.png

shell内でDISPLAYをVNC用に設定したアプリケーションApp. Aの中で作られた文字列をshellの方のクリップボードに格納したいということです。

環境について

Windows 10のクライアントPCにWSL1 (Penguin 20.04.1)を入れて、リモートサーバーにSSHしています。
XクライアントはX410を使っています。
接続先のリモートサーバーはCentOS 6.9です。
私は普段、リモートサーバー上ではターミナルを多重化してセッションを保持出来るtmux 2.3を使っています。
いずれのshellもzshを使っています。
Xのクリップボードにアクセスするコマンドとしてxsel 1.2を使用します。

基本的な使い方

本題に入る前に、基本的なVNCの使い方・xselの使い方を軽くまとめておきます。
本題は次のSectionです。

DISPLAYを指定してプログラムを起動する

X ServerのDISPLAYを指定してあるプログラムを開く時には

DISPLAY=:1.0 (shell command)

と記述します。このようにすることでVNC上のDISPLAYにXを出力できます。SSHでリモートサーバーにアクセスして、その上でXをローカルに飛ばすと通信で重くなるので、VNCで出力させた方が早いです。

クリップボードを共有する

Xはまた、クリップボードの管理機能も持っています。

ここでは、xselというパッケージを使用してクリップボードのコンテンツを読み書きします。
sshでログインする時に必ず-Xオプションをつけるか.ssh/configForwardX11 yesをつけてX forwardingをしておかないとSSHした先とのクリップボード共有が出来ません。
Windows 10とWSLとのクリップボード連携はWindows用のX client (X410とかVcXsrv)をインストールしてWSLでの環境変数DISPLAY=:0.0を通しておけば大丈夫(なはず)です。

使い方を簡単に説明すると

echo "hoge" | xsel -bi

でクリップボードに"hoge"がコピーされ

xsel -bo

でクリップボードの中身を出力します。

本題

VNCで画面出力しているプログラム上でクリップボードを共有するには?

ここで、一つの問題が生じます。DISPLAYを指定して起動させたプログラム(App. A)では、当然DISPLAY変数が指定されたものになります(某政治家みたい)。

例えば以下のように、ただ$DISPLAYを出力するだけのshell scriptを書きます。

print_display.sh
#!/bin/sh

echo $DISPLAY

これを普通に実行すると

% sh print_display.sh
localhost:10.0

と出力されますが、これをDISPLAYを指定して実行すると

% DISPLAY=:1.0 sh print_display.sh
:1.0

となります。これはこのshell script内ではDISPLAY変数が変更されて実行されていることになります。

これが、Xで管理されているクリップボードだとどうなるでしょうか。

% echo "from localhost" | xsel -bi # DISPLAYを指定しない(現在のDISPLAY)で"from localhost"を格納
% echo "from localhost to 1.0" | DISPLAY=:1.0 xsel -bi # DISPLAYを1.0にして、"from localhost to 1.0"を格納
% xsel -bo 
from localhost  # 現在のDISPLAYで動いているXのクリップボードを参照
% DISPLAY=:1.0 xsel -bo
from localhost to 1.0 # 1のDISPLAYで動いているXのクリップボードを参照

という結果になります。ではここで、VNCで動いているshellにてクリップボードの中身を参照してみます。

at_VNC
xsel -bo
:1.0

と、クリップボードの中身がVNC上で共有されています。

VNCにXを飛ばしているプログラム内で、今のshellのクリップボードを共有したい

では、イメージ図のApp. Aにてshellのクリップボードを利用する場合にはどうすれば良いでしょうか。
これの解はenvコマンドを使用して、このコマンドに対してのみshellのXを指定するということをします。
こちらを参考にさせていただきました。

このようなshell scriptを書いて実験します。

change_display_test.sh
#!/bin/sh

echo "change display" | env DISPLAY=localhost:10.0 xsel -bi

env DISPLAY=localhost:10.0で、環境変数$DISPLAYをshellのXに変更して実行するようにしています。
このスクリプトを:1.0ポート(App Aを想定)で実行します。

% echo $DISPLAY
localhost:10.0 # 現在のDISPLAYはlocalhsot:10.0
% DISPLAY=:1.0 sh change_display_test.sh
% xsel -bo # 現在のXのクリップボードを見る
change display   

大元のプログラムではDISPLAY:1.0に指定して走らせていますが、そのプログラムの中で唯一クリップボードに格納する処理だけDISPLAYの指定を解除すれば、クリップボードがVNC DISPLAYではなくshellのものと共有出来るようになるわけです。
これでDISPLAYを変えて動かしたプログラムにて現在のXのクリップボードが参照できました。

tmux使いだともうひと手間

これで普通にSSHでログインしたshellでApp. Aを使う場合にはOKです。
しかし、ここでtmuxを使っている方はもうひと手間必要です。

sessionが持っているDISPLAY変数は起動したDISPLAY変数が保持される

tmuxで新たにsessionを立ち上げた時、このsessionのDISPLAY変数は破棄されるか途中で変更するまでずっと立ち上げた時のDISPLAY変数が使われます。

例えばlocalhost:10.0のshellでtmuxを立ち上げると、ずっとDISPLAY変数はlocalhost:10.0のままです。 デタッチして再アタッチしたshellがlocalhost:10.0でなくても

実験をします。まず、1つめのshellを作ります。この中で新たにtmuxのsessionを起動させ、デタッチします。

1(10.0)
(client) % ssh (server)
(server) % echo $DISPLAY
localhost:10.0 # 起動したshellのDISPLAY変数
(server) % tmux
(server) % echo $DISPLAY
localhost:10.0 # tmuxのsessionが持っているDISPLAY変数
[detached (from session 1)] # デタッチさせます

ここで、localhost:10.0のshellを保持したまま新たにもう1回shellを作り、前のshellで作ってデタッチしたsessionにアタッチします。

2(11.0)
(client) % ssh (server)
(server) % echo $DISPLAY
localhost:11.0 # 起動したshellのDISPLAY変数
(server) % tmux a -t 1 # 先程デタッチさせたsessionをアタッチ
(server) % echo $DISPLAY
localhost:10.0 # tmuxを起動した時のDISPLAY変数のまま

と、起動しているshellとtmux上のDISPLAY変数の不一致が起きます。この状態でlocalhost:10.0に対応する1つめのshellを終了させてtmux上で取得出来るでxselを動かそうとします。

1(10.0)
(server) % exit
2(11.0)_tmux_session
(server) % echo "hoge" | xsel -bi # DISPLAY変数が本当は死んでいるlocalhost:10.0なので、Errorになる
xsel: Can't open display: (null)
: Connection refused

と、存在しないDISPLAY変数を持ってこようとするので、Errorとなります。

回避策

本来であればアタッチしたshellのDISPLAY変数が欲しいですよね。それ、出力できます!!!

tmux showenvコマンドを使用します。このコマンドの引数にDISPLAYを指定すると、アタッチしたshellのDISPLAY変数を取得できます。

2(11.0)_tmux_session
(server) % tmux showenv DISPLAY
DISPLAY=localhost:11.0

アタッチしたshellのDISPLAY変数を取ってこれました。あとはこれをenvコマンドと組み合わせるだけです。

最終形態

change_display_test.sh
#!/bin/sh

echo "hoge" | env $(tmux showenv DISPLAY) xsel -bi

これを実行すると

2(11.0)_tmux_session
(server) % DISPLAY=:1.0 sh change_display_test.sh

クライアント(WSL)の方に戻り、クリップボードの中身を確認すると

(client) % xsel -bo
hoge

と、狙った挙動になりました!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?