FileMakerからPowerShellスクリプト実行

FileMakerのスクリプト内でPowerShellを動かしたくなったのですが、概ねうまく行ったのでノウハウを忘れないうちにブログにしておきます。もちろん、Windows版です。PowerShellで動かしたいのは画面ショットです。こちらのサイト「Windowsのコマンドラインからスクリーンショットを撮る(PowerShell)」にそのまま使えるスクリプトがあったので使わせてもらいました。ありがとうございます。また、FileMakerのスクリプトからシェルスクリプトを稼働する方法は、「Claris Community | Powershell script not working」に記載があります。

これで終わるかといえば、それだけならブックマークに忘れないように覚えさせておくだけで終わってしまいます。まず、通常は、Windowsでは、いきなりシェルスクリプトはセキュリティ的な理由で動かせないようになっています。そこで、以下のようなコマンドを、PowerShell等で入れておく必要があります。これで実行権限が与えられます。そのユーザでログインしている間は再起動してもシェルスクリプトは動きます。「Bypass」はなんでも動かすためのものなので、オンラインからの素材は検疫(デジタル署名の確認を)する「RemoteSigned」という設定の方が少しはマシかもしれません。

Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope CurrentUser

これが実行されていないと全くスクリプトは動きません。

続いて、前述のClaris Communityのやり取りにもあるのですが、Quote関数を使うことが重要です。スクリプトを実行するためには、「イベントを送信」スクリプトステップを利用し、「送信イベント」は「ファイル/アプリケーションを開く」を選択しておきます。この選択肢は、FileMakerをWindows版で動かさないと出てきません。そして、「計算」を選択して、コマンドを送り込みます。ここで、あるシェルスクリプトにファイル名の引数を付与してPowerShellで実行する場合はこのような式になるかと思います。変数に期待する値などの状況も含めて書きます。

仮定
$scriptPath ← "C:/Users/msyk/myscript.ps1" // シェスルクリプトへの絶対パス
$targetPath ← "C:/Users/msyk/data.bmp" // スクリプトの引数に渡すファイルパス

「イベントを送信」スクリプトステップの「計算」の式:
"powershell.exe -command " & 
Quote(Quote($scriptPath) & " " &Quote($targetPath))

-commandパラメータの引数は、単一の文字列で「コマンドライン」を指定します。ですが、コマンド中に”があると、そこでパラメータが終わっているとシェルは思ってしまうので、全体をQuoteで囲みます。Quoteは全体をQuoteで囲むだけでなく、文字列内部の”は、\”のようにエスケープします。ただ、元のコマンド自体もパスであり、スペースが入る可能性を考えると、やっぱりQuoteします。コマンドラインとしてうまく値を引き渡すには、このQuoteのQuoteで大体うまくいくことがわかりました。

まず、ここで、Get関数を使えやと思うところかと思いますが、FileMakerのGet (ドキュメント)等は、「/C:/Users/msyk/Documents/」のように、頭に/がついてしまいますが、それだとPowerShellは正しくパスとして認識しません。C:/Users/…のように記述されていないといけないようです。ということで、Get関数の結果を文字列処理するか、いっそのこと、絶対パスを文字列で書くか、まあ、その辺りはソリューションに応じて頑張ってください。

もう1つ、ファイル名に使ってはいけない文字列があることを忘れないでください(例えば、こちらのサイトで一覧されています)。|や/はファイル名としては使えません。これらは、Substitute関数で置き換えてから、PowerShell実行スクリプトの引数に与えます。また、ファイル名として使えない名前(COM1など)もあるので、何かで取得した名前をファイル名として利用する場合にはデータを俯瞰して対策は必要になります。

FileMaker 19のJavaScriptはES6で書けるのか?

FileMaker 19が発表されました。1番の目玉は、JavaScriptということでチェックしてみました。Webビューアは便利な機能である一方、MacとWindowsでHTMLのレンダリングエンジンが違うことが非常に不便なところであり、結果的に機能制約の多いWindows版で開発して、Macでチェックするということになっていたかと思います。また、JavaScriptに関する追加機能である、JavaScriptからFileMakerのスクリプトを呼び出すことも試してみました。

まず、以下のようなレイアウトを作りました。下半分のテキストを、上半分にあるWebビューアに表示するものです。WebビューアのURLとして「data:text/html,下半分のテキスト」となるような実験環境を作りました。まず、navigator.userAgentの結果をみてわかるように、Internet Explorer Ver.11相当です。Trident 7.0という文字列がそれを示しています。手元で調べた結果だと、FM17がTrident 7.0でした。それ以前のものは残っていないので、わからないのですが、いずれにしても、レンダリングエンジンはFM19では変わっていません。

