AutoHotKey

LinuxコマンドラインからAutoHotKeyを実行する関数を実装してみた

https://yuis.xsrv.jp/images/ss/ShareX_ScreenShot_5ea9f51f-e3b6-496c-81af-89bb166e0c46.gif

autohotkeyをWSL Bashから引数でフレキシブルなコーディングを可能にする実装をしてみました。

autohotkeyでは俗に言うシェルにおいてのstdout、標準出力がないため、例えばpythonやrubyのように、bashスクリプト上での値をautohotkeyに渡してautohotkeyで処理された値を標準出力として受け取りバイプして…といったことができません。

OutputDebug

OutputDebugといういかにもなメソッドがあるのですが、こちらはもちろん試しているのですが、できませんでした。
どうやら、専用のエディタ、統合開発環境ソフトウェアでないと出力されないみたいです。意味ないね。

OutputDebug – Syntax & Usage | AutoHotkey

autohotkey print – Google Search

OutputDebug – Google Search

[Resolved] Print to Console – AutoHotkey Community

ファイルを使う

で、解決策をとりあえず見つけました。

ファイル – ファイルxと呼称 – に一時的に標準出力したいデータをFileAppendで出力して、bashでファイルxをcatすれば、実質stdoutが取得できることになります。

以下はbash上からautohotkey経由で、現在のアクティブウィンドウがatom.exeであればtrueを返し、それ以外であればfalseを返すスクリプトです。

https://yuis.xsrv.jp/images/ss/ShareX_ScreenShot_ad669195-818f-450a-8b6a-147cc471e07f.gif


export AHKPRINT_STDOUT_FILE="/mnt/c/data/ahkprint.txt"
export AHKPRINT_STDOUT_IDENTIFIER="var"

ahkprint(){

: <<<'
e.g. ahkprint
'

# tmpdird

: check AHKPRINT_STDOUT_FILE file and path/dir exist then handling error

[[ ! -f "${AHKPRINT_STDOUT_FILE}" ]] && {
  touch "${AHKPRINT_STDOUT_FILE}" || {
    printf "${red}E: Couldn't make the file." ; return 1 ;
  }
}

: make null the file
> "${AHKPRINT_STDOUT_FILE}"

cat << EOT > /mnt/c/_tmp/tmp.ahk
WinGet, ${AHKPRINT_STDOUT_IDENTIFIER}, ProcessName, A
FileAppend , %${AHKPRINT_STDOUT_IDENTIFIER}% , $( wslpathr "${AHKPRINT_STDOUT_FILE}" )
EOT

psl ahk $( wslpathr /mnt/c/_tmp/tmp.ahk )

cat "${AHKPRINT_STDOUT_FILE}"

}

stdin(標準入力)をautohotkeyに渡す

では次のステップとして、autohotkeyにbashからstdin標準入力を渡してみます。

以下スクリプトがその改良版です。

https://yuis.xsrv.jp/images/ss/ShareX_ScreenShot_35e715ad-4e19-445c-90e4-0810a64a7bde.gif


