Hatena::ブログ(Diary)

=== SANDmark 19106 === beginning stress test このページをアンテナに追加 RSSフィード

2017年03月30日

続・OmegaT用: 正規表現のグループ参照を使って訳文を置き換えるスクリプトを書いたよ(GUI対応)


前回の記事からの続きです。

変更点

使い方

前提として、このスクリプト正規表現グループ参照を用いた置き換えを行うものです。通常の正規表現検索・置換はOmegaTのみで可能です。

regexp = /Sample segment named (.+) could be replaced with (.*)./
def replace(m) { "サンプル分節${m[1]}が${m[2]}に置き換えられます。" }

  • とある部分を変更し、検索に使う正規表現と置換後の文字列を設定します。
  • 『実行』をクリックするとプレビューウィンドウが出てきます。
  • メニュー -> 実行を選択すると、実際に置換を行います。

デフォルトでは

  • Sample segment named something could be replaced with RegExp.
  • Sample segment named whatever could be replaced with script.

上記パターンにマッチする分節すべてがそれぞれ

  • サンプル分節somethingがRegExpに置き換えられます。
  • サンプル分節whateverがscriptに置き換えられます。

と置換されます。

謝辞

OmegaTに含まれるGroovyスクリプトをおおいに参考にさせていただきました。感謝!

技術的な話

GUIなんてやるもんじゃない。
Groovy経由だからJavaほど構築が面倒なわけではないけど、やっぱりコード内でUIを定義しようとするとやっぱりすごいことになりました。長年見なかった感じのコードというか。ほとんどコピペなのでSwingなにそれな私でもなんとかなりました。いろんなサンプルを用意してくれるOmegaTプロジェクトに改めて感謝です。未だにimport staticの意味がわかってないけど許して。

ただ、他のスクリプトを見るとあんまりeachイテレータ使ってなくて、トラディショナルなforループが多いです。分節区切りを変更したらeachで拾えなくなる分節があったんですが、インデックスで参照してたら拾えてたのかな?

OmegaTスクリプトを書こうっていう人がどれくらいいるのかわかりませんが、書いてみると意外と簡単だったりします。現在のベータ版(4.1.1)ではシンタックスハイライトが実装されてたりするので、興味のある人は触ってみてくださいねー。

2017年03月29日

OmegaT用: 正規表現のグループ参照を使って訳文を置き換えるスクリプトを書いたよ


2017-03-30: 更新しました -> 続・OmegaT用: 正規表現のグループ参照を使って訳文を置き換えるスクリプトを書いたよ(GUI対応) - === SANDmark 19106 === beginning stress test

経緯と使い方

なんか前の記事から一年以上経ってて引きました。

最近は翻訳のお仕事をしているんですが、支援ツールとして翻訳メモリソフトOmegaTno title)を使ってます。Javaで書かれたオープンソースソフトウェアで、実はTRADOSの体験版ダウンロードしている最中に暇つぶしで触ってみたらそのまま実務用になってしまったという、脅威の機能性を持つあなどれないやつです。TRADOS体験版の試用期間はまだあるんだけど、多分触らないまま終わりそう。基本的な使い方はマニュアルが充実しているのでそっちに任せるとして、今回はややマニアックな需要です。

チャットログなんかを翻訳していると定型文が大量に引っかかるわけで、いくら参考訳文に出てきても200件あったら大変なわけですよ。もちろん置き換え機能はOmegaTにありますし、正規表現も使えるのだけど、これが "John talked to Jessie", "Jack talked to Jimmy" とか、そういうのが大量にあると単純には置き換えられない。

ので、スクリプトを書きました。ダウンロードしてOmegaTインストールディレクトリにあるscripts/にぶち込んでください。あとはOmegaTを起動してツール→スクリプト→regexp_replacement.groovy→実行です。コンソールにプレビューが表示されるので、キャンセルを繰り返しながら置き換え後の文章を確認しつつ、これだと思ったところで置き換えてください。操作は元に戻せないのでtmxファイルのバックアップを強く推奨します。自己責任でどうぞ。

