メニューボタン
サポートチーム便り2010.06.23

RPGからPHP、QShell、PASEツールを実行

記事「Run QShell/PASE Commands with RPG SPECIAL Files」では、RPG プログラムから PHP スクリプトと Unix ユーティリティーを簡単に実行できる新しいオープンソース・ツールを紹介しました。

そのツールは初めて提供されたため、ツールを使ってみたユーザーからいくつか質問や便利で興味深い提案をいただきました。この記事では、最も一般的な質問に対応し、興味深いフィードバックを掲載しています。

また、このユーティリティーに関する読者のお考えを聞きたいと思っています。簡単に使えそうですか。ツールは UNIXCMD と呼ばれているため、RPG プログラマーの中には萎縮してしまう人もいるため、「Unix」という言葉が入っていない名前を選ぶべきだと言われたことがあります。(このツールの目的は PASE や QShell で使用できる Unix のようなユーティリティーを実行することだと頭に入れた上で) そう思いますか?フィードバックは System iNetwork フォーラムに書き込んでください。

文字変換の問題

Q: データが正しく変換されなくて困っています (つまり、PHP スクリプトに「ガーベッジ」入力が受信されます)。

A:それは大変ですね。しかし、簡単に修正できます。では説明します。

UNIXCMD に関する元の記事のサンプル・コードでは、PHP を RPG から呼び出しているところを示しました。元々 PHP にデータ送信しようとしたら、PHP が Web サービスを呼び出し、以下のようなデータを返信してきました。

FUNIX      CF   F  1000       SPECIAL PGMNAME('UNIXCMD')
F                                     PLIST(UNIXPARM) USROPN

D cmd             s           5000a

D record          ds          1000
D lat             s           11p 7
D lon             s           11p 7

C    UNIXPARM      PLIST
C                   PARM                    CMD
/free
    cmd = 'PATH=$PATH:/usr/local/Zend/Core/bin && +
php /www/zendcore/htdocs/geocode.php';
    open UNIX;

    record = '1600 W Pennsylvania Av, Washington DC';
    write UNIX record;

    read UNIX record;
    lat = %dec(record: 11: 7);

    read UNIX record;
    lon = %dec(record: 11: 7);

    close UNIX;

    dsply ('lat=' + %char(lat));
    dsply ('lon=' + %char(lon));
   *inlr = *on;
/end-free

このコードは動作すると期待していました。問題は、動作しなかったということです。アドレス (1600 W Pennsylvania Av) が EBCDIC から ASCII に変換されませんでした。見つけたどの資料でも変換されるはずだと書いてあったので、どうしてこうなったのか理解できませんでした。しかし、PHP スクリプトは常にデータをガーベッジとして受信しました。そこで、呼び出しを iconv ユーティリティーに挿入することで問題を回避し、以下のように EBCDIC を ASCII に変換するようユーティリティーに指示しました。

cmd = 'PATH=$PATH:/usr/local/Zend/Core/bin && +
            iconv -f 37 -t 819 | +
           php /www/zendcore/htdocs/geocode.php';

これは、私のマシンで見事に動作しました。問題は、このように動作するようサポートされていないということです。理論上では、変換は自動で行われ、iconv コマンドをそのように挿入すると二重変換が行われ、結果的にガーベッジになるのです。しかし私のボックスでは、自動変換は行われませんでした。そのため、iconv コールを追加しなければなりませんでした。

これを発行したため、この手法を使用してPHP スクリプトにガーベッジが入ることに苦情を言う人が何人かいました。全員ではなく私の場合だけ自動変換が失敗したのは明らかです。iconv ツールで私のボックスの問題は修正されましたが、他の人 (少なくとも問題を報告した人たち) に問題が発生しました。

このソリューションは簡単です。まず以下を試してください。

cmd = 'PATH=$PATH:/usr/local/Zend/Core/bin && +
          php /www/zendcore/htdocs/geocode.php';

これでうまくいけば素晴らしいです。うまくいかなければ、私の場合のように iconv ユーティリティーを追加してください。これが一貫して動作しない理由を知っている人さえ探し出せば、その問題を永久に解決できるでしょう。

READ 命令コードと WRITE 命令コード両方の許可

元の記事で私は、SPECIAL ファイルで READ と WRITE 両方を同時に使用できないと言いました。IBM トロントの RPG コンパイラーの主任アーキテクトである Barbara Morris からさっそく以下のような回答がありました。

CF を特殊ファイルとして定義しないと、READ と WRITE 両方することになります。
特殊ファイルになぜ、そうしたファイル追加ができないという制限があるのかはわかりません。最初に特殊ファイルのことを耳にしたとき、それらは順次処理だけ許可する不明なテープやプリンターに対応するため発明されたと言われました。これでは、実際特殊ファイルが順次処理だけに制限されている理由を説明することにはなりませんが。それに、特殊ファイルが順次処理だけのためだと「コンパイラー」が認識している場合に、コンパイラーが CF を許可する理由が説明されていません。