ahkprint(){

: <<<'
e.g. ahkprint
'

# tmpdird


: If a argment defined use it else if stdin defined use it else make error

[[ -z "${1}" ]] && stdin="$(</dev/stdin)"

[[ ! -z "${1}" ]] && {
  value="${1}"
} || {
  [[ ! -z "${stdin}" ]] && {
    value="${stdin}"
  } || {
    printf "${red}No data to store to the value. at least you'd specify argment(s) or stdin." ; return 1 ;
  }
}

: check AHKPRINT_STDOUT_FILE file and path/dir exist then handling error

[[ ! -f "${AHKPRINT_STDOUT_FILE}" ]] && {
  touch "${AHKPRINT_STDOUT_FILE}" || {
    printf "${red}E: Couldn't make the file." ; return 1 ;
  }
}

: make null the file
> "${AHKPRINT_STDOUT_FILE}"

cat << EOT > /mnt/c/_tmp/tmp.ahk
WinGet, activeWinproc, ProcessName, A

WinActivate, ahk_exe ${value}
winActivateErrorLevel=%ErrorLevel%

WinWaitActive, ahk_exe ${value}

${AHKPRINT_STDOUT_IDENTIFIER} = % activeWinproc . "  " . winActivateErrorLevel

FileAppend , %${AHKPRINT_STDOUT_IDENTIFIER}% , $( wslpathr "${AHKPRINT_STDOUT_FILE}" )
EOT

psl ahk $( wslpathr /mnt/c/_tmp/tmp.ahk )

: パイプ処理の場合に動かない。 ahkのファイル書き込み処理が終わる前にこちらが動いてしまっているものだと思われる

# cat "${AHKPRINT_STDOUT_FILE}"

: 上記の対策
: 数秒間ファイル書き込みがされるかどうかを検証。ファイル書き込みがされればそれをcatしてループを終了し、数秒間が過ぎた場合は変数およびファイルが空であるとしてループを終了する

tmpdate=$(date +"%s")

while [[ "$( echo $(( $( date +"%s" ) - ${tmpdate} )) )" -lt 3 ]] ;
do

[[ ! -z "$( cat "${AHKPRINT_STDOUT_FILE}" )" ]] && {
  cat "${AHKPRINT_STDOUT_FILE}" ; break ;
}

sleep 0.1

done



# echo "hogehoge"

}


汎用性を高める

最後に仕上げとして、任意のautohotkeyスクリプトで簡単に実装できるようにします。

すこしコードを変えたので使い勝手が上記のそれと変わりますが、気に入らなければ自分でコードいじってみてください


ahkprint(){

: <<<'
e.g. ahkprint
'

# tmpdird

stdin="$(</dev/stdin)"

: check AHKPRINT_STDOUT_FILE file and path/dir exist then handling error

[[ ! -f "${AHKPRINT_STDOUT_FILE}" ]] && {
  touch "${AHKPRINT_STDOUT_FILE}" || {
    printf "${red}E: Couldn't make the file." ; return 1 ;
  }
}

: make null the file
> "${AHKPRINT_STDOUT_FILE}"

cat << EOT > /mnt/c/_tmp/tmp.ahk

$( eval "cat << EOT
$( echo "${stdin}" )
EOT" )

FileAppend , %${AHKPRINT_STDOUT_IDENTIFIER}% , $( wslpathr "${AHKPRINT_STDOUT_FILE}" )
EOT

psl ahk $( wslpathr /mnt/c/_tmp/tmp.ahk )

: パイプ処理の場合に動かない。 ahkのファイル書き込み処理が終わる前にこちらが動いてしまっているものだと思われる

# cat "${AHKPRINT_STDOUT_FILE}"

: 上記の対策
: 数秒間ファイル書き込みがされるかどうかを検証。ファイル書き込みがされればそれをcatしてループを終了し、数秒間が過ぎた場合は変数およびファイルが空であるとしてループを終了する

tmpdate=$(date +"%s")

while [[ "$( echo $(( $( date +"%s" ) - ${tmpdate} )) )" -lt 3 ]] ;
do

[[ ! -z "$( cat "${AHKPRINT_STDOUT_FILE}" )" ]] && {
  cat "${AHKPRINT_STDOUT_FILE}" ; break ;
}

sleep 0.1

done


}

軽くまとめると、

  • AHKPRINT_STDOUT_FILE定数にはこの関数で使用するstdoutの値を一時的に置いておくファイルのパスを指定します
  • AHKPRINT_STDOUT_IDENTIFIER定数にはstdoutとして標準出力するautohotkey上の変数名を指定します。(デフォルト: var)
  • stdin、標準入力がautohotkeyのスクリプトコードそれ自体になります。それに加え、そのコードの内部にはbashの引数、変数を埋め込むことができます。(以下の様なことも可能)
  • 今言ったように変数的な意味合いでは、bash上の引数と変数をそれぞれ使うことができます。
  • stdout、標準出力したいデータは、autohotkeyスクリプト上でvar変数( またはAHKPRINT_STDOUT_IDENTIFIERの値、または${AHKPRINT_STDOUT_IDENTIFIER} )に格納します。

e.g.

[autohotkeyスクリプトを生成するコード] | ahkprint
or
ahkprint <<< "echo [autohotkeyスクリプト]"

(引数の代わりに変数を使う場合)


value=typora.exe

ahkprint <<< "$(cat <<'EOT'
WinGet, activeWinproc, ProcessName, A

WinActivate, ahk_exe ${value}
winActivateErrorLevel=%ErrorLevel%

WinWaitActive, ahk_exe ${value}

var = % activeWinproc . "  " . winActivateErrorLevel
EOT
)"

