subprocess sftp

sftp コマンドをラッピングしてアプリを自作するのもアリかも。
と前回書いたし自分で作ろうかなって。
Python なら subprocess であっさり作れそう。
後は PyObjC あたりで GUI を作ればいい。

まず問題なのがパスワード入力をどうするかってこと。
すぐに見つかったけど sshpass コマンドを使えば簡単にできるようだ。
Fedora には最初からあるけど macOS には無いんだなぁ。
まあいい後回し、今回は Fedora から接続で試す。

次は接続されるのを待つ必要があるな。

Python の subprocess – Qiita

この一番下みたいに開始の出力を得ればなんとかなりそう。
色々試したけど Connected to … は stderr 出力だ、なんだそれ。

Pythonでデッドロックを回避しながらサブプロセスの標準出力を1行ずつ読み込む – 物理の駅 by onsanai

デッドロックしまくったけどコレをみつけてなんとかなった。
色々遠回りしたけどなんとかなったコード。

#!/usr/bin/env python3

import shlex, subprocess

args = shlex.split('sshpass -p PASSWORD sftp USERNAME@HOSTNAME')

with subprocess.Popen(args, encoding='UTF-8', stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as p:
    # wait Connect
    while True:
        line = p.stdout.readline()
        print(line)
        if 'Connected' in line:
            break
    # Start
    s = input('test > ')
    outs, errs = p.communicate(input=s, timeout=15)
    print(outs)

communicate ではプロセスが終わってしまうんだよな。
bye を送るまで while ループさせる所で今つまっている。

Paramiko 使えってのは無しにして、それじゃつまらないし勉強にもならない。
外部モジュールで満足する人はフリーソフトのインストールで満足しちゃう人だと思う。

Fedora and macOS sftp 2

前回の macOS から Fedora への ssh の件。
Fedora の共有設定で「リモートログイン」をオンにするだけだった。

ssh で macOS から接続の初回は警告が出るけど yes を選べば以降は普通に接続。
Fedora の ssh ポート(22) は最初から開いているし SELinux にも邪魔されない。
ログアウトは control+D でいいのね。

後は sftp だけど、よく考えたら Finder でやる必要はなかった。
普通にアプリを探せばいいじゃん。
GNOME だと Nautilus が便利すぎてそういう選択肢を忘れてしまう。
本サイトの macOS Tips ページも実は全部 Nautilus でアップロードしている。

てかフリーソフトをインストールしまくるなんて初心者しかやらないし。
homebrew とかいったい何故そんなのを使いたい人がいるのかも理解できない。
Xcode すら入れていない筆者がこんな Blog をヤレるのに。
脱線しそうなのでこのくらいにして。

多分アップデート以外で App Store を開くのは数年ぶり。
sftp で検索。
Transmit というのが定番と出るけど調べると年額 2800 円って、高いよ!
ForkLift というアプリが 8.5MB だし無料のようなので試してみる。
初回起動でメールアドレス入力が出るけど no, thanks! を選択でいいみたい。

普通に接続できた、DnD で問題なく双方向コピーできる。
思いっきり英語メニューだけど普通に使うぶんには問題ないかな。

でもサーバー側は日本語がバケる、コピーはバケたまま可能だけど。
W クリックでファイルを開くことも、ただし command+O は使えない。
Favorites に command+D で登録できない、てかどうやって登録するんだ?
サードパーティとはいえもっと統一感がほしい、って GNOME 使いは思う。

ところで macOS にも sftp コマンドが最初から入っているんだね。
ラッピングしてアプリを自作するのもアリかも。

Fedora and macOS sftp

macOS Catarina になって私的に一番困ったのが Fedora との接続です。
afp が使えなくなったので samba を使ってファイルのやりとりをしているのだけど。
Catarina にアップグレードした直後から唖然とするほど遅くなった。

初期化だけなら別にかまわないんだけど、すべてが遅い。
ディレクトリ移動毎にしょんぼりするほど待たされるってどうなのよ。

他の手段で Fedora から macOS のファイルにアクセスというと。
やっぱり UNIX 系 OS の基本は SSH ですよね。

ssh ユーザー名@macのコンピューター名.local/

ssh

普通に接続できた、しかもディレクトリ移動もまるでローカルのように早い。
ログインして cat コマンドで表示される文字列を端末エミュレーターからコピペ。
で大半の作業はイケるはず、てか Catarina になってからそうやっていた。

でもこれじゃファイルの転送はできない、何より面倒。
やっぱりファイルマネージャを使った GUI でやりたいよね。
sftp という ssh を使ったプロトコルがあったよな。
ものは試しのつもりで Nautilus で Ctrl+L を叩いて以下を打ち込む。

sftp://macのコンピューター名.local/

sftp

できるジャン、しかも感動するくらい早い!
Nautilus をもう一つ立ち上げ普通に DnD でコピペできる。
Gedit や eog で普通にファイルを開くこともできる。

てか、samba はコピーするとパーミッションが 600 になって困っていた。
コッチならパーミッションは維持される、すばらしい。

何故誰もこの方法を教えてくれなかったんだ!
ってしかたがないか、大半の人は Linux から macOS に接続なんて考えないし。
どちらかが Windows だと samba になってしまうよね。

mac sftp

ちなみに Finder からは sftp はできませんでした。
ssh すら、Firewall ポートは空けて SELinux を無効にしても駄目だった。
まだ何かやる必要があるみたいだけど Nautilus で全部やればいいから別にいいか。
だって Finder って使い辛いんだもん。

Atom: Script to atom-runner

macOS で Atom を使おうとしたら Script パッケージのアップデートが。
適用したらエラーで動かなくなった、なんじゃそりゃ。
バグ報告は上がっているようなので修正を待つとするか。

いや、ぶっちゃけ気に入らない所も多かったし別の手段も考えよう。
せっかくなので自作、も考えたけどアウトプットパネルはどうしよう?
Gedit みたいに標準では付いていないみたいだし、うーん。
もっとシンプルなパッケージを探して参考にしようかなと。

オススメのatomパッケージ7選 – Qiita

atom-runner というのを試しに入れてみた。
これは別 Pane を使って出力するようだ、なるほど。
そのおかげかパッケージもシンプルで素敵。

てゆーか、コイツは shebang どおりにスクリプトを実行してくれるヤン!
対応言語なんて shebang を書くなら設定不要、拡張子無しもイケる。
何だよ自分で作らなくてもいいジャン、こいつを今度から使うことにする。

ただデフォルトの状態だと何か表示が変、無駄な余白がある。
下側ピッチリに表示させたいんだけど。

Atom-Runner, Moving output pane to right side – support – Atom Discussion

こんなのを見つけた。
試してみると -4 に指定すれば下に出るようになるな。

#pane = panes[panes.length - 1][dirfunc](view)
pane = panes[panes.length - 4][dirfunc](view)

それとカレントディレクトリが全部ルート (/) になるんだが。
ファイルの場所を指定するには、新しいペインを表示する前に記憶すりゃいいかも。

  run: (selection) ->
    # add
    path = atom.workspace.getActivePaneItem()?.buffer?.file?.path
    @cwd = p.dirname(path)

##################################

    try
      '''dir = atom.project.getPaths()[0] || '.'
      try
        if not fs.statSync(dir).isDirectory()
          throw new Error("Bad dir")
      catch
        dir = p.dirname(dir)
      @child = spawn(cmd, args, cwd: dir)'''
      @child = spawn(cmd, args, {cwd: @cwd})

それと esc を押したら Pane も削除してほしいな。
https://flight-manual.atom.io/api/v1.41.0/Pane/
Pane.destroy() でペインは削除できるようだ。

  stopAndClose: ->
    {pane, view} = @runnerView()
    pane?.removeItem(view)
    pane?.destroy() # add
    @stop(view)

よし後は run:file を F5 に割り付けして。

理想どおりになったけどかなり改造してしまった。
Atom を使う人はプログラマーだからこんな改造はみんなできるよね。

GNOME 3.34 modal dialog bug

Fedora 31 の GNOME 3.34 にて。
Gedit でファイルの編集中にする。
F11 でフルスクリーンにする。
Ctrl+W でファイルを閉じようとして modal なアラートを出す。

何じゃこりゃ。

逆にフルスクリーンから戻すともっと悲惨。

自作アプリの Comipoli でファイル切り替えの動作が変だった。
モーダルダイアログを出すと一瞬だけ先頭ページが表示される現象に悩まされた。
これが原因じゃねーか、三日も悩んで損した。

GNOME だから 3.36 になるまでこのままなんだろうな。
しかたがないので Comipoli はしばらく GtkInfoBar に切り替えることに。

モーダルにできないのでキーボード処理は表示時のみ専用ハンドラに逃がして。
何故か use_markup が使えない、文字列だけにするしかないみたい。
gtk_info_bar_set_default_response が何故かエラーになる、困った。
スペースキーをガシガシするだけの特徴は残したいけど自前処理しか手段が無いな。
困った時の g_idle_add だ、この関数マジ便利。

#!/usr/bin/env python3

from gi.repository import GLib, Gtk

class ComipoliInfoBar (Gtk.InfoBar):
    def __init__(self):
        Gtk.InfoBar.__init__(self, no_show_all=True, valign=Gtk.Align.START)
        #
        self.uri = ''
        self.label = Gtk.Label(visible=True)
        area = self.get_content_area()
        area.add(self.label)
        self.ok = self.add_button('_OK', Gtk.ResponseType.OK)
        self.add_button('_Cancel', Gtk.ResponseType.CANCEL)

    def show_message(self, text, uri):
        self.uri = uri
        self.label.set_text(f'Next\n{text}\n\nEsc Cancel')
        GLib.idle_add(self._show_message)

    def _show_message(self):
        self.show()
        self.ok.props.has_focus = True
        return False

を GtkOverlay に乗せて。

class ComipoliWindow(Gtk.ApplicationWindow):
    def __init__(self, app):
        Gtk.ApplicationWindow.__init__(self, application=app)
        # InfoBar
        self.infobar = ComipoliInfoBar()
        self.infobar.connect('response', self.on_next_info)
        #
        # etc...
        #
        overlay = Gtk.Overlay()
        infolay = Gtk.Overlay()
        overlay.add_overlay(self.upperbar)
        overlay.add(infolay)
        infolay.add_overlay(self.infobar)
        infolay.add(self.canvas)
        self.add(overlay)

    def on_next_info(self, widget, res_id):
        if res_id == Gtk.ResponseType.OK:
            self.set_uri(widget.uri)
        widget.hide()

    def change_pixbuf(self, nex):
		#
		# etc...
		#
        next_index = cbzs.index(etc) + 1
        if l > next_index:
            esc = html.escape(cbzs[next_index])
            uri = GLib.filename_to_uri(GLib.build_filenamev([path, cbzs[next_index]]), None)
            self.infobar.show_message(esc, uri)
            ''' GNOME 3.34 bug
            dlg = Gtk.MessageDialog(
                transient_for=self,
                buttons=Gtk.ButtonsType.OK_CANCEL,
                #message_type=Gtk.MessageType.QUESTION,
                text=f'<span bgcolor="red">Next:</span> {esc}',
                use_markup=True,
                secondary_text='ESC Cancel')
            dlg.set_default_response(Gtk.ResponseType.OK)
            if dlg.run() == Gtk.ResponseType.OK:
                uri = GLib.filename_to_uri(GLib.build_filenamev([path, cbzs[next_index]]), None)
                self.set_uri(uri)
            dlg.destroy()
            '''

で。

なんとかなった。
もう少し debug してから更新します。