z/OSの新しい管理方法を探る - Lightweight Command Utility on z/OS USS

# はじめに
常々、PCOMのインターフェースをどうにかしたいと思っておりました。z/OSの技術者が減っていく諸悪の根源の1つがこのPCOMのUIだと思っています。
とあるプロジェクトにアサインされた若者がPCOMを見て放った
「常軌を逸したインターフェースですね」
という一言が忘れられません。z/OS Explorerとかz/OSMFとかZoweとか、新しいインターフェースが提供され始めましたが、そこに移行するにはもう少し時間がかかりそうです。これらがセットアップとかメンテナンスとかリソース不足とかパフォーマンスをほとんど意識することなく普通にデフォルトで使えるようになるといいんですけど、まだちょっとハードルがあるように思います。(ZoweのCLIには期待していたのですが、普段のオペレーションで使うにはちょっとコレジャナイ感があったし...)
ということで、ここに**新たなz/OSのオペレーション方法を提案します**。
...と、大口叩いてみたものの、実体としてはUSS上にちょっとしたREXX, Shell Scriptの作り物をして、teratermでいくつかの操作ができるようにしてみた、というものです。
作成物はGitHubに公開しています。
PCOMを無くせるとは思っていませんが、できるだけ他のインターフェース(主にteraterm)を活用して作業効率を上げる、あるいは、Unixに慣れている人にとって使いやすいインターフェース(CUI)を提供する、しかもそれを軽量な仕組みで実現する(Lightweight)、というのが狙いです。