Groovy自体はじめてだったのでいろいろ調べながらのわくわくさんクオリティでごめんなさい。自分でも相当使いづらいのでそのうち直します。

5行目が検索正規表現とグループ指定、6行目が置き換え後の文字列です。置き換え後の文字列がちょっと読みづらいですが仕様です。""でくくられた部分だけ編集しましょう。${m[1]}がひとつめの括弧、${m[2]}がふたつめの括弧…というように対応してます。

regexp = /(.*) talked to (.*)./
def replace(m) { "${m[1]}さんが${m[2]}さんに言いました。" }

/(.*) talked to (.*)./が例えば "(John) talked to (Jessie)" にヒットして、"JohnさんがJessieさんに言いました。" に置き換えられます。人名はがんばってください。

技術的な話

なんでハードコーディングしてるのかっていうと、OmegaTがグループマッチの部分参照に対応していないからです。どうもJavaのMatcherクラスが十分なメソッドを提供していないからっていう感じはしますが、Twitterでアドバイスをくれたエラリー・ジャンクリストフさんによると



とのことです。ひょっとすると将来的に実装されるかもしれませんが、現時点では他の機能のほうが需要が高い様子。まぁスクリプトでできたわけだしいいよね。

OmegaTはGroovyとJavaScriptの両方でスクリプトが書けますが、今回は好奇心もあってGroovyに挑戦しました。Rubyインスパイアされただけあってとりあえず書き下すのは楽でした。そのぶん処理の最適化とかまったくしていないコード(同じ処理を2回行う手抜き)なので、分節が10万とかになるとどうなるかわかりません。

置き換え後の文字列がStringじゃなくてクロージャになっているのは呼び出しの都合です。"_1さん_2さん"とかで参照したいんですが、面倒なので実装しません。

UIもハードコーディングでローカライズとかまったく考えてないんですが、よし海外ニーズがあったとしても、向こうにはもっといいものがあるでしょうきっと。

2013年08月14日

Papyrusログに MHP_ConfigQuest のスタックが溜まって落ちる人へ

私的経緯

例えば歩いているだけで錬金素材が集まるMODなんかを入れていると、
「持てるだけ持ち歩いて後でまとめて錬金しよう」とか思うわけです。
持ち歩ける重量が多ければ多いほど、のちのち「山の青い花 (280)」とかになる。
ちなみに私が愛用させてもらっているのはこちら。

当然300回近く R キーを押すなんて人間には至難の業で、
漏れそうな下を必死こいた気分で我慢しながら駅のトイレのドアを叩くような事態に陥るよりも、
きっとこういう連打ツールを使いたくなるはずなのです。

しかし使ってみるとわかるんですが、人智を超えたスピードでアイテムが作成されるため
Papyrusスクリプトエンジンには途方も無い負荷がかかります。
いや、本来は(多分)スクリプト関係ないんだけど、MODによってはあるようで。

MHP = More HotKeys Please

タイトルにある MHP_ConfigQuest は比較的有名かつ高機能なホットキーMODである*1

に含まれている MHP_PlayerScript.psc に含まれているものです。

上記ブログで言及されているように、More HotKeys Please(以下MHP)は
インベントリへのアイテムの追加、およびアイテムの削除をモニタリングしているので、
一度の錬金につき「一つ目の材料削除」「二つ目の材料削除」(オプション材料削除)「薬の追加」という
少なくとも合計3つ以上のイベントが同時に発生することになります。

原因はスクリプトの過負荷

さらにそのイベント内で深くネストされたIf - EndIfWhile - EndWhileブロックが大量にあって、
Papyrusコード的には結構な負荷がかかります。落ちても仕方がないレベルと言ってもいいです。
これはホットキーにポーション選択したときの<mag>を計算している――
つまり同じ名前の薬のうちどれを優先的に使用するかの判断をしているわけですが、
個人的に薬はSwiftPotionRebornを使っているのであんまり必要ありません。