本当にそれを聞いて安心しました。これで UPDATE 命令コードで無茶せずに、入出力用共用ファイルとしてファイルを定義し、そのファイル上で READ と WRITE を使用できます。この方がずっと自然に思えます。UNIXCMD で含めたサンプル・コードを更新し、この手法を使用しました。

1000 以外のレコード長

UNIXCMD の元のバージョンには、レコード当たり 1000 バイトのレコード長がハードコーディングされていました。Pepe Hipolito は即座に私にその長さを増やすことはできるのか、どこまで増やすことができるのかたずねてきました。

最初私は、それにはプログラムの変更が必要で、おそらく UNIXCMD プログラムだけを変更した場合は 32764、UNIXCMD とその配下の UNIXPIPER4 サービス・プログラム両方を変更した場合は 32766 まで増やせるのではと回答しました。ILE RPG リファレンス・マニュアルによると、F 仕様で許可されている最大レコード長である 32766 が限界だと話しました。しかし、Pepe は 65535 まで試し、うまくいきました。ということは、マニュアルが間違っているのではと思っています。少なくとも、特殊ファイルに関する限りは!

もう少し調査した結果、レコード・サイズ 65535 長をサポートするよう UNIXCMD を簡単に変更できることがわかりました。やれやれ。しかし、ほとんどの場合はそんなに長いレコードは必要ありません。効率的ではありません。プログラマーが自分自身のレコード長を選択できれば素晴らしいと思いませんか。もちろん、F 仕様でレコード長を指定すればそれは可能ですが、その情報をどのように UNIXCMD プログラムと通信させるのでしょうか。

私が見つけた唯一のソリューションは、別のオプション・パラメーターを UNIXCMD に追加することでした。この新規追加されたパラメーターを渡さない場合、UNIXCMD はレコード長 1000 が指定されているとみなします。パラメーターを渡すと、UNIXCMD はレコード長として渡す長さを使用します。例えば、以下のようになります。

FUNIX      CF   F65535        SPECIAL PGMNAME('UNIXCMD')
F                                     PLIST(UNIXPARM) USROPN

D cmd             s           5000a
D type            s              1a   inz('Q')
D reclen          s              5p 0 inz(%size(Record))

IUNIX      NS
I                                  165535 Record

C     UNIXPARM      PLIST
C                   PARM                    CMD
C                   PARM                    TYPE
C                   PARM                    RECLEN
/free
    cmd = 'cat /tmp/filetoread.txt';
    open UNIX;

    read UNIX;
    dow not %eof(UNIX);
       // The "Record" variable contains one record from
       // the "filetoread" file.  You can do something
       // with it here...
       read UNIX;
    enddo;

    *inlr = *on;
/end-free

この例では、PLIST は 3 つのパラメーターを指定しています。3 つ目はオプションで、そのパラメーターが渡されると、レコード長をいくつにするか UNIXCMD が指示されます。(残念ながら、UNIXCMD でこの長さを自動的に検出する方法はありません。したがって、レコード長をパラメーターとして渡さなければなりません。)

この例では、努力せずに最大 65535 バイト長のレコードを読み取ることができます。(パフォーマンス向上のため) それより短いレコード長を使用する場合、単に 65535 より小さな値を指定すればよいのです。

UNIXCMDが指定した長さより長いレコードを返す場合、レコードは分割されます。レコード長を 100 に設定し、Unix プログラムで返された実際の行が 450 バイトの場合、5 つのレコードに分割されます。最初の 4 つのレコードは完全に埋められますが、最後のレコードは最初の 50 バイトにデータが入り、残りの 50 バイトは空白になります。

これからの UNIXCMD は?

UNIXCMD の CL コマンド・インターフェースを真剣に検討しています。現在、CL から IFS にアクセスするのはちょっと面倒で、CL は自然言語として使用する場合が多いため、それは大きな問題になります。さらに、CL は SFTP、PGP、PHP などのツールを自動化する場合に使用されることが多々あります。CL からQShell ツールや PASE のツールを利用できる簡単なインターフェースがあれば便利ではないでしょうか。

これはまだ作成されていませんが、以下のような CL コマンド・インターフェースを考えています。

PGM

      OPENPIPE CMD('cd /tmp; ls CDC*.txt')
               TYPE(*QSHELL) +
               RCDLEN(1000)

      RCVPIPE VAR(&LINE) EOF(&EOF)
      DOWHILE COND(&EOF *EQ '0')
         /* DO SOMETHING WITH DATA */
         RCVPIPE VAR(&LINE) EOF(&EOF)
      ENDDO

      CLOPIPE

ENDPGM

繰り返しますが、この機能はまだありませんが、今後使用できるようになるでしょう。どう思いますか。(CL インターフェースに関して) 何か違う方法はありますか。あれば、以下にコメントで教えてください。

UNIXCMD ユーティリティーの取得

UNIXCMD をオープンソース・プロジェクトに変えました。私のサイトから、サンプル・プログラムとともにツールをダウンロードできます。

この記事で紹介されている多くの機能 (特に、レコード長の増加) は元の UNIXCMD では使用できません。まだそれをダウンロードしていない場合は、Web サイトからコピーを更新しましょう。

あわせて読みたい記事

PAGE TOP