JavaScriptの関数のなかに、「FileMaker.PerformScript()」という記述があります。このように、JavaScriptからスクリプトを呼び出すことができます。引数は、スクリプト名とスクリプトを引数を指定します。ただし、この関数が使えるようにするには、Webビューアのオプション「JavaScriptによるFileMakerスクリプトの実行を許可」にチェックを入れておく必要があります。最初はオフになっているので、入れ忘れないようにしないといけません。

ここでは次のような、単に1行だけのTestスクリプトを定義しておきました。

Webビューア上に見えているボタンをクリックして、JavaScriptを実行してみます。すると、隣のテキストフィールドをquerySelectorで参照して、そこに入力した文字列をvalueプロパティで取り出し、それをFileMaker.PerformScriptの引数に渡してTestスクリプトを実行し、ダイアログボックスが表示されています。なるほど、JavaScriptとの連携がだいぶんとやりやすくなりました。

なお、もう1つの新機能は「WebビューアでJavaScriptを実行」というスクリプトステップです。このスクリプトステップは1つの関数を呼び出すものです。これにより便利そうなのは、正規表現での文字列処理あたりがまず浮かびます。

ここで、コメントにしている行を有効にしてみます。バッククォートで囲むと、文字列のテンプレート処理が可能になり、${変数}で、変数やあるいは式の値を文字列に埋め込みができます。しかし、残念ながら何も表示されなくなります。つまり、JavaScriptのコンパイルで失敗していて、JavaScriptのプログラムが動かない状態になっています。もちろん、ボタンを押しても何も起こりません。なお、変数定義のlet、constについては、IE11がサポートする数少ないES6機能の1つです。

Mac版のFM19だとこのように、文字のテンプレート処理もきちんと動いています。userAgentで出てくる結果は、OSに入っているSafariと同一でした。ChromeやEdgeは「AppleWebKit/537.36」と出るので、FileMakerはOSに入っているものを利用していると思われます。

そういうわけで、FM19でJavaScriptがどの程度ジャンプするのか気になったところですが、Windows版がIE11相当なエンジンであるので、残念ながらES6(正しくはES2015というべき?)でのプログラミングができる状況ではありません。Babel使うくらいなら、ES5というか、IE11互換で記述した方が良さそうですね。

ちなみに、現在、JavaScriptで作られた様々なライブラリは「ES6のみ」という限定がされているものはまだ少数派ですが、Reactなんかは最初からES6で作られていますし(ReactアプリをIE11で動かす方法はある)、INTER-Mediatorの最新版はIE非対応としています。一方、メジャーなライブラリはIE11対応しているので、IE11エンジンだからといって、世間のライブラリが使えないということはほとんどないと思います。いずれにしても、Claris社がすっきりと「ES6対応です」と言わないのは何故だろうかと思っていたのですが、そういうことだったわけです。

composer.jsonファイル内でのスクリプトのパスをUNIX/Windows互換にする

 PHPでのモジュール管理ツールであるcomposerでは、通常「composer update」と入力することでインストール作業が始まるが、インストール後に実行するスクリプトを記載することもできる。INTER-Mediator Ver.6では、composerの仕組みによりnode.jsをインストールして、「composer update」に引き続いて「npm install」を自動的に動かすようにしている。composer.jsonファイルの主要部分はこんな感じだ。package.jsonはすでに用意されている。composerはvendorというディレクトリを作り、そこにモジュールをコピーし、加えてネームスペースを元にしたファイル検索の仕組みが組み込まれる。単にコマンドを入れてみれば、nodeやnpmコマンドがvendor/binにコピーされているので、相対パスの記述が適切だと考えた。