ので、とにかく落ちないようにするためにスクリプトを書き換えてみました。
需要があればコンパイル済みの MHP_PlayerScript.pex もどこかに置きますが、
MHPの作者さんに申し訳が立たないので多分やりません。コードのみ。

スクリプトの書き換え準備

バージョンは More HotKeys Please v3.0 です。

必要なもの:

スクリプトのコンパイルに Creation Kit 本体は使いません。
くっついてくる Papyrus Compiler というものを使います。
\SteamApps\common\Skyrim\Papyrus Compilerに存在が確認できるはず。
無ければそこはプレイ用のSkyrimフォルダなので、Creation Kitインストールしたフォルダを漁りましょう。

発見できたら、デスクトップとか適当なところに簡単な .bat ファイルを作ります。
ドラッグ&ドロップでスクリプトをコンパイルしてくれる親切設計ですが、
CK をインストールしたフォルダとプレイ用 Skyrim フォルダを把握しておく必要があります。
が、今回は同一フォルダにインストールされているものとしましょうね。*2

compile_pupyrus.bat:

"D:\SteamLibrary\steamapps\common\skyrim\Papyrus compiler\PapyrusCompiler.exe" %1 -f="TESV_Papyrus_Flags.flg" -i="D:\SteamLibrary\steamapps\common\skyrim\data\scripts\source" -o="D:\SteamLibrary\steamapps\common\skyrim\Data\Scripts"
pause

D:ドライブの直下にSteamライブラリフォルダがあると仮定します。
場合によっては C:\Program Files(x86)\Steam\steamapps\... のようになっている可能性もあります。

PapyrusCompiler.exe は第一引数スクリプトもしくはフォルダを取り、
-i にソーススクリプトの場所を指定、 -o にコンパイル済みスクリプトの出力先を指定するようです。
-f 引数についてはよくわかりませんが、あまり深く触らないほうが良さそうなのでおまじない。

スクリプト改竄

MHP_PlayerScript.psc をどこか適当なところ(やはりデスクトップとか)にコピーし、適当なエディタで開きます。
このとき Data\Scripts\Source にあるものを直接編集してしまうとオリジナルが行方不明になるので、
確実にコピーを取るようにしてください。

それから次の行を探します。

  • Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)
  • Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)

v3.0 では81行目、174行目にありました。
見付けたらそれぞれ2箇所編集します。 ; 記号から後ろはコメントとみなされて無視されますが、
日本語が入っているとコンパイルに失敗する可能性があるかもです。

まずは MHP_PlayerScript.psc: (OnItemAdded付近)

Event OnItemAdded(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akSourceContainer)
	If akBaseItem.gettype() == 46
		If Writer.PotionEffects.length < 58
			Writer.PotionEffects = New MagicEffect[58]
			int i = 0
                        ; この下をいじくる
			; while i < Writer.PotionEffects.length ←コメントアウト
			while i < 0 ; 追加
				Writer.PotionEffects[i] = writer.MHP_AlchEffects.getat(i) as MagicEffect
				i += 1
			endwhile
		endif
		potion tempPotion = akBaseItem as Potion
		int count = tempPotion.getnumeffects()
		int j = 0
                ; この下もいじくる
		; while j < count ←コメントアウト
		while j < 0 ; 追加

それぞれ 0 を指定するとポーションの使用に支障をきたす可能性があるので、
必要に応じて数値を変更してください。具体的な目安はわかりませんが、5から10など。
数値が大きくなればなるほど負荷が大きくなります。
逆に 0 を指定した場合はループが一切回らなくなるので、負荷はほとんどかからなくなります。

続いて OnItemRemoved ですが、こちらも同じようなコードを2箇所変更します。
作用・副作用については上記と同じ。
MHP_PlayerScript.psc: (OnItemRemoved付近)