[GitHub: Lightweight Command Utility on USS](https://github.com/tomotagwork/Lightweight_Command_Utility_on_USS)

参考: 
[z/OSのユーザー・インターフェースについて考える ～teraterm利用のすゝめ～](https://qiita.com/tomotagwork/items/5ae230c7a215f6ef4409)
[USSからREXX, シェル・スクリプトでz/OSを操作する](https://qiita.com/tomotagwork/items/10b3a83b3db65b9aea19)

# 概要
PCOMでよく利用するオペレーションの一部をteratermから行えるようにします。SYSLOG, JOBLOG, システムコマンド実行、PDSメンバー操作、辺りのオペレーションが対象です。そのために、USS上で稼働するREXX, Shell Script群を提供しています。
z/OS側では、USSが使えるユーザーとsshデーモンの稼働が前提となります(ここがハードルになってしまう場合もありますが、さすがにここは避けて通れません)。
また、一部の機能(dsExplorer.shのldサブコマンド)では、内部的にFTPを使っているので、FTPサーバーも上がっていることが前提です。
ただ、z/OSMF, Java, Node.jsなどは一切使っていないので、古いz/OS環境でも利用できると思います。

基本的には、提供しているスクリプト群をUSS上の適当な場所に配置して、PATHとaliasを設定してあげればよいです(設定方法などは後述)。
他の追加コンポーネントは不要です。
セキュリティ設定について以下の辺りを参照のこと。
参考: [REXXからSDSFアクセスについて](https://qiita.com/tomotagwork/items/362b0db5d4479eecebac#rexx%E3%81%8B%E3%82%89sdsf%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)

シェル上で各種テキスト情報を扱うようにしているため、Unixユーザーには馴染み深い方法で情報を扱えるのが特長です。例えば、grepで特定のキーワードを含む行だけ抽出したり、ファイルにリダイレクトしたり、必要な情報をコピペしたりするのが非常にやりやすくなると思います。

とりあえずどういうイメージで操作ができるかを手っ取り早く見たい場合は、先に下のユーティリティ使用例の[**デモ動画**](https://qiita.com/tomotagwork/items/bb7f9592658389d2c5c6#%E3%83%87%E3%83%A2%E5%8B%95%E7%94%BB)をご参照下さい。

# ユーティリティ機能説明
当Utilityで提供している機能について説明します。
このユーティリティでは主に以下の操作をUSSシェルから(ssh経由で接続したteratermから)利用するため機能を提供しています。

- SYSLOG表示
- SDSFコマンド実行
- STパネル/DAパネル相当のJOB一覧参照、および、JOBLOG確認
- JCLサブミット
- MVSデータセット参照、PDSメンバー編集(vi)

※ちょこちょこアップデートは入れているので、細かいところは少しずつ変わる可能性がありますのでご了承下さい。

## SYSLOG表示
関連ソース:
syslog.sh
syslog.rex

SYSLOGを参照するための機能です。

```xxx:Usage
$ syslog.sh -h
Usage: syslog.sh  [-h] | [-w [n] ] [-l [n] ] | [-fd YYYY/MM/DD] [-ft hh:mm:ss] [-td YYYY/MM/DD] [-tt hh:mm:ss]
  -h:              Show this help
  -w [n]:          Watch(tail) syslog with specifed interval in second. n=1-300 (default: 2sec)
  -l [n]:          Display syslog of last <n> minutes. n=1-59 (default: 1min)
  -fd YYYY/MM/DD   From Date (default: today)
  -ft hh:mm:ss     From Time (default: 00:00:00)
  -td YYYY/MM/DD   To Date (default: today)
  -tt hh:mm:ss     To Time (default: now)
  without option   Display syslog of last 1 minute.
```

### 直近のSYSLOG表示
何もオプションを指定しないと、直近1分間のSYSLOGを標準出力に表示します。
-lオプションで分単位で直近<n>分のSYSLOGを表示できます(lはLastの頭文字)。

例:
```syslog.sh```: 直近1分のSYSLOGを出力
```syslog.sh -l 10``` : 直近10分のSYSLOGを出力

### 特定の期間のSYSLOG表示
-fd, -ft, -td, -tt オプションで、表示させたい期間を指定して、その期間のSYSLOGを表示できます。
(オプション名は、それぞれFromDate, FromTime, ToDate, ToTimeの略です)
4つのうちいずれかのオプションを指定した場合、特定期間のSYSLOG表示となります。
Fromのデフォルトは当日の00:00:00、Toのデフォルトはスクリプト実行タイミングです。

例:
```syslog.sh -ft 10:00:00```: 当日の10:00から現在時刻までのSYSLOGを出力
```syslog.sh -tt 01:00:00```: 当日の00:00～01:00までのSYSLOGを出力
```syslog.sh -ft 10:00:00 -tt 11:00:00``` : 当日の10:00～11:00までのSYSLOGを出力
```syslog.sh -fd 2019/05/01 -ft 23:00:00 -td 2019/05/02 -tt 01:00:00```: 2019/05/01 23:00:00 ～ 2019/05/02 01:00:00 までのSYSLOGを出力

### 最新のSYSLOGを自動更新
-wオプションを指定すると、ファイルをtailしているようなイメージで、SYSLOGメッセージが追加されたら自動で表示させることができます(wというのはWatchの頭文字)。作業中はそのウィンドウ(シェル)はSYSLOG表示専用として置いておくのがよいでしょう。
デフォルトでは2秒おきに最新部分をAppendしていくことになりますが、更新間隔を秒単位で指定することも可能です。
-lオプションとの併用する場合、直近n分のSYSLOGを表示した上で、自動更新モードに入ります。

例:
```syslog.sh -w```: 直近1分のSYSLOG表示後、2秒間隔で自動更新を繰り返す
```syslog.sh -l 5 -w 10```: 直近5分のSYSLOG表示後、10秒間隔で自動更新を繰り返す


### <font color="red">注意！</font>

SYSLOGを取得するのに、REXXから```Address SDSF "ISFLOG READ TYPE(SYSLOG)"```というコマンドを使っており、この時、ISFLOGSTARTTIME, ISFLOGSTARTDATE, ISFLOGSTOPTIME, ISFLOGSTOPDAT という変数で取得する時刻を指定しています。ところが、マニュアルをよくよく見てみると、以下のような記述がありました。

[Browsing the system log with ISFLOG](https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.isfa600/isflog.htm)

>ISFLOGSTARTTIME, ISFLOGSTARTDATE, ISFLOGSTOPTIME and ISFLOGSTOPDATE define the date and time range for the records. Use them to ensure that your date and time range is reasonable, so that an excessive number of variables is not created.
When these special variables are used, SDSF positions the SYSLOG as near as possible to the requested record. <font color="red">However, due to the precision used for time stamps and the time the record is actually written to SYSLOG, it is possible that this may be several lines away from the desired record.</font>

記録されているタイムスタンプと実際に書かれるタイミングが若干ずれる可能性があるらしい。まぁそれはよいとしても、上の変数で指定した時刻と、実際返されるSYSLOGデータにズレがあるっぽい！実際、時刻を一定時間ずらして取得する今回のスクリプトでは、一部メッセージがロストしてしまうケースがありました。うむむ、イケてない。**厳密に**SYSLOG取得しなければいけないケースだとちょっと向かないです...。うぐぐ。


## SDSFシステムコマンド実行
関連ソース:
sdsf.rex

システムコマンドを実行するための機能です。

usage:
```sdsf.rex <command>```
これだけ。

コマンドの結果として返されるものは、標準出力に表示します。

```xxx:実行例
# sdsf.rex d iplinfo
RC:  0
ISFMSG:   COMMAND ISSUED
ISFMSG2.1 : ISF776I Processing started for action 1 of 1.
ISFMSG2.2 : ISF769I System command issued, command text: d iplinfo.
ISFMSG2.3 : ISF766I Request completed, status: COMMAND ISSUED.
ZOS1      2019136  20:49:05.64             ISF031I CONSOLE CICS004 ACTIVATED
ZOS1      2019136  20:49:05.64            -d iplinfo
ZOS1      2019136  20:49:05.65             IEE254I  20.49.05 IPLINFO DISPLAY 459
                                            SYSTEM IPLED AT 10.51.54 ON 04/01/2019
                                            RELEASE z/OS 02.03.00    LICENSE = z/OS
                                            USED LOADA0 IN SYS0.IPLPARM ON 08889
                                            ARCHLVL = 2   MTLSHARE = N
                                            IEASYM LIST = 08
                                            IEASYS LIST = (A0) (OP)
                                            IODF DEVICE: ORIGINAL(08889) CURRENT(08889)
                                            IPL DEVICE: ORIGINAL(08882) CURRENT(08882) VOLUME(EP23T1)
```

## JOBLOG表示
関連ソース:
joblog.sh
jobddlist.rex
joblog.rex
joblog_dd.rex
joblog_purge.rex

JOBLOGを参照するための機能です。
JOBIDを引数にして、起動すると専用プロンプトが表示され、対話形式でJOBLOGに対する操作ができます。

```xxx:Usage(起動方法)
$ joblog.sh
Usage:
 $ /u/cics004/Util/joblog.sh <JOBID>

 Example1:
  $ /u/cics004/Util/joblog.sh STC03076
```

※この機能は単発で使うこともできますが、後続のスクリプトと合わせ技で使う想定の機能です。

起動すると、当該JOBに対して、DDのリストが表示され、DD毎にJOBLOGを操作できます。

```xxx:実行例
$ joblog.sh STC00733
-----------------------
JOBNAME: CT54D4A1
JOBID:   STC00733
OWNER:   IBMUSER
Max-RC:
-----------------------
No.  DDNAME    StepName  ProcStep  DSID Owner     Rec-Cnt   Byte-cnt  CrDate-CrTime
========================================================================================
1    JESMSGLG  JES2      -         2    IBMUSER   2         136       2019.176 14:16:34
2    JESJCL    JES2      -         3    IBMUSER   120       10976     2019.176 14:16:34
3    JESYSMSG  JES2      -         4    IBMUSER   2         143       2019.176 14:16:34
4    DFHCXRF   CT54D4A1  -         101  IBMUSER   0         -         2019.176 14:16:35
5    MSGUSR    CT54D4A1  -         103  IBMUSER   854       86036     2019.176 14:16:35
6    CEEMSG    CT54D4A1  -         104  IBMUSER   0         -         2019.176 14:16:35
7    CEEOUT    CT54D4A1  -         105  IBMUSER   0         -         2019.176 14:16:35
8    COUT      CT54D4A1  -         109  IBMUSER   0         -         2019.176 14:16:36
9    CRPO      CT54D4A1  -         110  IBMUSER   0         -         2019.176 14:16:36

-------------------------
Select 0 for All DDs or DD Number (with file name optionally), or Input quit
 subcommand usage
   0              :Display Whole JOBLOG
   <num>          :Display JOBLOG of selected DD
   <num> <file>   :Output selected JOBLOG to file
   r              :Refresh DD list
   q|quit         :Quit this shell script

CT54D4A1/STC00733 >>>
```

上の例は、稼働中のCICSのJOBIDを指定して、当機能を実行した例です。
DDのリストと、サブコマンドの使い方が表示されます。
最終行に専用のプロンプト```CT54D4A1/STC00733 >>>```が表示されます(行頭の文字列は、参照しているJOBNAME/JOBIDを表しています)。
このプロンプトに、DD名の左に表示されている番号を入力すると、そのDDの内容が表示されます。結果は"more"コマンドで表示されるので、moreのサブコマンドで表示を制御することができます([space]で次ページ、[b]で前ページ、[/]でキーワード検索、など)。
0番を入力すると、DD毎ではなくJOBLOG全体が表示されます。
番号の後にブランク区切りで文字列(joblog.txt)などを入力すると、結果がファイルにリダイレクトされます。
サブコマンド未入力でカラEnterを押すと、再度DDリストが表示されます。
rでDDリストのリフレッシュが行われ、qでこのプロンプトを抜けます。

なお、このJOBLOGのDDのリストは、シェルスクリプト実行中は一時ファイル(テキストファイル)に情報を保持し、最後に削除するようにしています。一時ファイルは、```/tmp/Util_<pid>_<YYYYMMDD>_<hhmmss>.txt``` というfile名で保持します。シェルスクリプトを途中で異常終了させたりするとゴミが残る可能性がありますのでご注意ください。

## STパネル情報表示
関連ソース:
st.sh
st.rex
(+JOBLOG表示用スクリプト)

STパネルの情報を参照するための機能です。

```xxx:Usage
$ st.sh -h
Usage: st.sh [-j jobname] [-o owner] [-i]
  -j: jobname filter (pertial match)
  -o: owner filter (partial mathc)
  -i: interactive mode
```

```xxx:実行例
$ st.sh -o cics004
No.  JOBNAME   JobID     Owner     Prty Queue     PhaseName             C  Pos   Max-RC      Status    SysName
================================================================================================================
1    CICS004   TSU08034  CICS004   15   EXECUTION EXECUTING             -  -     -           -         ZOS1
2    LISTC     JOB07460  CICS004   1    PRINT     AWAITING OUTPUT       A  1260  CC 0000     -         -
3    TAGBPX01  JOB07461  CICS004   1    PRINT     AWAITING OUTPUT       A  1261  CC 0000     -         -
4    LISTC     JOB07464  CICS004   1    PRINT     AWAITING OUTPUT       A  1262  CC 0000     -         -
5    TAGBPX01  JOB07508  CICS004   1    PRINT     AWAITING OUTPUT       A  1284  CC 0000     -         -
6    TAGBPX01  JOB07511  CICS004   1    PRINT     AWAITING OUTPUT       A  1286  CC 0000     -         -
7    TAGBPX01  JOB07512  CICS004   1    PRINT     AWAITING OUTPUT       A  1287  CC 0000     -         -
8    TAGBPX01  JOB07513  CICS004   1    PRINT     AWAITING OUTPUT       A  1288  CC 0000     -         -
9    LISTC     JOB07515  CICS004   1    PRINT     AWAITING OUTPUT       A  1289  CC 0000     -         -
10   LISTC     JOB07539  CICS004   1    PRINT     AWAITING OUTPUT       A  1300  CC 0000     -         -
11   LISTC     JOB07565  CICS004   1    PRINT     AWAITING OUTPUT       A  1307  CC 0000     -         -
12   TRANCBL   JOB08060  CICS004   1    PRINT     AWAITING OUTPUT       A  1606  CC 0008     -         -
13   TRANCBL   JOB08066  CICS004   1    PRINT     AWAITING OUTPUT       A  1610  CC 0000     -         -
```

### JOBリスト表示のみ
引数を何も指定せずにst.shを実行すると、STパネルで表示できる全JOBのリストが表示されます。
-jオプション、-oオプションで、それぞれJOBNAME, OWNERでの絞込みができます(両方指定した場合はAND条件となります)。
それぞれのオプションで指定する文字列は部分一致でのフィルターになります。
指定した文字列は大文字に変換して検索するので、小文字での指定でもOKです。

例:
```st.sh```: STパネルに表示される全JOBのリストを表示
```st.sh -j ct54```: JOBNAMEに"CT54"を含むJOBのリストを表示
```st.sh -j tran -o cics004```: JOBNAMEに"TRAN"を含み、かつ、OWNERに"CICS004"を含むJOBのリストを表示

### 対話モード
さらに、-iオプションを指定すると、リスト表示に続いて専用プロンプトが表示され、対話形式でリストされたJOBを操作することができます。

```xxx:実行例
$ st.sh -j ct54 -i
No.  JOBNAME   JobID     Owner     Prty Queue     PhaseName             C  Pos   Max-RC      Status    SysName
================================================================================================================
1    CT54D0CM  STC00597  IBMUSER   15   EXECUTION EXECUTING             -  -     -           ARMELEM   ZOS1
2    CT54D0WE  STC03070  IBMUSER   15   EXECUTION EXECUTING             -  -     -           ARMELEM   ZOS1
3    CT54D4A1  STC03076  IBMUSER   15   EXECUTION EXECUTING             -  -     -           ARMELEM   ZOS1
4    CT54DMA1  STC08021  IBMUSER   15   EXECUTION EXECUTING             -  -     -           ARMELEM   ZOS1

-------------------------
Select JOBID Number, or Input quit
 subcommand usage
   <num>     :Display DD list
   <num> p   :Purge JOBLOG
   r         :Refresh JOBID list
   q|quit    :Quit this shell script

ST: >>
```

末尾に専用のプロンプト```ST: >>```が表示されます。
ここに、JOBのリストの左の数字を入力すると、当該JOBのJOBIDを指定して先のJOBLOG表示機能が呼び出されます。つまり、ここからJOBを選択してそのJOBLOGを見ることができます。
数字の後にpを入力すると、選択したJOBをPurgeすることができます。
サブコマンド未入力でカラEnterを押すと、再度JOBのリストが表示されます。
rでJOBリストのリフレッシュが行われ、qでこのプロンプトを抜けます。



## DAパネル情報表示
関連ソース:
da.sh
da.rex
(+JOBLOG表示用スクリプト)

DAパネルの情報を参照するための機能です。
使い方はSTと同様なので割愛します。


## JCLサブミット
関連ソース:
submitJcl.sh
checkJobStatus.rex
(+JOBLOG表示用スクリプト)

JCLをサブミットする機能です。

```xxx:Usage
$ submitJcl.sh -h
Usage: submitJcl.sh [-f <jcl_file> | -d <jcl_dataset>] [-i|-s] [-t <timeout>] [-p <propertyFile> | -l <propertyList>]
 -f : specify uss file
 -d : specify PDS member
 -i : interactive mode to display JOBLOG
 -s : script mode to execute in shell script
 -t : timeout in sec for waiting JOB completion
       default 0 sec (no timeout) / speciry between 0 to 3600
 -p : specify property file name on USS
       (@prop01@=XXX in property file means replacement from @prop01@ to XXX in JCL)
 -l : specify property list instead of file in following format: @prop01@=XXX,@bbb@=YYY,@ccc@=ZZZ
 -c : check JCL before submit

  Example1: submitJcl.sh -f sleep.jcl
  Example2: submitJcl.sh -d 'CICSSHR.CICS004.JCLLIB(LISTC)'
  Example3: submitJcl.sh -f template.jcl -l "@prop01@=XXX,@prop02@=YYY"
```

実行したいJCLとしては、-fオプションでUSS上のファイルか、-dオプションでPDSメンバーのいずれかを指定できます。
-fオプションで指定するファイルは、相対パスもしくは絶対パスで指定します。
-dオプションで指定するPDSメンバーは、シングルクォーテーションでくくります。

### サブミットのみ

例:
```submitJcl.sh -f sleep.jcl```: カレントディレクトリにあるsleep.jclをサブミット
```submitJcl.sh -f /u/cics004/test/sleep.jcl```: /u/cics004/test/sleep.jclをサブミット
``` submitJcl.sh -d 'CICSSHR.CICS004.JCLLIB(LISTC)'```: PDSメンバー'CICSSHR.CICS004.JCLLIB(LISTC)'をサブミット

いずれも、```JobID: JOB08350```というように、JobIDが結果として返されます。

### 対話モード
-iオプションを指定してJCLをサブミットした場合、当該JOBの完了を待って、Max-RCを表示、さらに、JOBLOG表示機能が呼び出され、JOBLOGを確認することができます。

```xxx:実行例
$ submitJcl.sh -f sleep.jcl -i
JobID: JOB08352
waiting......
Max-RC: CC 0000
-----------------------
JOBNAME: TAGBPX01
JOBID:   JOB08352
OWNER:   CICS004
Max-RC:  CC 0000
-----------------------
No.  DDNAME    StepName  DSID Owner     Rec-Cnt   Byte-cnt  CrDate-CrTime
==============================================================================
1    JESMSGLG  JES2      2    CICS004   20        1185      2019.136 22:20:43
2    JESJCL    JES2      3    CICS004   9         519       2019.136 22:20:43
3    JESYSMSG  JES2      4    CICS004   20        1157      2019.136 22:20:43
4    STDOUT    STEP1     103  CICS004   4         105       2019.136 22:20:43

-------------------------
Select 0 for All DDs or DD Number (with file name optionally), or Input quit
 subcommand usage
   0              :Display Whole JOBLOG
   <num>          :Display JOBLOG of selected DD
   <num> <file>   :Output selected JOBLOG to file
   r              :Refresh DD list
   q|quit         :Quit this shell script

TAGBPX01/JOB08352 >>>
```

上の例は、意図的にSleepを入れて少し時間のかかるJOBを実行した例です。
JobIDを出力した後に、```waiting....```という表示がありますが、ここがJOBの完了を待っていることを表しています。1秒間隔でJOBのステータスをチェックしにいきその都度「.」を出力しています(時間がかかるJOBはwaitingの後の「.」が長くなっていく)。
JOBが完了すると、Mac-RCを出力し、JOBLOG表示機能が自動的に呼ばれ、JOBLOGを確認することができます。
デフォルトではJobの完了を待つタイムアウト値は0sec(タイムアウトなし)ですが、-tオプションで明示指定することもできます(秒単位)。

### スクリプトモード
-sオプションを指定してJCLをサブミットした場合は、スクリプトモードとなります。
このモードは、自動化などのためにスクリプトから実行されることを想定しています。
当該JOBの完了を待って、Max-RCを表示します。
JOBのRCが0～255以内であれば、それをシェルの実行結果コードとして返します。
(exit の値としてRCを返すので、後続処理では$?で結果コードを判定可能)
それ以外(RCが255以上や、JCL ERROR, ABENDなど)の場合は結果コード255を返します。
-tオプションでタイムアウトを指定して、その時間内にジョブの実行が完了しなかった場合は結果コード200を返します。

### テンプレートの使用
JCL中の一部の文字列を変数として埋め込んだテンプレートを用意しておき、実行時にその文字列を適切な文字列に置き換えて実行するためのオプション"-p", "-l"を用意しています。
変数は、他で使っていないような文字列として定義します。("@"を前後ではさんだ文字列など)

例えば、以下のようなJCLテンプレートを用意します。

```jcl:test_template.jcl
//@JOBNAME@ JOB ,CLASS=A,MSGCLASS=X
//*********************************************************************
//STEP1    EXEC PGM=BPXBATCH,REGION=0M,MEMLIMIT=4G,
//         PARM='PGM /u/cics004/Util_test/sleep.sh 10'
//STDOUT   DD   SYSOUT=*
//STDERR   DD   SYSOUT=*
//STDENV   DD   *
ENVTEST1=aaa
ENVTEST2=bbb
PTEST01=@prop_Test01@
PTEST02=@prop_Test02@
PTEST03=@prop_Test03@
PTEST04=@prop_Test04@_@prop_Test01@_@prop_Test04@
PTEST05=@prop_Test05@
/*
//
```

上のJCLでは、```@JOBNAME@``` や、```@prop_Test01@```が変数として定義されています。
これらの変数に具体的な値を設定するためのプロパティファイルを用意します。

```txt:test_properties.txt
@JOBNAME@=TAGSLEEP
@prop_Test01@=AAA
@prop_Test02@=BBB
@prop_Test03@=CCC
@prop_Test04@=DDD
@prop_Test05@=EEE
```

これを使って、以下のようにJCLをサブミットすると、test_properties.txtの内容でJCLテンプレートの変数が置き換えられて実行されます。

```xxx:実行例
$ submitJcl.sh -i -f test_template.jcl -p test_properties.txt
```

変数名が少ない場合は、プロパティファイルを作らなくても、以下のようなフォーマットで-lオプションで直接変数名と値を指定できます。

```xxx:実行例
$ submitJcl.sh -i -f test_template.jcl -l "@JOBNAME@=TAGSLEEP,@prop_Test01@=AAA,@prop_Test02@=BBB,@prop_Test03@=CCC,@prop_Test04@=DDD,@prop_Test05@=EEE"
```

```-c``` オプションを使用すると、変数置換後のJCLを事前にチェックしてからJCLを実行することができます。
(moreでJCLを確認し、その後プロンプトでy/nを選択することでJCLの実行/キャンセルが可能)

※注意点
この仕組みでは、JCLテンプレートとして指定されたテキストファイル中の、```xxx=yyy```という式の左辺の文字列を単純に右辺の文字列に置換しているだけですので、JCL中の任意の箇所を変数に置き換えることができます。ただし、指定された後の文字列によって桁数のずれが生じる可能性がありますので、テンプレートの作成、値の指定は、桁数を意識した上で実施する必要があります。
また、変数の置換には内部的にsedコマンドを使用しています。そのため、置換前後の文字列に「```/```」や「```(```」など一部の記号についてはバックスラッシュによるエスケープ表記をする必要があります。
例:「```\/```」,「```\(```」

## MVSデータセット操作
関連ソース:
dsExplorer.sh

MVSデータセット(主にPDSのメンバー)を、USS上のファイルっぽく操作できるようにしたものです。
専用プロンプトが表示され、対話モードで実行することになります。

```xxx:Usage
$dsExplorer.sh
-------------------------
subcommand usage
   pwd                     :Print current working directory
   cd <dir>                :Change working directory
                             you can specify absolute/relative path with using ".."
   ls                      :List dataset for current working directory
   lm                      :List member for current working directory (valid only at PDS dataset)
   ld                      :List detail of dataset for current working directory by using ftp
                             this command requires ftp password at first time
   vi <member>             :Edit/Create member in current working directory by using vi
   rm <member>             :Remove member in current working directory
   cp <mem1> <mem2>        :Copy member from <mem1> to <mem2> in current working directory
   clear                   :Clear cached ftp password
   q|quit                  :Quit this shell script

/ >>>
```


### サブコマンド: cd
ドット(.)区切りのデータセットをディレクトリのように見立てて、cd (Change Directory)コマンドで移動できます。CICSSHR.CICS004.JCLLIBという構造のデータセットがあった場合、cd /cicsshr/cics004/jcllib といった感じで移動できます。
絶対パス、相対パス指定ができ、一つ上の上位ディレクトリは「..」で指定できます。

```xxx:実行例
/ >>> cd /cicsshr/cics004/jcllib
/cicsshr/cics004/jcllib >>>
```
プロンプトの先頭に現在の作業ディレクトリが表示されます。
最初はルートディレクトリです。

### サブコマンド: pwd
ドット(.)区切りのデータセットをディレクトリのように見立てて現在の作業ディレクトリを表示します。
表示はデータセット形式で、大文字/ドット区切りです。

```xxx:実行例
/cicsshr/cics004/jcllib >>> pwd
CICSSHR.CICS004.JCLLIB
```

### サブコマンド: ls

現在の作業ディレクトリ下のデータセットのリストを表示します。
ルートディレクトリにいる場合はHLQのリスト、それ以外の場合はデータセットのリストが表示されます。
パイプが1度だけ使えますので、grepやmoreと組み合わせて利用できます。

```xxx:実行例
/ >>> ls | grep SYS
SYS0
SYSADMIN
SYSGEN21
SYSLOGD
SYS1
/ >>> cd SYS0
/SYS0/ >>> ls | more

0      NONVSAM    SYS0.ARM.CDS01
1      NONVSAM    SYS0.ARM.CDS02
2      NONVSAM    SYS0.CFRM.CDS01
3      NONVSAM    SYS0.CFRM.CDS02
4      NONVSAM    SYS0.CMDPROC
5      CLUSTER    SYS0.IODF00.CLUSTER
6      DATA       SYS0.IODF00
7      NONVSAM    SYS0.IODF00.WORK.ACTLOG
8      CLUSTER    SYS0.IODF00.WORK.CLUSTER
9      DATA       SYS0.IODF00.WORK
10     NONVSAM    SYS0.IODF01.ACTLOG
11     CLUSTER    SYS0.IODF01.CLUSTER
12     DATA       SYS0.IODF01
13     NONVSAM    SYS0.IPLPARM
14     NONVSAM    SYS0.ISPPLIB
15     NONVSAM    SYS0.LINKLIB
16     NONVSAM    SYS0.LOGR.CDS01
17     NONVSAM    SYS0.LOGR.CDS02
18     NONVSAM    SYS0.OMVS.CDS01
19     NONVSAM    SYS0.OMVS.CDS02
20     NONVSAM    SYS0.PARMLIB
21     NONVSAM    SYS0.PARMLIB.BK180806
22     NONVSAM    SYS0.PARMLI2
23     NONVSAM    SYS0.PROCLIB
24     NONVSAM    SYS0.PROCLIB.BACKUP
25     NONVSAM    SYS0.SFM.CDS01
26     NONVSAM    SYS0.SFM.CDS02
27     NONVSAM    SYS0.TCPPARMS
28     NONVSAM    SYS0.VTAMLIB
29     NONVSAM    SYS0.VTAMLST
30     NONVSAM    SYS0.WLM.CDS01
31     NONVSAM    SYS0.WLM.CDS02
32     NONVSAM    SYS0.XCF.CDS01
33     NONVSAM    SYS0.XCF.CDS02
34     NONVSAM    SYS0.Z21VUP.CATALOG.Z23
35     NONVSAM    SYS0.Z21VUP.JCLLIB
36     NONVSAM    SYS0.Z23VUP.JCLLIB
37     NONVSAM    SYS0.Z23VUP.PREWORK.JCLLIB
38     ALIAS      SYS0
--More--(EOF)
```

### サブコマンド: lm
現在の作業ディレクトリがPDS/PDSEの場合に、そこに含まれるメンバーのリストを表示します。

```xxx:実行例
/ >>> cd /cicsshr/cics004/jcllib
/cicsshr/cics004/jcllib >>> lm
--VOLUMES--
Z9CI01
--MEMBERS--
BAQJS2LS
BAQLS2JS
CBCMLK00
CPALIST
CPALISTE
CPASTAE
CPASUME
CPASUM1
CSDUPDEF
CSDUPLIS
(以下略)
```

### サブコマンド: ld / clear

現在の作業ディレクトリ下のデータセットの詳細情報を表示します。
現在の作業ディレクトリがPDS/PDSEの場合には、そこに含まれるメンバーの詳細情報を表示します。

※注意
ルートディレクトリの場合は無効です。
この機能のみ、FTPを利用しています(自分自身のホスト名に対して、ログインしているユーザーを使ってFTP接続して情報を取得します)。
そのため、ldコマンド実行時は、最初のみパスワードの入力が求められます。パスワードはキャッシュされるので、2回目以降はパスワード入力の必要はありません。パスワードを間違うと情報の取得ができないので、いったんclearサブコマンドでキャッシュをクリアしてから再度パスワード入力して下さい。

```xxx:データセットの例
/ >>> cd cicsshr/cics004
/cicsshr/cics004/ >>> ld
 Input Password for CICS004 : xxxxxxxx
Volume Unit    Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname
Z11CU1 3390   2018/02/08  1    5  VB      84 27998  PS  CERTIP
Z11CU1 3390   2016/01/19  1    5  VB      84 27998  PS  CERTWEB
Z11CU1 3390   2018/02/27  1    5  VB      84 27998  PS  CERT2048
Z11SM1 3390   2016/01/19  1    5  VB      84 27998  PS  CLICERT
Z9CI01 3390   2019/03/07  1   75  FB      80 27920  PO  COPYLIB
ZR21W2 3390   2014/01/27  1    5  VB      84 27998  PS  ISE2WEB
Z11CU1 3390   2014/01/27  1    5  VB      84 27998  PS  ISE22048
Z11SM1 3390   2018/02/27  1    5  VB      84 27998  PS  ISE3CTG
Z11CU1 3390   2014/02/03  1    5  VB      84 27998  PS  ISE32048
Z9CI01 3390   2019/07/01  1  150  FB      80 27920  PO  JCLLIB
Z9CI01 3390   2019/07/01  1   15  FB      80 27920  PO  JCLLIB.BK
CICVLJ 3390   2019/05/28  2 1545  FB     140 14000  PO  JOBLOG
Z9CI01 3390   2019/06/13  2  150  FB      80 27920  PO  PGMSOR
Z9CI01 3390   2019/07/01  1   15  FB      80 27920  PO  REXX
Z9CI01 3390   2019/06/25  1  150  U    23200 23200  PO  USERLIB
Z9CI01 3390   2019/06/25  1   15  U    23200 23200  PO  USERLIB2
250 List completed successfully.
```

```xxx:PDSメンバーの例
/cicsshr/cics004/ >>> cd jcllib
/cicsshr/cics004/jcllib/ >>> ld
 Name     VV.MM   Created       Changed      Size  Init   Mod   Id
BAQJS2LS  01.01 2016/03/14 2016/10/25 18:54   100   100     0 CICS004
BAQLS2JS  01.07 2016/03/14 2016/10/25 18:54   101   101     0 CICS004
CBCMLK00  01.00 2012/10/17 2012/10/17 16:26    43    43     0 CICS004
CPALIST   01.24 2012/05/10 2016/06/09 16:55    90    75     0 CICS004
CPALISTE  01.27 2012/05/10 2016/06/13 20:17    93    75     0 CICS004
CPASTAE   01.26 2012/05/10 2016/06/13 20:27    65    78     0 CICS004
CPASUME   01.24 2012/05/10 2016/06/13 20:17    88    78     0 CICS004
CPASUM1   01.20 2012/05/10 2016/06/13 14:36    85    78     0 CICS004
CSDUPDEF  01.01 2016/01/18 2016/01/18 13:52   143   143     0 CICS004
CSDUPLIS  01.02 2016/01/18 2016/01/18 13:57    34   143     0 CICS004
(以下略)
```

※操作しようとしているメンバーの作成日や最終更新日時を確認したいということは多々あるので、この機能必要だよなと思ったのですが、USSからデータセットの詳細情報取ってくるうまい仕組みが見つかりませんでした。わざわざFTP使うのもどうかと思うのですがこれが一番手っ取り早く情報取れました(アイデアくれたKさんに感謝)。
ただ、頑張って無理やりFTPまで使ってこの機能を作ったのですが、後述のviサブコマンドを使ってファイル編集しちゃうと、内部的にはUSSファイルへコピー => 編集 => PDSメンバーへ上書きコピー という処理が行われることになり、そうすると作成日とか更新日時がブランクになってしまうという、なんとも残念な状況になってしまいました...。うむぅ。

### サブコマンド: cp
現在の作業ディレクトリがPDS/PDSEの場合に、そこに含まれるメンバーをコピーします。(現行作業ディレクトリ内でのコピーのみ)

```xxx:事項例
/cicsshr/cics004/jcllib/ >>> lm | grep LIST
CPALIST
CPALISTE
LISTC
LISTC2
LOGLIST
/cicsshr/cics004/jcllib/ >>> cp LISTC2 LISTC3
copy from CICSSHR.CICS004.JCLLIB(LISTC2) to CICSSHR.CICS004.JCLLIB(LISTC3)
/cicsshr/cics004/jcllib/ >>> lm | grep LIST
CPALIST
CPALISTE
LISTC
LISTC2
LISTC3
LOGLIST
```

### サブコマンド: vi
現在の作業ディレクトリがPDS/PDSEの場合に、そこに含まれるメンバーをviで編集します。
裏では、一旦USS上のテンポラリーファイルに指定したメンバーの内容をコピーし、それをviエディターで開きます。保存すると、それをPDSメンバーとして上書きコピーします。
存在しないメンバー名を指定した場合は、新規にメンバーが作成されることになります。

```xxx:(1)_vi実行
/cicsshr/cics004/jcllib/ >>> vi LISTC3
```

```xxx:(2)_viエディターで編集
//LISTC    JOB   MSGCLASS=X,CLASS=A,NOTIFY=&SYSUID                      00012038
//*--test ------------------------------------------
//*                                                                     00014038
//GO       EXEC PGM=IDCAMS,REGION=0M                                    00018038
//SYSPRINT DD SYSOUT=*                                                  00019038
//SYSIN DD *                                                            00020038
 LISTC LEVEL('CICSTS54.@DBDC')                                          00021039
/*                                                                      00022038
~
~
~
~
~
~
"/tmp/Util_83952228_20190701_194149_LISTC3.txt" 8 lines, 620 characters
```

```xxx:(3)_保存して終了
Override member? / CICSSHR.CICS004.JCLLIB(LISTC3)
(y/n) >>>y
save this change
Created new member CICSSHR.CICS004.JCLLIB(LISTC3)
```

### サブコマンド: rm
現在の作業ディレクトリがPDS/PDSEの場合に、そこに含まれるメンバーを削除します。

```xxx:実行例
/cicsshr/cics004/jcllib/ >>> lm | grep LIST
CPALIST
CPALISTE
LISTC
LISTC2
LISTC3
LOGLIST
/cicsshr/cics004/jcllib/ >>> rm LISTC3
Remove member? / CICSSHR.CICS004.JCLLIB(LISTC3)
(y/n) >>>y
remove this member
/cicsshr/cics004/jcllib/ >>> lm | grep LIST
CPALIST
CPALISTE
LISTC
LISTC2
LOGLIST
```

## (参考)共通関数
ソース:
commonFunctions.sh

いくつかのシェル・スクリプトで使用するロジック(オプションのチェックなど)を共通関数として定義しています。

# ユーティリティー使用例
さてさて、このユーティリティーを使うとどんな感じで作業ができるのか、実際の使用例をキャプチャしてみました。

## シナリオ
この動画では、以下のような操作を行っています。

- SYSLOG表示(tail)
- CICSリージョンの稼働確認
    - Displayコマンド
    - DAパネル
- CICSリージョン停止
    - Modifyコマンド(CEMT P SHUT)
- 起動プロシージャー編集
    - viエディター
- CICSリージョン再起動
    - Startコマンド
- CICS-COBOLプログラム編集
    - viエディター
- コンパイル/リンク
    - JCLサブミット
- CICS資源定義追加
    - JCLサブミット
- CICS資源定義インストール
    - Modifyコマンド(CEDA)
- テスト実施
    - PCOMからCICS端末に接続してCECI(インタープリター)
- CICS JOBLOG確認
    - DAパネル

このシナリオでは、アプリのテスト用にCICS端末接続するためにPCOM使っていますが、それ以外は全てteratermからオペレーションを行っています。(TSOは未使用)

## 前提
添付のデモ動画の環境では、操作しやすくするために、以下のようなAliasを設定したシェルにて実行しています。

```sh:util_env.sh
UtilDir=/u/cics004/Util
export PATH=$UtilDir:$PATH

alias sdsf=sdsf.rex
alias d="sdsf d"
alias f="sdsf f"
alias s="sdsf s"
alias p="sdsf p"
alias v="sdsf v"
alias da="da.sh -i"
alias st="st.sh -i"
alias syslog=syslog.sh
alias jl="joblog.sh"
alias sub="submitJcl.sh -i"
alias ds="dsExplorer.sh"
```

UtilDirは、このUtilityのシェルスクリプト群を配置したディレクトリです。
DA情報確認とか、JCLサブミットは、デフォルトで対話モードにしたいので、-iオプション付きでalias定義しています。
上のスクリプトを、利用ユーザーの.profileに仕込んでおけばOKです。

```sh:.profile抜粋

. /u/cics004/Util/util_env.sh

```


## デモ動画
<blockquote class="twitter-tweet" data-lang="en"><p lang="und" dir="ltr"><a href="https://t.co/Tep6rUPAde">https://t.co/Tep6rUPAde</a></p>&mdash; tomotagwork (@tomotagwork) <a href="https://twitter.com/tomotagwork/status/1150579722511761413?ref_src=twsrc%5Etfw">July 15, 2019</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


# 補足

## TSOコマンド実行
USSのシェルからTSOコマンドを実行するための機能はデフォルトで提供されています。その名も「tsocmd」。
Utilityとして提供しているシェル・スクリプト中からも使用しています。

```xxx:実行例
[CICS004@EPLEX1:/u/cics004] tsocmd listuser
listuser
USER=CICS004  NAME=CICS004               OWNER=IBMUSER   CREATED=02.129
 DEFAULT-GROUP=ESA      PASSDATE=19.175 PASS-INTERVAL= 30 PHRASEDATE=N/A
 ATTRIBUTES=SPECIAL OPERATIONS
 REVOKE DATE=NONE   RESUME DATE=NONE
 LAST-ACCESS=19.190/13:11:56
 CLASS AUTHORIZATIONS=NONE
 NO-INSTALLATION-DATA
 NO-MODEL-NAME
 LOGON ALLOWED   (DAYS)          (TIME)
 ---------------------------------------------
 ANYDAY                          ANYTIME
  GROUP=ESA       AUTH=USE      CONNECT-OWNER=IBMUSER   CONNECT-DATE=02.129
    CONNECTS= 2,665  UACC=NONE     LAST-CONNECT=19.190/13:11:56
    CONNECT ATTRIBUTES=NONE
    REVOKE DATE=NONE   RESUME DATE=NONE
SECURITY-LEVEL=NONE SPECIFIED
CATEGORY-AUTHORIZATION
 NONE SPECIFIED
SECURITY-LABEL=NONE SPECIFIED


[CICS004@EPLEX1:/u/cics004] tsocmd "LISTCAT LEVEL('CICSSHR.CICS004')"
LISTCAT LEVEL('CICSSHR.CICS004')
NONVSAM ------- CICSSHR.CICS004.CERTIP
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.CERTWEB
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.CERT2048
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.CLICERT
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.COPYLIB
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.ISE2WEB
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.ISE22048
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.ISE3CTG
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.ISE32048
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.JCLLIB
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.JCLLIB.BK
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.JOBLOG
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.PGMSOR
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.REXX
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.USERLIB
     IN-CAT --- UCAT.Z9CI01
NONVSAM ------- CICSSHR.CICS004.USERLIB2
```

## PDSメンバー<=>USSファイルのコピー
こちらも、デフォルトの機能として、USSのシェルのcpコマンドで、USSファイルとMVSのPDSメンバー間でコピー操作ができます。

```xxx:実行例
[CICS004@EPLEX1:/u/cics004/JCL] cp "//'CICSSHR.CICS004.JCLLIB(LISTC)'" listc2.jcl

[CICS004@EPLEX1:/u/cics004/JCL] cp listc2.jcl "//'CICSSHR.CICS004.JCLLIB(LISTCX)'"

```

MVSデータセットは、シングルクォーテーションでくくって、先頭に//を付けて、さらに全体をダブルクォーテーションでくくります。
これもUtilityで内部的に利用しています。

参考: [z/OS UNIX ファイル・システムと MVS データ・セットとの間でのデータのコピー](https://www.ibm.com/support/knowledgecenter/ja/SSLTBW_2.3.0/com.ibm.zos.v2r3.bpxa400/fcop.htm)


## ユーザー指定のJCLサブミット
よく、JCL実行時に、JOBパラメーターでUSER名指定でJCLサブミットすることがありますが、シェルから上のsumitJCL.sh使う場合は、その方法は使えません。基本的にsshでログインしているユーザー権限で実行されることになりますので、実行ユーザーを変えたい場合は、su でJCL実行したいユーザーにスイッチしてからサブミットして下さい。

## パスワード変更
PCOMでTSOに入る時には、パスワードがExpireしていなくてもデフォルトでNew Passwordを指定する欄があります。

teratermからSSHで接続する場合には、ログイン時に新パスワードを指定する欄なんてありませんので、明示的にパスワード変更する場合は個別にコマンド叩く必要があります。
Unix系と同様にシェルから```passwd```コマンドで変更可能です。

```xxx:例
$ passwd
Enter current password: *********
Enter new password: ********
Enter new password again: ********
```

## 弱点
基本、テキストしか扱えません。例えばJOBLOGとかに文字化けするようなデータが入っているような場合はうまく表示できません。当然HEXでの参照、入力もできません。
日本語(DBCS)は想定していないません。テストしきれていないですが色んな所で不具合出てくるでしょう。
データセット周りの操作は苦手です。PDSデータセット作成とかできません。
エディタはviだけだとやはり限界があるので、ゴリゴリ編集する必要があるのであれば専用のエディタを利用するのがよいでしょう。現時点ではEclipseベースのツール(z/OS Explorer, IDzあたり)が有力かと思いますが、最近だとZowe+VSCodeというのが私の中で大ヒットです。

# 応用編
[z/OS上でのTCP/IPパケットトレース取得メモ](https://qiita.com/tomotagwork/items/418cf7bf120720aa54bf)
[z/OSからSlackへのメッセージ送信](https://qiita.com/tomotagwork/items/3bfca18326635998ddb1)

# おわりに
ウィンドウサイズを気にせず沢山の情報を一度に表示したり、複数ウィンドウを並べて表示したり、必要な情報を抽出してファイルに落としたり、というように、Unixで当たり前のように使われているteraterm上の操作で、z/OS周りの操作がしやすくなるかと思います。
今回はユーザーがシェルから直接使う想定で作っていますが、シェル・スクリプトベースなので、出力フォーマットをもう少し加工すれば、自動化にも応用が利くと思います。USS上のシェル・スクリプトなので、SSH経由でリモートのオープン系との連携もできます。(なんでもかんでもREST APIじゃなくても、SSHで繋がれば結構いろんな連携できるんですよね。インフラ系は特に。)

ちなみにz/OSMFでは、テンプレートを元に決められたワークフローを流して環境の複製を自動化するようなプロビジョニング機能が提供されています。当ユーティリティでは、JCLテンプレート+プロパティファイルで、外から変数与えてJOB実行できるようにしていますが、これはz/OSMFのワークフローを意識して作りました。単純に変数置き換えしかできませんが、この辺りの操作がUSSシェルで実行できると、シェル・スクリプトでフローを書けるので、自動化の仕組みが断然作りやすくなります。
例えば、上のデモ動画で、CICSアプリのソース編集、資源定義辺りを個々のコマンドで実行しましたが、あの辺スクリプトにしてしまえば、ソース編集したら一発コマンド叩くだけでテストまで自動的に実行！、みたいなことに繋がると思います。しかも、製品/機能に特化した新しいスキルはあまりいらないのが利点です。
単純<=>複雑 / 機能:少<=>機能:多 辺りはトレードオフなのでどちらがよいかは状況次第ですが、製品提供機能はどうも重厚長大で機能過多になりがちな気がします。もちろん仕組みを自作してしまうとそれを自分でメンテナンスしなければならないということも出てくるのですが。その辺りは環境や要件を見極めて選択していくことになるでしょう。
※ということで当ユーティリティーの動作保証はしかねますので悪しからず。