{
"name": "inter-mediator/inter-mediator",
"version": "6-dev",
"time": "2019-03-07",
:
"require": {
"php": "^7",
:
"mouf/nodejs-installer": "*",
:
"scripts": {
:
"post-update-cmd": [
"./vendor/bin/npm --save-dev install"
],
:

 これで、macOS、Linuxでは正しくnpmコマンドが稼働するのではあるが、Windowsではエラーが出てnpmが動かない。PowerShellでは次のようにでる。最後の行は赤い背景になる。コマンドプロンプトでもほぼ同様だ。

> ./vendor/bin/npm --save-dev install
'.' is not recognized as an internal or external command,
operable program or batch file.
Script ./vendor/bin/npm --save-dev install handling the post-update-cmd event returned with error code 1

 やっぱり/だろうかと思ってディレクトリセパレータを¥にしてみた。もちろん、JSONなので¥¥とタイプするが、やはり同じだ。./を無くしても同じである。

 あれこれ調べて分かった。composerのドキュメントのNoteの所に書いてある。要するに、./vendor/bin(あるいは.¥vendor¥bin)へは、composerによってパスが通っているらしい。composerによってインストールされるコマンドの場合は、以下のように相対パスを書かなくても、npmコマンドの実行はできるということである。この記述だと、UNIXでもWindowsでも同一の記述でnpmコマンド実行ができる。

 :
"post-update-cmd": [
"npm --save-dev install"
],
:

FileMaker 13で、XMLおよびXSLTによる出力

久しぶりにタイトルのような事をやりました。この記事のハイライトは、変数でXSLTファイルの位置を指定できるようになったものの、エラーが出るという事象に遭遇しますが、その対処方法が分かったということです。

XML出力は、FileMaker 5くらいからありますし、出力するXMLデータにXSLTを適用して、テキストやらHTMLやらを生成することができるので、ちまちまと文字列処理を書かなくてもいいので便利です。XSLTの習得が大変という話もありますが、今時この種の情報は「基本知識」でしょうから、勉強すればいいのです。私自身は幸いにも、今はなくなってしまったXSLTでのWeb公開の時代に、かなり勉強していたし実稼働させたソリューションもあったので、自分的にはXSLTはOKです。この記事では、XMLやXSLTあるいはそれらを利用するFileMakerの機能については詳細には説明しませんので、必要な方は予習をしてください。

現実にXML/XSLTを使うとき、当然ながらXMLはテーブルを出力するときのフォーマットなので、テーブルから生成されることになります。一方、XSLTはどうでしょうか? 確実は方法は、Webサーバ等に配置して、URLで指定することですが、データベースファイルと別の管理対象ができて不便です。そこで、XSLTのテキスト自体をテーブルのフィールドにテキストとして保存しておくことで、データベースと一体化して管理ができて便利です。ただし、そのXSLTのテキストを利用するときにはファイルにしなければなりません。現実にはスクリプトを組むとして、1フィールド、1レコードだけのタブ区切りで出力すれば、余計な “” などは含まれません。ただし、その1フィールドに、改行が入っていると、コード番号12のものに置き換えられます。後から変更するのは面倒なので、XSLTのテキストから改行を除去する計算式を設定した計算フィールドをエクスポートすればいいのです。もちろん、XSLT内では改行の有無で動作が変わらないにしなければなりません。

さて、そこで、このXSLTファイルをどこに保存するのかということですが、WindowsでもMacで稼働できるようするには、テンポラリフォルダがいいのではないでしょうかということで、次のようなスクリプトで変数にパスを入れることにしました。順当な方法かと思います。

スクリーンショット 2014-06-26 10.47.20

この変数$templateJの結果を利用して、XSLTのテキストをエクスポートします。前に説明したように、スクリプトで、1レコードのみを対象レコードとして、改行を省いたテキストのフィールドだけをタブ区切りで出力します。出力ファイルに、変数$templateJを指定します。

ここで、XSLTによる変換をスクリプトのどこかで組み込むとします。スクリプトステップの「レコードのエクスポート」で、「出力ファイルの指定」を行い、OKボタンを押すと、ファイルタイプが「XML」の場合はさらに次のダイアログボックスが表示されます。ここで「XSLスタイルシートを使用」を選択し、ファイルを指定するわけですが、「ファイル」だとダイアログボックスが出て来て、特定のファイルの指定が必要です。Get ( テンポラリパス ) は一定のパスではない模様ですし、Win/Macでパスは違います。なので、「計算」を選択して、前の変数$templateJを指定して、実際に動かしてみます。ボタンと計算式の文字列が重なっているところにFileMaker社のこの機能への姿勢が見て取れます。

スクリーンショット 2014-06-26 10.46.40

すると、XMLのエクスポートをするときに、次のようなエラーが出ます。SAX(XMLのパーサ)のエラーがそのまま見えていて意味が分かりにくいですが、ようするにファイルが見つからないということです。Windowsでは、実際に見つからなかったパスも見えています。

スクリーンショット 2014-06-26 10.49.33

変数$templateJで出力し、その直後に変数$templateJでファイル参照して見つからない。ようするにバグなのですが、バグで使えません、以上ということでは前に進みません。バグは直らない可能性もあるからです。

そこで、変数$templateJの内容を、MacとWindowsで調べ見ました。Macだと、「/Macintosh HD/var/tmp/….」みたいになっています。Windowsだと、「/C:/Users/msyk/….」となっています。まず、Macは、明らかにパスの最初のコンポーネントを取り去れば、存在可能なUNIXパスになります。Windowsは…ともかく、まず、XSLTのテキストをエクスポートした後、以下のような「変数を設定」スクリプトステップを追加して、最初のコンポーネントを省いてみました。

スクリーンショット 2014-06-26 10.46.58

Macでは、予想通り、それでエラーなく動きました。一方、Windowsは別の対策をしないといけないかと思ったのですが、なんと、ちゃんと動きました。UNIXみたいなパスの与え方でどうやら動くようです。

XSLTのTipsを少し書いておきましょう。関連レコードのフィールドは、TOさえあれば出力できると思っていたら、ポータルに展開しないと出力されません。ポータルがない場合は、ちゃんと警告メッセージを出して、最初のレコードの値だけが出力されると言われます。ということで、ポータル作成はさぼれません。

関連レコードのデータは、たとえば2つのフィールドがあるとして、関連レコードが3つあると、次のように出力します。このノードの上位ノードのタグはROWになります。

<COL><DATA>1</DATA><DATA>2</DATA><DATA>3</DATA></COL>
<COL><DATA>abc</DATA><DATA>def</DATA><DATA>ghi</DATA></COL>

上記の2つのフィールドの番号を1、2とします。ROWタグがカレントノードのとき、上記のデータを展開するのにfor-eachを使うのですが、同じポータルにある関連レコードがいくつかるとき、for-eachをそのうち1つのフィールドに対して行います。次のような感じです。ネームスペースの略号は、xsl、fmrとしています。

<xsl:for-each select="fmr:COL[1]/fmr:DATA">
    <div>
        <xsl:value-of select="." />
        <xsl:value-of select="../../fmr:COL[2]/fmr:DATA[position()]" />
    </div>
</xsl:for-each>

最初のfor-eachは普通にDATAタグが複数あるので、それを1つ1つ取り出します。3行目の「.」によって、1つ目のフィールドの現在のDATAのテキストを取り出すので、「1」「2」「3」という値が順次取り出されます。2つ目のフィールドは、4行目にあるように、XPATHの記述で、カレントノードからROWまで「../..」でさかのぼり、そこから、COL、DATA集合へと下ります。そして「position()」というのは関数です。for-eachでループしているときに、何番目なのかを示す値を返します。これで、「abc」「def」「ghi」という文字列が順次得られます。

最後に大サービスで(笑)、FileMakerのXML出力向け(ただし、FMRXMLRESULTスキーマ)の汎用的なテンプレートを書いておきましょう。コピペして、あとはデータの順序に合わせてFMPXMLRESULTやROWタグ要素に対応するテンプレートにHTML等で出力テンプレートを書くだけです。

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xhtml="http://www.w3.org/1999/xhtml"
    xmlns:fmr="http://www.filemaker.com/fmpxmlresult"
    exclude-result-prefixes="xhtml fmr">
<xsl:output encoding="UTF-8" method="xml" indent="yes"
   omit-xml-declaration="yes"/>

<xsl:template match="fmr:ERRORCODE | fmr:PRODUCT | fmr:METADATA">
</xsl:template>

<xsl:template match="fmr:FMPXMLRESULT">
    <html><body><table>
       <tr><th>Name</th><th>Activity</th></tr>
       <xsl:apply-templates />
    </table></body></html>
</xsl:template>

<xsl:template match="fmr:ROW">
    <tr>
        <td><xsl:value-of select="fmr:COL[3]/fmr:DATA[position()]" /></td>
        <td>
            <table>
                <xsl:for-each select="fmr:COL[1]/fmr:DATA">
                   <tr>
                       <td><xsl:value-of select="." /></td>
                       <td><xsl:value-of select="../../fmr:COL[2]/fmr:DATA[position()]" /></td>
                   </tr>
                </xsl:for-each>
             </table>
         </td>
    </tr>
</xsl:template>

MacでWindows VMを外付けドライブで稼働

我が家にはWindowsマシンは全くなく、MacでWindowsをVMで使うのでもう何年も過ごしています。自宅で仕事をすろときには、サーバのMac miniでVMを動かし、Remote Desktopで接続していましたが、立て続けに外出してWindowsを使わないと行けない仕事が発生しました。MacBook Air 2012を使っていますが、ストレージはいっぱいです。ということで、高速なメモリか、HDDかを迷ったのですが、両方買ってしまいました。実はメモリを買ったのですが、納期が遅れるということでHDDも念のために買いました。

結論は、HDDの方がスムーズにWindows 7のVMを使えました。購入したのは次の2つのデバイスです。前者は1万ちょい、後者は500GBを買ったので5000円程度。安い方がよかったのです。

TransMemoryは、サイトではMacだとUSB 2.0認識するように書かれていますが、実際にはUSB 3.0 Super Speedバスの方につながります。一方、ADATAのHDDは、仕様ではUSB 3.0対応と書かれていますが、システム情報を見ると、最大480Mbpsと出ています。つまり、USB 2.0接続しているということのようです。

ところが、両方に同じWindows 7を入れて稼働させると、TransMemoryの方は時々操作がまったくできない時間がしばらく続くと言った具合で、何か作業ができそうな感じはしません。一方、ADATA HDDはちょっともたつく感じはありますが、一定した応答で十分に使って作業ができそうです。実際、Excelで長時間作業をしたりもしました。

どうも逆の結果としか思えないのですが、そういえば、VMの最適化などはしていないので、もう一度時間ができたら確認をしてみたいと思います。