Event OnItemRemoved(Form akBaseItem, int aiItemCount, ObjectReference akItemReference, ObjectReference akDestContainer)
	If akBaseItem.gettype() == 46 && player.GetItemCount(akBaseItem) <= 0
		If Writer.PotionEffects.length < 58
			Writer.PotionEffects = New MagicEffect[58]
			int i = 0
                        ; この下をいじくる
			; while i < Writer.PotionEffects.length ←コメントアウト
			while i < 0 ; 追加
				Writer.PotionEffects[i] = writer.MHP_AlchEffects.getat(i) as MagicEffect
				i += 1
			endwhile
		endif
		potion tempPotion = akBaseItem as Potion
		int i = 0
                ; この下もいじくる
		; while i < Writer.KeyForm.length ←コメントアウト
		while i < 0 ; 追加

コンパイルと設置

あとはもうコンパイルするだけです。
といっても簡単で、先ほど改竄した MHP_PlayerScript.psc compile_pupyrus.bat にドラッグ&ドロップするだけ。
コンパイルに必要なファイルが自動的にロードされ、即座にコンパイルが始まります。

Starting 1 compile threads for 1 files...
Compiling "MHP_PlayerScript"...
Starting assembly of MHP_PlayerScript
0 error(s), 0 warning(s)
Assembly succeeded

Compilation succeeded.

Batch compile of 1 files finished. 1 succeeded, 0 failed.

こんな感じの文字が表示された黒いウィンドウが出るはずです。
もし Batch compile of 1 files finished. 0 succeeded, 1 failed. と表示されている場合は
コンパイルに失敗しているので、必要なファイルがあるかどうか確認してください。

SKSE, SkyUIスクリプトのソースが展開されているかどうか、
compile_pupyrus.bat に記述されている -i= のフォルダが正しいかどうか、などです。
Batch compile of 1 files finished. 1 succeeded, 0 failed. と表示されていれば成功しているので、
何か適当なキーを押すか、右上の☓ボタンでウィンドウを閉じてしまって大丈夫です。

確認

さて実際にスクリプトが更新されたのかどうかですが、
Data\Scripts フォルダを開いて日付順に整列させればすぐに分かります。
直近に更新されたファイルが MHP_PlayerScript.pex になっていれば成功です。

もしそうでなければコンパイルに失敗しているか、
どこか別のところにスクリプトが出力されている可能性があります。
compile_pupyrus.bat-o= のフォルダを確認してみてください。

ゲームの起動

いつも通り起動してしまって問題ないはずです。
ただし動いているのは改竄済みのスクリプトなので、
ポーション関係、あるいはまったく別のところでおかしな動きをしても、自己責任ということで。

私への質問などは構いませんが、間違っても More HotKeys Please のフォーラムに
バグ報告などはしないようにしましょう。もっとも、私も MHP について
完全に把握しているわけではないので、これでいいのかどうかもまだわかりません。

ご利用は計画的に

*1:最初はGRHotkeys使ってたんだけどScriptDragonの導入が面倒になってやめてしまったよ…。

*2:なぜなら解説が面倒だから。

2013年06月28日

VMware上のUbuntu13.04でvmware-toolsが動かなかったので

タイトル通り vmware-tools が動かなかったのです。というより、

  • カーネルが 3.8 になったので vmware-tools をビルドしようとしたら
  • linux-headers に version.h が無いと怒られ失敗し
  • ln -s でリンクを貼ってビルドし直したんだけど
  • カーネル 2.8 の方法でビルドしようとしやがるので当たり前のように失敗する

というどうしようもない状態に陥っていました。
ちなみに vmware-tools のバージョンは 9.5.0.19712 (build-958366) だったらしい。
多分バージョンによってはビルドできる。

が、いまどき VMware Player で Ubuntu を動かすライトユーザーとしては
いちいち VMware® さんに個人情報を送信して有料版体験版ダウンロードし、
そこから vmware-tools だけを失敬してアップデート…
などという苦行を強いられるのはさすがにもう嫌なので、
思い切って open-vm-tools に乗り換えてみました。

ようするにオープンソース版の vmware-tools らしい。