さて、上記コードをもとにして、以下のようなフレキシブルなコードで上記と同様のことが実装できます。


ahkprint "typora.exe" <<< "$(cat <<'EOT'
WinGet, activeWinproc, ProcessName, A

WinActivate, ahk_exe ${1}
winActivateErrorLevel=%ErrorLevel%

WinWaitActive, ahk_exe ${1}

var = % activeWinproc . "  " . winActivateErrorLevel
EOT
)"

以下に同じ理屈で量産した実用例を置いておきます。

現在のアクティブウィンドウを取得

getActiveWindowName(){

ahkprint ${@} <<< "WinGet, var, ProcessName, A"

}

使用例

[[ ! "$( getActiveWindowName )" == atom.exe ]] && { : アクティブウィンドウがatom.exeじゃないときにしたい処理here ; }

マウス座標を取得


getMousePos(){

  : e.g. getMousePos
  : e.g. getMousePos Relative

ahkprint ${1:-Screen} ${@:2} <<< "$( cat << 'EOT'
CoordMode, Mouse, ${1}
MouseGetPos, posX, posY
var = % posX . "  " . posY
EOT
)"

}

使用例

$  getMousePos Relative
998  510
$  getMousePos
991  923
$

ミュートならミュート解除

unmute(){

  : e.g. unmute


ahkprint ${@} <<< "$( cat << 'EOT'

;ミュートならミュート解除
SoundGet, MuteState, Master, Mute

if ErrorLevel
{
MsgBox, %ErrorLevel%
Return
}

prevMuteState = %MuteState%

if MuteState=On
{
MuteState=1
}
if MuteState=1
{
Send, {Volume_Mute}
}

SoundGet, currentMuteState, Master, Mute

var = % prevMuteState . "  " . currentMuteState

EOT
)"

}

使用例


$ unmute
Off  Off
$ unmute
On  Off
$

音量を取得

getVolume(){

  : e.g. getVolume

ahkprint ${@} <<< "$( cat << 'EOT'
SoundGet, var
EOT
)"

}

使用例

$ getVolume
6.999999

独自定義の関数とエイリアス

$ psl which ahk
C:\Program Files\AutoHotkey\AutoHotkeyU64.exe
$ psl which ahk^C
$ type psl wslpathr
psl is aliased to `/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe'
wslpathr is a function
wslpathr ()
{
    wslpath -w $(realpath $1)
}

まとめ

一応bashやその他プログラミング言語の外部コマンドから任意のブログラマブルなautohotkeyスクリプトを実行して結果を受け取る、というところまで実装してみました。

どこか抜けているところやもっといい方法などあればコメントなどで教えてくれたら嬉しいです。


JavaとかC#とかわかればいいんですけどね。なんかもう、ああいう言語はやりたくないw
ファイルの数が多くてね。重い開発用エディタ使わないといけないとかっていうのも好きになれない。( 64gRAMが言うなよって感じですね )

まぁ使い始めてしまえば慣れるんでしょうがね…。教えてくれる人いないしね…。まぁ、CはないとしてもJavaはそのうち機会があればやりたいとは思ってます。でもautohotkeyの手軽さはやっぱりいいですよね。
でもまぁとりあえずbash最高。