IBM i (旧AS400）の自己学習メモ　コマンド／ツール／運用　*FILE SAVFオブジェクトを取り扱う

# 自己学習メモインデックス

[IBM i (旧AS400）の自己学習メモ　インデックス](https://qiita.com/You_name_is_YU/items/3964849e7803c8eaf767)

# 概要

AS400を利用していると、開発会社に開発してもらったプログラムなどを、***.SAVFという拡張子のファイルで連携し、顧客環境へ展開してUATや本番リリースを行うケースがあります。（あるようです）
そこで、*FILE SAVFオブジェクトが自分でも取り扱えるようになると便利かなと思い、いじってみることにしました。

この記事では、`*FILE SAVFオブジェクト`のことを`SAVFファイル`と呼称します。

# SAVFファイルの取り扱い

## 指定したオブジェクトをSAVFファイルに保存してローカルPCに取得する

### 空のSAVFファイルを作成する

```shell-session
crtsavf file(yamaguchi1/testsavf) TEXT('テスト用のSAVF')
```

<img width="1088" alt="スクリーンショット 2020-06-22 8.09.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/c0924c83-496b-3ede-085b-8cc1716ad314.png">

### SAVOBJコマンドで指定したオブジェクトをSAVFファイルに保管する

ここでは、`QCLSRC`と`QSAVTSTSRC`の2つのファイルをSAVFファイルに保管しています。
CLEAR(*REPLACE)は、既に存在するSAVFファイルの中身を削除してから置き換えるという指定です。

```shell-session
SAVOBJ OBJ(QCLSRC QSAVTSTSRC) LIB(YAMAGUCHI1) DEV(*SAVF) SAVF(YAMAGUCHI1/TESTSAVF) CLEAR(*REPLACE)
```

### 保管したSAVFファイルの中身を確認する

`DSPSAVF`コマンドで保管したSAVFファイルの中身を確認することができます。
また、`Display Saved Objects`の画面から、Optionに5を指定することで、そのファイルの中身（メンバー）まで確認することが可能です。

```shell-session
dspsavf file(yamaguchi1/testsavf)
```

![as400.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/c75e7a49-8237-7f2c-c31e-9102a68b1c3e.gif)

### AS400上にあるSAVFファイルをFTPで取得する

まずはftpコマンドを利用して `ftp {AS400のサーバー}`を実行し、ログインIDとパスワードを入力します。
私の自己学習用の環境がMacなので`brew install tnftp`でFTPコマンドが利用できるように事前にセットアップしています。
Windows10の環境であれば、ftpコマンドは標準搭載されていたかと思いますので、コマンドプロンプトでそのまま実行できるはずです。

```shell-session:ftpコマンドで接続
$ ftp pub400.com

Connected to pub400.com.
220-QTCP at PUB400.COM.
220 Connection will close if idle more than 16 minutes.
Name (pub400.com:you_name_is_yu): YAMAGUCHI
331 Enter password.
Password: 
230 YAMAGUCHI logged on.
Remote system type is .
ftp> 
```

FTPには転送モードとしてアスキーモードとバイナリモードが存在します。
ざっくりと

- アスキーモード：テキストファイルを転送する時に使う（改行コードが自動的に修正されて転送される）
- バイナリモード：テキストファイル以外のファイルを転送する時に使う（ファイルがそのまま転送される）

みたいな感じです。
SAVFファイルは、テキストエディタなどで開いてみても、中身がなんなのかよくわからない化けた状態となっているファイルです。
そのため、アスキーモードではなくバイナリモードでデータ転送します。

```shell-session:バイナリモードに変更
ftp> bin
200 Representation type is binary IMAGE.
```

SAVFファイルが格納されているローカルPC側のディレクトリにカレントディレクトリを移動します。
`lcd`コマンドは、ローカルPC側のカレントディレクトリを移動するために利用するFTPコマンドを利用している場合のコマンドです。
l(エル)を付けない場合、FTPで接続している先（AS400）のディレクトリが移動してしまいます。

```shell-session:ローカル環境のカレントディレクトリを変更
ftp> lcd /Users/you_name_is_yu/Downloads
Local directory now: /Users/you_name_is_yu/Downloads

ftp> !pwd
/Users/you_name_is_yu/Downloads
```

転送先となるAS400側のライブラリにカレントディレクトリを移動します。

```shell-session:AS400側のライブラリにカレントディレクトリを移動
ftp> cd /QSYS.LIB/YAMAGUCHI1.LIB
250 "/QSYS.LIB/YAMAGUCHI1.LIB" is current library.

ftp> ls
229 Entering Extended Passive Mode (|||49527|).
125 List started.
......
-rwx------   1 YAMAGUCHI 0          24576 Jun 21 23:06 TESTSAVF.SAVF  <-- 事前に作成済みのSAVFファイル
250 List completed.
```

FTPのPUTコマンドを利用してSAVFファイルをAS400の指定したライブラリに転送します。

```shell-session:GETコマンドでファイルを取得
ftp> get TESTSAVF.SAVF
local: TESTSAVF.SAVF remote: TESTSAVF.SAVF
229 Entering Extended Passive Mode (|||36583|).
150 Retrieving member TESTSAVF in file TESTSAVF in library YAMAGUCHI1.
   247 KiB   18.04 KiB/s 
226 File transfer completed successfully.
253440 bytes received in 00:13 (18.04 KiB/s)
```

以上の手順で指定したオブジェクトをSAVFファイルに保管し、ローカルPCにダウンロードが完了です。
私の場合は開発者ではないので、どちらかというと開発会社から現行環境のオブジェクト提供依頼があった場合などに、エクスポートするような感じで利用することになろうかと思います。

## 受領したSAVFファイルをAS400に転送して展開する

開発会社に開発してもらったプログラムが、SAVFファイルになって納品されるケースを想定し、
その受領したファイルを自社環境のAS400に展開する方法についてまとめました。

SAVFファイルはFTPによって転送できますが、事前に転送先のライブラリ（ここではYAMAGUCHI1ライブラリ）内に、予め*FILE SAVFオブジェクトを空でもなんでもいいので作成しておく必要があります。
作成済みのSAVFファイルに対してFTPで上書き転送することで、正しく転送できます。
事前にSAVFファイルを作成しなかった場合、物理ファイル・オブジェクトとして転送されてしまい、SAVF関連のコマンドで操作できないファイルとなってしまいます。

### FTPでAS400にSAVFファイルを転送する

```shell-session:ftpコマンドで接続
$ ftp pub400.com

Connected to pub400.com.
220-QTCP at PUB400.COM.
220 Connection will close if idle more than 16 minutes.
Name (pub400.com:you_name_is_yu): YAMAGUCHI
331 Enter password.
Password: 
230 YAMAGUCHI logged on.
Remote system type is .
ftp> 
```

バイナリモードに変更する。

```shell-session:バイナリモードに変更
ftp> bin
200 Representation type is binary IMAGE.
```

```shell-session:ローカル環境のカレントディレクトリを変更
ftp> lcd /Users/you_name_is_yu/Downloads
Local directory now: /Users/you_name_is_yu/Downloads

ftp> !pwd
/Users/you_name_is_yu/Downloads
```

転送先となるAS400側のライブラリにカレントディレクトリを移動します。

```shell-session:AS400側のライブラリにカレントディレクトリを移動
ftp> cd /QSYS.LIB/YAMAGUCHI1.LIB
250 "/QSYS.LIB/YAMAGUCHI1.LIB" is current library.

ftp> ls
229 Entering Extended Passive Mode (|||49527|).
125 List started.
......
-rwx------   1 YAMAGUCHI 0          24576 Jun 21 23:06 TESTSAVF.SAVF  <-- 事前に作成済みのSAVFファイル
250 List completed.
```

FTPのPUTコマンドを利用してSAVFファイルをAS400の指定したライブラリに転送します。

```shell-session:PUTコマンドでAS400にSAVFファイルを転送
ftp> put TESTSAVF.SAVF TESTSAVF.SAVF
local: TESTSAVF.SAVF remote: TESTSAVF.SAVF
229 Entering Extended Passive Mode (|||17390|).
150 Sending file to member TESTSAVF in file TESTSAVF in library YAMAGUCHI1.
100% |******************************************************************************************|   247 KiB  100.79 KiB/s    00:00 ETA
226 File transfer completed successfully.
253440 bytes sent in 00:06 (38.98 KiB/s)
```

以上でSAVFファイルをAS400の指定したライブラリに転送が完了しました。

### SAVFファイルの中身を確認する

SAVFファイルの中身を確認するには`DSPSAVF`コマンドを使います。

```shell-session
dspsavf file(yamaguchi1/testsavf)
```

<img width="1088" alt="スクリーンショット 2020-06-24 7.19.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/028a6b5a-e0fb-a92b-75e7-e15c0f9408ad.png">

### SAVFファイルを指定したライブラリに展開（解凍）する

自己学習環境として利用しているPUB400.comでは、どうやらSAVOBJやSAVLIBで保管したSAVFファイルを、指定したライブラリへ展開する`RSTOBJ`や`RSTLIB`コマンドが権限制御されており、利用できません。
https://pub400.com/help.html
の`How can i load my data?`の部分にその記載があります。

代わりに、`RESTOBJ`や`RESTLIB`コマンドを用意しているみたいなので、そのコマンドを使った展開方法をここには記載します。
※本来のAS400標準には存在しないコマンドのようなので、あくまでもPUB400.com環境でのみ動作するとご理解ください。

```shell-session
RESTOBJ LIBNAM(YAMAGUCHI1) SAVFNAM(TESTSAVF) SAVFLIB(YAMAGUCHI1) 
```

実行すると以下のようなメッセージが表示されます。
<img width="1088" alt="スクリーンショット 2020-06-24 7.26.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/fc378778-1ab0-4104-d6c4-a7f5ed05c0fc.png">

しばらく待って、`DSPMSG`コマンドでメッセージ情報を再度確認してみると、次のようなメッセージが表示されます。
<img width="1088" alt="スクリーンショット 2020-06-24 7.28.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/9b20534d-11b3-9bb8-6325-fdc1c5d9748c.png">

ここにある以下の内容が重要です。

```
Your Savefile TESTSAVF  has been restored into temporary library TMP911787
  . Please copy the objects into one of your libraries and delete this    
  temporary library because of your storage is limited. Use command WRKLIB
  TMP* to see the library.                                                
```

TMP911787というライブラリに展開したオブジェクトが格納されているよ。
この中からオブジェクトを自分のライブラリにコピーして、一時ライブラリを削除してね。
なぜならあなたのストレージには制限があるから。ということです。

TMP911787の中身を確認してみます。
メッセージでヒントとしてコマンドを教えてくれているので、そのコマンドの通りに辿ってみます。

```shell-session
WRKLIB TMP*  
```

<img width="1088" alt="スクリーンショット 2020-06-24 7.39.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/74c6b946-33b4-d7ff-48b7-eab2f28121ff.png">

OPT=12を指定して`Work with Objects`の画面に遷移

<img width="1088" alt="スクリーンショット 2020-06-24 7.36.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/88fdc496-258f-da88-4d7e-5afdf862140d.png">

確かに展開されたオブジェクトが存在します。
コピーしたい場合は、OPTに3を設定して、To lbraryに自分のライブラリを指定すれば良いのかと思います。

<img width="1088" alt="スクリーンショット 2020-06-24 7.37.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/47f34d3d-f675-f23c-c47d-98d5851be0a4.png">

必要なオブジェクトのコピーが完了したら、` Work with Libraries`の画面でOPT=4を指定して削除すれば、PUB400.comからの指示通りの挙動ではないかと思います。

<img width="1088" alt="スクリーンショット 2020-06-24 7.41.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/df512afd-ba88-8061-2335-cf042fa91401.png">
<img width="1088" alt="スクリーンショット 2020-06-24 7.41.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246556/c2ae3723-32bb-ae37-0ccf-7681d882dba9.png">

PUB400.com専用の操作となり、通常のAS400運用とは異なる内容にはなってしまいましたが、
自己学習するうえで、本番環境を自由に（何かをやっちまって障害を起こしても何もきにしないで）いじくりまわせるAS400の環境は他には無いと思います。
このぐらいの制限は気にせず、これからも活用させていただきたいなと思います。