$ sudo apt-get install open-vm-tools       # 設定ファイルを置き換えるかどうか訊かれる。とりあえず全部置き換えた
$ sudo /etc/init.d/vmware-tools stop       # バッティング予防に既存の vmware-tools を停止
$ sudo /etc/init.d/open-vm-tools restart   # open-vm-tools 再起動

ここまでで特にエラーは無かったのだけれど、
共有フォルダが有効にならないという比較的フェイタルな事態に遭遇。

「これだから…」などという自分勝手な呆れ感を抱きながらグーグリングしていると
[ubuntu] Auto mounting shared folders with open-vm-tools というアメリカ語の記事を発見。
どうやら共有できないのではなく、自動でマウントしてくれないらしい。

上記URLに掲載されてる open-vm-tools (シェルスクリプト) をためらいもなく実行すると
Syntax Error と怒って仕事してくれなかった上に、
よくよく見るとオリジナルのスクリプトとあまりにも構造が違っていたので
手動で改造した結果がこちら。

#!/bin/sh

### BEGIN INIT INFO
# Provides:		open-vm-tools
# Required-Start:	$local_fs $remote_fs
# Required-Stop:	$local_fs $remote_fs
# X-Start-Before:
# X-Stop-After:
# Default-Start:	2 3 4 5
# Default-Stop:		0 1 6
# Description:		Runs the open-vm-tools services
# Short-Description:	Runs the open-vm-tools services
### END INIT INFO

. /lib/lsb/init-functions

exit_if_not_in_vm () {
	if [ ! -x /usr/bin/vmware-checkvm ] || ! /usr/bin/vmware-checkvm > /dev/null 2>&1
	then
		echo "Not starting as we're not running in a vm."
		exit 0
	fi
}

case "${1}" in
	start)
		# Check if we're running inside VMWare
		exit_if_not_in_vm

		log_daemon_msg "Loading open-vm-tools modules"
		log_progress_msg "vmhgfs"; modprobe vmhgfs
		log_progress_msg "vmxnet"; modprobe vmxnet
# ここに2行追加
		log_daemon_msg "Mounting hgfs"
		mount -t vmhgfs .host:/ /mnt/hgfs

		log_end_msg 0

		log_daemon_msg "Remove pcnet32 module, if vmxnet is loaded"
		# check if vmxnet module is loaded
		loaded="$(lsmod | grep -o vmxnet)"
		if [ "$loaded" = "vmxnet" ]; then
			# Removes pcnet32 module if vmxnet module is loaded
			modprobe -r vmxnet
			log_progress_msg "Removing pcnet32"; modprobe -r pcnet32
			modprobe vmxnet
		fi
		log_end_msg 0

		log_daemon_msg "Starting open-vm daemon" "vmtoolsd"
		/usr/bin/vmtoolsd --background /var/run/vmtoolsd.pid
		log_end_msg 0
		;;

	stop)
		# Check if we're running inside VMWare
		exit_if_not_in_vm
# ここにも2行追加
# ※ vmtoolsd のプロセスをころる前に umount するとエラーを吐くとコメントで情報を頂いたので位置だけ変更
		log_daemon_msg "Unmounting /mnt/hgfs"
		umount /mnt/hgfs

		log_daemon_msg "Stopping open-vm guest daemon" "vmtoolsd"

		if [ -f /var/run/vmtoolsd.pid ]
		then
			kill $(cat /var/run/vmtoolsd.pid)
		fi

		log_end_msg 0

		log_daemon_msg "Removing open-vm-tools modules"
		log_progress_msg "vmhgfs"; modprobe -r vmhgfs
		log_progress_msg "vmxnet"; modprobe -r vmxnet

		log_end_msg 0
		;;

	force-reload|restart)
		${0} stop
		sleep 1
		${0} start
		;;

	*)
		log_success_msg "Usage: ${0} {start|stop|restart|force-reload}"
		exit 1
		;;
esac

実際には mount,umount コマンドをそれぞれ start,stop に追加するだけなんですが、
mount -t vmhgfs .host:/ /mnt/hgfs ってなんだよ覚えられる気がしないよ。
起動時に毎回このコマンドを実行するのかと思うと何かこみ上げてくるものがありますね。

$ sudo /etc/init.d/open-vm-tools start
* Loading open-vm-tools modules                                         [ OK ]
* Mounting hgfs                                                         [ OK ] 
* Remove pcnet32 module, if vmxnet is loaded                            [ OK ] 
* Starting open-vm daemon vmtoolsd                                      [ OK ]
$ ls /mnt/hgfs/
C/ D/ E/ J/

何はともあれ、これで共有フォルダがゲスト側から見えるようになりました。
結論:

  • VMware Player ユーザには open-vm-tools のほうがいい
  • デフォルトで自動マウントするようにして欲しい
  • 作業が中断されて2時間経った

2012年11月03日

GitHubにpush,pullするときのパスワード認証が鬱陶しいのでzshで何とかする

皆さんお馴染み no title ですが、
パスワード付きの鍵を作ってしまうと、 push, pull のたびに認証を要求されてしまいます。

GitHub曰く「別にパスワード付けても凄くセキュアになるわけじゃないよ」だそうですが、
同時に「でもパスワード付ければちょっとはセキュアになるよ」とも言っています。
とても親切です。

でも認証が面倒くさいじゃん!!

という人が僕以外にも多数いたようで、公式のヘルプページスクリプトが載ってました。
以下抜粋:

SSH_ENV="$HOME/.ssh/environment"

# start the ssh-agent
function start_agent {
    echo "Initializing new SSH agent..."
    # spawn ssh-agent
    ssh-agent | sed 's/^echo/#echo/' > "$SSH_ENV"
    echo succeeded
    chmod 600 "$SSH_ENV"
    . "$SSH_ENV" > /dev/null
    ssh-add
}

# test for identities
function test_identities {
    # test whether standard identities have been added to the agent already
    ssh-add -l | grep "The agent has no identities" > /dev/null
    if [ $? -eq 0 ]; then
        ssh-add
        # $SSH_AUTH_SOCK broken so we start a new proper agent
        if [ $? -eq 2 ];then
            start_agent
        fi
    fi
}

# check for running ssh-agent with proper $SSH_AGENT_PID
if [ -n "$SSH_AGENT_PID" ]; then
    ps -ef | grep "$SSH_AGENT_PID" | grep ssh-agent > /dev/null
    if [ $? -eq 0 ]; then
  test_identities
    fi
# if $SSH_AGENT_PID is not properly set, we might be able to load one from
# $SSH_ENV
else
    if [ -f "$SSH_ENV" ]; then
  . "$SSH_ENV" > /dev/null
    fi
    ps -ef | grep "$SSH_AGENT_PID" | grep ssh-agent > /dev/null
    if [ $? -eq 0 ]; then
        test_identities
    else
        start_agent
    fi
fi

どうやらフォーラムに投稿されたものらしいです。
で、これを .bashrc なり .profile なりに書けば自動的に ssh-agent が立ち上がるぞと。
試してみたら愛用している zsh でも動きました。

しかし問題がありまして、既に立ち上がってるssh-agentがあるとコケるんですね。
端末を閉じる前に毎度毎度

$ ps aux | grep ssh-agent
$ kill $SSH_AGENT_PID
$ kill $SSH_AGENT_PID
$ kill ...

なんてやってたらパスワード認証よりも面倒くさい。
私は不精ですから(キリッ

というわけで zsh のコールバックを利用して、
「zshが終了するときに自動的にssh-agentを終了させる」
が実装できないかどうか調べてみたら、あっさり解決したのでメモメモ。

具体的には .zshrc の末尾(でもどこでもいいけど)に

TRAPEXIT() {
    ssh-agent -k
}

と書くだけでした。

Signalについては詳しくないので TRAPINT のほうが確実かも知れませんが、
とりあえずこれで動いてくれているのでとりあえず良し。

でもこれ、端末を複数立ち上げるとまた問題になりそうですね…。
screenを使う手もあるんですが、窓が沢山無いと落ち着かない私。
どうしたものかしら…。