SageNB ワークシート (Sagemath ワークシート)から Jupyter notebook の ipynb ファイルへの一括変換のスクリプトを書いた件

Sagemath の `sage --notebook=export --list` というコマンドを使うと sagenb ワークシートのリストを出力することができます. また, `sage --notebook=export --ipynb=mycalc admin:1` というコマンドを使うと ポート `admin:1` の sagenb ワークシートを `mycalc.ipynb` というファイル名の ipynb ファイルに変換することができます. 両者を組み合わせるシェルスクリプト(sagenb→ipynb 一括変換スクリプト)を書きました. 本体は sagenb ワークシートのリストからポートとタイトルの情報を取り出してシェルコマンドに変換する awk スクリプトです.

このスクリプトには注意事項があります.

* awk の方言に依存するスクリプトになってしまいましたので, 使用環境が異なる場合は, スクリプトを書き換える必要があります.
* sagenb のアカウント名は ` ` (スペース) `|`, `-`, `:` を含まないものとします. `|` , `-`, `:` があるとタイトルが少しおかしくなります. アカウント名にスペースがあるとこのスクリプトでは変換ができません.
* ワークシートのタイトルを変換結果の ipynb ファイルのファイル名に埋め込む努力をしましたが, ascii の記号が多い場合は読みにくくなってしまいます.
* 変換結果の ipynb ファイルにワークシートのタイトルを markdown のタイトルとして埋め込む努力をしましたが, うまくいかない場合が残ります. TeX の部分で LaTeX に処理できないコードが含まれる場合などは変換結果の ipynb ファイルを jupyter が拒否する可能性があります. TeX のコードを人間が読む形で使う場合には LaTeX  の処理系が解釈できないコードも少なくありません. 対策として, ワークシートのタイトルを Sagemath のコード・セルの中に `# ` で始まる python のコメントとして埋め込むオプションをつけました.タイトルの埋め込みを断念するオプションも付けました.
* utf-8 対応の努力をしましたので, 欧文, 和文のタイトルも扱えると思います.
* 変換結果の ipynb ファイルを pandoc で LaTeX ソースに直接変換すると, markdown セルの内容が消えてしまいます. ipynb ファイルの中に html のタグが残ってしまっているためです.
    - pandoc で html ファイルを経由して 2段階の変換をすると markdown セルの内容を保つことができます.
    - jupyter から markdown 形式で download して, その markdown ファイルを pandoc で LaTeX ソースに変換すると markdown セルの内容を保つことができます.
* 変換結果の ipynb ファイルに markdown セルが多い場合は, markdown セルの中身を書き換える作業が html の編集のようになってしまい, markdown の利点が活かせません.
* sagenb ワークシートのタイトルに `*`, `_`, `[...](...)` などの文字列が含まれている場合, タイトルを変換結果の ipynb ファイルに挿入するオプションを使用すると,  `*`, `_`, `[...](...)`  などがそのまま markdown のタイトルに入ってしまいます. そのため, `Sagemath checklist like 2*(x+1)*(x-1) for mathematical expression 2(x+1)(x-1).` の掛け算の記号が表示から消えてしまうなどの症状があります. jupyter で開いて該当する markdown セルをダブルクリックすると, markdown ソースには `*` などが残っていますので, ipynb ファイルの中のタイトルをケースバイケースで修正して下さい.

## 実際に試した使用環境

* MacOS 10.14.6 Mojave

```Console
myhome$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
Copyright (C) 2007 Free Software Foundation, Inc.
myhome$ awk --version
awk version 20070501
```

* SageMath-8.9.app and/or SageMath-9.0.app

## sagenb→ipynb 一括変換スクリプトの背景

Sagemath の `sage --notebook=export --list` というコマンドを使うと sagenb ワークシートのリストが出てきます. 

```
myhome$ export PYTHONIOENCODING=utf-8
myhome$ sage --notebook=export --list
Unique ID       | Notebook Name
-------------------------------------------------------------------------------
admin:0         | Save 30% by Sagemath  (~/Manuals/SageTutorial9.0.pdf)
admin:1         | "Rabbit's population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
admin:2         | Integral #1: $f(x)=\frac{1}{\sqrt{1-x^2}}$, $|x| < 1$.
admin:3         | Integral #2: why is f(x) = x * sqrt(1-x^2), (x in [-1,1]) easier?
admin:4         | Oval body & `tail' of @rana-aerea
admin:5         | Calculus of log|x|, x > 0.
admin:6         | mycalc
admin:7         | mycalc
admin:8         | Math par «Calcul mathématique avec Sage»
admin:9         | Sagemath 数式処理ノート
admin:10        | More mathematics $\sqrt{5$
admin:11        | After Gauss [x](integer part) is a function
```
 
最初の行の環境変数は, python の内部の unicode 文字列を出力する際に utf-8 に変換するように指示するための設定です. ワークシートのタイトルに ascii コード外の文字がある場合, この設定がないと, 上記の sage コマンドがエラーを起こして止まってしまいます. 私自身, この点に引っかかってしまいました. `@FGtatsuro` さんの [Python2で文字列を処理する際の心掛け](https://qiita.com/FGtatsuro/items/cf178bc44ce7b068d233) を読んでなんとか解決したしだいです. python3 に移行したSageMath-9.0.app からは, この環境変数は関係なくなっているようです.

このコマンドで標準出力に出てくる情報は, 縦線の右側が Sagemath の Web インターフェイス `The Sage Notebook` にある タイトル(`Notebook Name`)で, 左側がポート(`Unique ID`)です. ポートはアカウントとポート番号の組で, コロンを境にして密接しています. 

`sage --notebook=export --ipynb=mycalc admin:7` というコマンドを使うと, ポート `admin:7` の sagenb ワークシートを `mycalc.ipynb` というファイル名の ipynb ファイルに変換することができます.

リストのエントリーをシェルの1行コマンドにする変換は

```
admin:7        | mycalc
```

を

```
/Applications/SageMath-8.9.app/sage --notebook=export --ipynb="mycalc.ipynb" admin:7
```

に変える処理でだいたいうまくいきます. (うまくいかない場合がぼろぼろ出てきてしまいました... 後で考察します.)

タイトルが全て ascii の \[:graph:\] (空白文字以外の印字文字)の文字列になっている場合には, この処理は awk のスクリプト

```Awk
BEGIN {
              Converter="/Applications/SageMath-8.9.app/sage" ;
              OptionHeader="--notebook=export --ipynb=" ;
              space=" " ;
              quote="\"" ;
            }
/:/        { print(Converter space OptionHeader quote $3 ".ipynb" quote space $1)}
```

でできます.  `admin` の sagenb ワークシートに限定して変換したい場合はパターンを指定して

```Awk
BEGIN {
              Converter="/Applications/SageMath-8.9.app/sage" ;
              OptionHeader="--notebook=export --ipynb=" ;
              space=" " ;
              quote="\"" ;
            }
/admin:/        { print(Converter space OptionHeader quote $3 ".ipynb" quote space $1)}
```

というスクリプトを使えばいいわけです.

ここまでの処理は, sagenb ワークシートのリストを表計算ソフトに入れて, 列をコピペで移動する処理+αでも可能です.

実際にはタイトルの中にスペースが入っていたりしますから, 対策が必要になります.

## sagenb→ipynb 一括変換スクリプトの問題意識

`Notebook Name` は一見すると同じフォルダーに並んでいるファイルのファイル名のように見えてしまいますが, 実は sagenb ワークシートのタイトルです. ipynbf ファイルのファイル名を `Notebook Name` から生成するに当たってこの微妙な差異が問題点になります.

1. 複数の sagenb ワークシートの間でタイトルの重複が可能です.
2. 欧文のタイトルでは空白文字が入るのが当然です.
3. 科学技術関係ではタイトル内の特殊記号は普通の現象です.
4. タイトルでは日本語も普通です. 最近は日本語のファイル名も普通になりましたが, awk の日本語への対応は不完全ですので工夫がいります.
5. ipynb ファイルに変換した後は, unix ファイルとして, また, `Finder` からアクセスするファイルとして使用することに加えて, Windows 用のハードディスクや USB にコピーしたり, linux のパワーユーザーにファイルを送って修正してもらったりというシーンがあると思います. 標準の OS とシェルの下でトラブルを起こさないファイル名でないと困ります.
6. 今の所 ipynb ファイルの使用にはコマンドラインからのアクセスが必要な状況です (SageTeX で使う場合, 直接コマンドラインから使ったり, pandoc などのツールをバッチ処理で使用することになります.) ところが, 科学技術計算関係ではタイトルに参考文献を引用符で囲って入れたり, 所有のアポストロフィが付いている用語が入っていたりすることも普通です.
7. 変換結果の中にタイトルを入れる場合, jupyter, markdown 処理系, pandoc, latex の処理を止めない工夫が必要になります.


sagenb ワークシートに空白文字や特殊文字が入っている状況について簡便な調査の結果を書いておきます. `Worksheet of Strange Name /:¥\$|Sage` というタイトルで   sagenb ワークシートを保存してから Sagemath を起動し直して,  sagenb ワークシートを読み込む実験をしたところ, 入力したタイトルが再現されていました. `worksheet_conf.pickle` というファイルに

```
VWorksheet of Strange Name /:<A5>\u005c$|Sage
```

という行がありましたので, sagenb ワークシートのタイトルは特殊文字へ対応するコンセプトのようです.

デフォールトの設定では, sagenb ワークシートは

```Console
myhome$ ls ~/.sage/sage_notebook.sagenb/home
__store__	admin		guest		pub
```

の子フォルダー(サブフォルダー)にあります.  この中の `__store__` は sagenb ワークシートのフォルダー・ツリーの本体で,  `admin`, `guest`, `pub` は `__store__` の中のフォルダーへのシンボリック・リンクです. 例えば, `admin` を覗くと, `history.pickle` というファイルと `0`, `1`, ... という名前の子フォルダーがあります. この子フォルダーが sagenb ワークシートです. 本体は子フォルダーの直下の `worksheet.html` というファイルですが, タイトルは同じ子フォルダーの `worksheet_conf.pickle` という ascii 可読ファイルの中に書いてあります. グラフなどは孫フォルダーに格納するようになっています. `worksheet_conf.pickle` は unicode に対応していて, タイトルに日本語やスラッシュを使えるようになっています.

しかし, `/Applications/SageMath-8.9.app/sage --notebook=export --ipynb="FILENAME.ipynb" admin:NN` の `FILENAME` の部分ではスラッシュなどの特殊文字がトラブルを起こしてしまいます.

## sagenb→ipynb 一括変換スクリプト作成の概略

1. Sagemath の `sage --notebook=export --list` というコマンドの出力を awk スクリプトで加工して, `sage --notebook=export --output=FILENAME.ipynb` とう形の変換コマンドと, 変換結果にタイトルを挿入する `ed` コマンドを実行するシェルコマンドからなるバッチファイルを作ります. このバッチファイルを `/bin/bash` で実行します. 
2. ipynb ファイルのファイル名の `FILENAME` の部分は, `アカウント-ポート番号-タイトルの無害化版` という書式として, タイトルの無害化はタイトル内の文字のうち `a-zA-Z0-9._-` 以外の ascii コードをアンダースコア `_` に変換する処理とします.
3. `ed` コマンドは awk スクリプトが生成します. `ed` コマンドのファイル名は `ed-FILENAME.ed` という形にして, ipynb ファイルと関連が判るようにします.
4. awk の方言の対策のため, スクリプトはユーザーサイトで書き換えてデバッグして使えるようにします.
5. タイトルの中の TeX の部分が LaTeX で処理できない形の場合に対する対策のオプションを用意します.
5. ファイル名やタイトル名の処理がどうしてもうまくいかない時の対策として, 変換結果のファイル名を ポートからくる文字列で  `admin-0.ipynb`, `admin-1.ipynb`, ...としてしまうオプションを用意します.
6. ファイル名を 255バイトに収める必要がある場合については, awk スクリプトの出力をユーザーが好みのテキストエディターで編集して対応する方式とします.

## sagenb→ipynb 一括変換スクリプト変更やデバッグでの注意事項

本稿の一括変換スクリプトは MacOS 10.14.6 Mojave に付属の `awk version 20070501` と `GNU bash, version 3.2.57(1)-release` と MacOS アプリ版の `SageMath-8.9.app` を用いています.

一括変換スクリプトで悩まされた事項がいくつかあるのですが, `awk` の方言やバグに関連する問題のようです. `awk version 20070501` は行を選択するパターンと行を加工する `sub` や `gsub` 関数で使うパターンの文法が異なっていて, `sub` や `gsub` 関数で使うパターンで縦線やバックスラッシュを捉える方法が `man 7 re_format` のページの正規表現に記載していない方法になっています. バグに依存する方法だという疑いがありますので, `awk` の他のバージョンを使っているサイトや, 他の方言を使っているサイトでは awk スクリプトのソースに手を加える必要があると思います.

バックスラッシュについては `@515hikaru` さんの　[sub, gsub でバックスラッシュ(\\)に置換するとき](https://qiita.com/515hikaru/items/d8000456824136bc415c) と `corbie` さんの [corbieのブログ](http://blog.livedoor.jp/corbie/archives/1939160.html) に置換後の文字列の書き方の話があります. 私が知りたいところは丁度抜けているのですが, 情報から推測して試行錯誤した結果, 表面的な解決はなんとかなりました.

awk からのファイルへの書き込みについては, SOUM/misc の `naka` さんの [awkガナス 第6回 複数ファイルへの出力(リダイレクト機能) 2015.09.02](https://www.soum.co.jp/misc/awk/6/) を参照してください.

シングルクォートの使い方がコメントアウトしてあります. このテクニックについては, `@sumomo_99` さんの [bashでシングルクォートをエスケープする方法](https://qiita.com/sumomo_99/items/5fb9c054c542456a6cdc) を参照して下さい.

ソースに手を加えることが前提ですので, オプションもソースの中の定数の変更で対応する形としました.

使用上は,  awk スクリプトをシェルスクリプトに埋め込んだソースをサーチパス上に配置して使う方法がよいはずなのですが, ユーザーサイトでのデバッグが前提となる状況では, 逆に awk スクリプトを外部ファイルにしておいた方が扱いやすくなります. そのため,  awk スクリプト埋め込み版と, awk スクリプト外付け版の両者を掲載します.

## sagenb→ipynb 一括変換スクリプトの使い方

### 新規フォルダーを作成して使用.
まず, sagenb ワークシートの一括変換でできる ipynb ファイルを入れるためのフォルダーを新規作成します. 以下ではこれを変換先フォルダーということにします. 

### スクリプトの配置
一括変換スクリプトは  2セットありますが, ユーザー側でソースを書き換えてデバッグしながら使うことになりますので,  `sagenb2ipynbasf.sh` と `sagenb2ipynbasf.awk` という組み合わせの方を使うことになると思います. このスクリプトは両方をカレント・ディレクトリーに置いて使うことを想定しています. この組み合わせの方を awk スクリプト外付け版ということにします.

もう片方のセットは `sagenb2ipynb.sh` というスクリプト単体です. こちらは, パスが通っているところに置いて使ったり, 相対パスや絶対パスでファイル名を指定して使うことができます. awk-スクリプトの非常にテクニカルな事項に慣れている人はこちらを使うことも原則可能だと思います.  `sagenb2ipynb.sh` は  `sagenb2ipynbasf.sh` の中に `sagenb2ipynbasf.awk` を埋め込んだスクリプトです. パラメーターの設定の仕方などは,  awk スクリプト外付け版と同じすので, 使用法の説明は awk スクリプト埋め込み版の説明を流用して下さい.

### シェルで変換先フォルダーに移動

Termimanl.app  などの `/bin/bash` の中で,  `cd` を使うなどして, 変換先フォルダーへ移動して下さい. スクリプトの実行はこのシェルから行って下さい. デバッグの過程で `/bin/bash` 特有の構文を使うことになる可能性があります.

### 起動中の SageMath アプリや sage コマンドを終了

sagenb ワークシートを ipynb ファイルに変換するファイル処理には, `sage` コマンドを使います. 起動中の SageMath アプリや sage コマンドと干渉する可能性がありますので, 念の為, 起動中の SageMath アプリや sage コマンドを終了させておいて下さい.

### スクリプトのオプション設定

設定するオプションは `sagenb2ipynbasf.sh` の中の変数 `ConverterHandler`と `sagenb2ipynbasf.awk` の中の変数 `debug`, `titleclass` と `fileclass` です.

`sagenb2ipynbasf.sh` の中の

```Console
# Debug option for /bin/bash in the third stage of the cascaded pipes.
# ConverterHandler="/bin/cat"              # Debugging mode prdducing ed-files only.
ConverterHandler="/bin/bash -v"    # Debugging mode producing ipynb-files,
# ConverterHandler="/bin/bash"     # Normal  mode producing ipynb-files.
```

という部分でシェルの変数 `ConverterHandler` を選ぶことができます. 最初は動作テストが必要ですので, `ConverterHandler="/bin/cat"` の行を活性化して下さい.

`sagenb2ipynbasf.awk` の中には

```Awk
  debug=1 ;  # Set 1 for debugging mode or 0 otherwise.
```

という行と

```Awk
      # Cell of  title in ipynb file.
      # -- 4 for markdown section; 3 for code section; 0 -- 2 for none;
  titleclass=4;
      # Type of filename -- 1 for port and title; 0 for port only.  
```

という行に加えて 

```Console
      # Type of filename -- 1 for port and title; 0 for port only.  
  fileclass=0;
```

という行があります.

MacOS でバージョン情報が

```Console
myhome$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
Copyright (C) 2007 Free Software Foundation, Inc.
myhome$ awk --version
awk version 20070501
```

と一致する場合は  `debug=0`, `titleclass=4`, `fileclass=1` か  `debug=0`, `titleclass=3`, `fileclass=1` で  `./sagenb2ipynbasf.sh` を実行して下さい. うまくいかなかった場合は次に進んで下さい.

Linux など MacOS 以外の unix 場合, MacOS でバージョン情報が上記のものと異なる場合, そして, `./sagenb2ipynbasf.sh` の実行がうまくいかなかった場合は動作を確認しながらスクリプトを加工して使うことになります.

念の為

```Console
myhone$ export PYTHONIOENCODING=utf-8
```

を実行して, `python2` に `utf-8` で出力させるようにしておきます.

まず,  `debug=0`, `titleclass=1`, `fileclass=1` で動作テストを行って下さい.

* `debug=1` は awk の方言依存の動作を調べるためのオプションです. 
* `debug=0` はシェルが実行しないデータの出力を抑制します.

後者は皮肉なことですが,   awk スクリプトが出力するコマンドを読みやすくするために使うことができます. 

* `titleclass=4` は sagenb ワークシートのタイトルを ipynb ファイルの先頭に markdown セルとして挿入するオプションです. 
* `titleclass=3` はsagenb ワークシートのタイトルを ipynb ファイルの先頭に Sagemath のコード・セル内の python コメントとして挿入するオプションです.
* `titleclass=2` , `titleclass=1`と `titleclass=0` はsagenb ワークシートのタイトルを ipynb ファイルに埋め込まないオプションです.
    * `titleclass=2`  は `ed` コマンドの実行を抑制します.
    * `titleclass=1`  は `ed` コマンドの書き出しも抑制します.
    * `titleclass=0` はシェルのコメントにタイトルを入れる処理まで抑制します.
* sagenb ワークシートの中のタイトルに markdown に変換しにくい部分がある場合は `titleclass= 2` を使って, タイトルの文字列を `ed` のコマンドのスクリプトの中に保存しておくということが可能です. 

* `fileclass=1` はタイトルの情報を変換結果のファイル名に反映させる努力をします. `fileclass=0` は `admin-7.ipynb` のような形でポートの情報のみのファイル名を使用します.

2回目からは `debug=1` として awk スクリプトの動作を調べて下さい. その次は  `debug=1` のまま `titleclass` の値を 1ずつ大きくしていって動作を調べて下さい. なんとか動くようになったら, 出力をファイルに保存して, `/Applications/SageMath...` で始まる行と次の行 (`if` で始まっているはずです) のペアをコピペ して `/bin/bash` 1行ずつ実行してみて下さい. (このペアの1行前には, `ConverterHandler="/bin/bash" ` 用に進捗情報を表示するコマンドがあります.)

実行結果の ipynb ファイルは, Terminal.app などで変換先フォルダーにいる状態で

```Console
SageMath-8.9.app/sage --notebook=jupyter
```

などのコマンドを実行すると jupyter の Web インターフェイスから簡単に開くことができます. ファイルがおかしい場合は, `titleclass` の値を 1ずつ減らしてスクリプトの出力を採集し, 変換結果の ipynb ファイルを削除するかリネームするかしながら変換をやり直してみて下さい.  

どうしてもうまくいかない場合は, `titleclass=0`, `fileclass=0` を試して下さい.

意図する変換ができた場合は, sage コマンドを終了させて, 変換結果の ipynb ファイルを削除するかリネームした上で, `sagenb2ipynbasf.sh` の中の `ConverterHandler="/bin/bash -v"` か `ConverterHandler="/bin/bash"` を活性化して `./sagenb2ipynbasf.sh` を実行して下さい.

###  ipynb ファイルの点検とタイトルの修正

カレント・ディレクトリーを変換先フォルダーにした状態で,

```Console
SageMath-8.9.app/sage --notebook=jupyter
```

を実行して, Sagemath を起動します. ipynb ファイルを順に検査して下さい.

タイトルについては, markdown の強調に使うことのある  `*` や `_` という文字やリンクの指定の `[...](...)` についての処理をしていませんので, タイトルが意図した通りになっているかどうか点検して下さい.

`2*(x+1)*(x-1)` が 2_(x+1)_(x-1) と表示されるなどの問題は見逃しやすいので, ゆっくり丁寧に調べて下さい. 書き換えの参考に小さな表を作りました.

Text | Display
---|---
`2*(x+1)*(x-1)` | 2_(x+1)_(x-1) 
`2 *  (x+1) * (x-1)` | 2 *  (x+1) * (x-1) 
`(10 **2)** 3` | (10 **2)** 3 
`(10 \*\*2)\*\* 3` | (10 \*\*2)\*\*3 
`(10 ** 2) ** 3` | (10 ** 2) ** 3 
`F_m + F_n` | F_m + F_n
`F _m + F_ n` | F _m + F_ n
`F\_m + F\_n` | F\_m + F\_n
`[x](integer part)` | [x](integer part)
`[x](integer_part)` | [x](integer_part)

2020年03月07日追記:
アカウントの書式は `[a-zA-Z0-9_@.]+` で Sagemath がこの書式のみ受け付けるようになっています. Konstantin Podshumok さん  [sagenb/sagenb/data/sage/html/accounts/registration.html] (https://github.com/sagemath/sagenb/blob/master/sagenb/data/sage/html/accounts/registration.html) のプログラムに `'Your username must start with a letter and be between 3 and 64 characters long. You may only use letters, numbers, underscores, @, '` と書いてあります. また, `/Applications/SageMath-8.9.app/sage` で `notebooke(accounts=True)` を実行しして sagenb の Web インターフェイスを開いてから, サイン・アウトしてからサインインすると `Sign up for a Sage Notebook account` というページがでてきます. ここで特殊記号入りのアカウントでサインアップを試みると確かに拒否されます. このページにも上記のメッセージがあります.

 
## スクリプトファイル

本文ではスクリプトのファイル名に日付が入っていませんが, バージョン管理の都合上, スクリプトのファイル名に日付を入れます.

2020年03月07日追記:
* ファイル名に日付を入れてテストし直した結果, 外付け版のシェルスクリプトの中の awk スクリプトのファイル名のバグが見つかりましたので, 訂正しました.
* アカウントが `[a-zA-Z0-9_@.]+`だということに対応しました.
* awk スクリプトのオプション `debug`, `titleclass`, `fileclass` の値を出力するようにしました.

awk- スクリプトはファイルに格納して使うことができます.

```Awk:sagenb2ipynbasf20200307.awk
BEGIN {
      # filename: sagenb2ipynbasf.awk
      # author: rana-aerea
      # date: March 07 Saturday 2020
      # Reference: https://qiita.com/rana-aerea/items/e820004a538a72be504d
      # Tested on MacOS Mojave with "awk version 20070501".
      #
      # Awk-script file for converting sagenb files into ipynb files.
      # This awk-script is a filter to transforming
      #    the list of ports and titles of sagenb worksheets produced by
      # myhome$ /SageMath-X.X/sage --notebook=export --list
      #    to shell-commands for converting sagenb worksheets.
      # Usage:
      # myhome$ awk -f sagenb2ipynbasf.awk port-title-list.txt
      # Assuming port-title-list.txt contains
      #    the list of ports and titles of sagenb worksheets.
      # The shell script "sagenb2ipynbasf.sh" uses this awk-script.
      # The shell script "sagenb2ipynb.sh" has this awk-script embedded.
      #
      # Caution: Do not put single quotes in side this script
      # or use the technique described below the variable named quote.
      #
  debug=1 ;  # Set 1 for debugging mode or 0 otherwise.
      # Cell of  title in ipynb file.
      # -- 4 for markdown section; 3 for code section; 0 -- 2 for none;
  titleclass=4;
      # Type of filename -- 1 for port and title; 0 for port only.  
  fileclass=1;
      #
      # Names for characters hard to write.
  space=" " ;
      # If someone modifies this awk-script to allow some special character
      #    in filenames,
      #    enclosing filenames by double quotes might help.
  quote="\"" ;
      # If single quote  is really necessary, activate the following.
      #  quote="\'\''" ; # delete this line if this awk-script is hard to debug.
      # -- double quote for awk, backslash itself, terminating single quote,
      # -- escaped single quote, opening single quote, double quote for awk
      #
      # Command for converting sagenb worksheet to ipynb file.
  Converter="/Applications/SageMath-8.9.app/sage" ;
  OptionHeader="--notebook=export --ipynb=" ;
      #
      # Shell command for inserting title to ipynb file.
  titlewriter="if [ $? == 0 ]; then  ed -s   %s  < %s ; fi\n"
      # format of insertion command of ed for ipynb file:
      # ```ed
      # 3i
      #   {
      #    "cell_type": "markdown",
      #    "metadata": {},
      #    "source": [
      #     "# <title>"
      #    ]
      #   },
      # .
      # wq
      # ```
      # Here, the <title>  is to be replaced with the title of the sagenb worksheet.
      # Alternative format saves title as python comment in code cell.
  if (titleclass>=4) {  
    titlesaver1="3i\n {\n   \"cell_type\": \"markdown\",\n" ;
    titlesaver2="   \"metadata\": {},\n"
    titlesaver3="   \"source\": [\n    \"# %s\"\n   ]\n  },\n.\nwq\n" ;
  } else if (titleclass>=2) {
    titlesaver1="3i\n {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n" ;
    titlesaver2="   \"metadata\": {},\n    \"outputs\": [],\n" ;
    titlesaver3="   \"source\": [\n    \"# %s\"\n   ]\n  },\n.\nwq\n" ;
  } else { # if everything failed, activate this branch for titleclass==2.
    titlesaver1="!";      # shell escape
    titlesaver2=" # %s";  # to shell comment
    titlesaver3="q\n";    # leave the ipynb file and quit.
  }
  titlesaver=titlesaver1 titlesaver2 titlesaver3;
  print("#!/bin/bash");
  print("");
  print("# debug=" debug ",  titleclass=" titleclass ", fileclass=" fileclass);
 }
/:/ {
      # Change the pattern from /:/ to /^admin:/
      #     for restricting the account of conversion to admin.
      # The list of the account for sagenb files is found by
      # myhome$ ls  ~/.sage/sage_notebook.sagenb/home
      #     as folder names excepting "__store__"
      #         if SageMath is in the default settings.
      # Alternatively, one can read out accounts from the output of
      # myhome$ /Applications/SageMath-X.X.app/sage --notebook=export --list
      #
      # Description of how this awk-script works.
      #    * Skip the first line "Unique ID       | Notebook Name".
      #    * Skip also the second line consisting of many "-".
      #    * Catch every line, which looks lik the examples below.
  if(debug) print("#input : " $0) ;         # Now, $0 looks like below.
      #input : admin:1         | "Rabbit`s population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
      #input : admin:7         |  mycalc
      #       (The easiest and hardest samples among title in ascii codes.)
      #    *  Take the port.
  port =$1 ;
  if(debug) print("#port  : " port) ;         # Now, $0 looks like below.
      #input : admin:1
      #input : admin:7
  if(titleclass>0 || fileclass>0) {
      #    * Focus on the frist "|".
      #    * Remove space between the port and this  "|".
      #    * Remove one space after  this "|".
      #    * Remove this "|".
      #    * Awk of macOS Mojave support "cat|dog" meaning "cat" or "dog".
      #    * In patterns for sub and gsub functions "[|]" means "|" itself
      #    * Since [[:punct:]] and [[:cntrl:]] behaves strangely for non-ascii letter,
      #       [[:blanck:]] might also behave strangely for some non-ascii letters.
      #    * we try some alternative to
      #sub("[[:blank:]]*[|][[:blank:]]", "-", $0) ;
      #    * Replacing the last [[:blank:]] with " " is alright.
    sub("[ \t\r\n]*[|] ", "-", $0) ;
    if(debug) print("#input : " $0) ;         # Now, $0 looks like below.
      #input : admin:1-"Rabbit`s population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
      #input : admin:7-mycalc
  }
      #    * Take the title.
      #          - Unfortuntately, title might oftain contain " "
      #          - This means the title cannot be recovered from $3.
      #          - So, we take the title out of the input line, which is $0.
  if (titleclass > 0) {
    titlestring =$0 ;
      #    * The title in mark down document should fit in one line.
      #          - Web Interface "The Sage Notebook" refuses new line in title.
      #          - Akw does not catch string containing new line.
      #          - For just in case, we change possible line breaks with space.
    gsub("[\n\r]", space, titlestring) ;
    if(debug) print("#title : " titlestring) ;  # Now, titlestring looks like below.
      #title : admin:1-"Rabbit`s population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
      #title : admin:7-mycalc
      #    * Remove the port from titlestring.
    sub("^[^-]*-", "", titlestring) ;
    if(debug) print("#title : " titlestring) ;  # Now, titlestring looks like below.
      #title : "Rabbit`s population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
      #title : mycalc
      # For markdown cell of ipynb file,
      #     double quotes and backslashes need be escaped.
    gsub("\\\\", "\\\\", titlestring) ;
    if(debug) print("#title : " titlestring) ;  # Now, titlestring looks like below.
      #title : "Rabbit`s population" $F_n$ is like $((1+\\sqrt{5})/2)^n$!
      #title : "mycalc
    gsub("\"", "\\\"", titlestring) ;
    if(debug) print("#title : " titlestring) ;  # Now, titlestring looks like below.
  }
      #title : \"Rabbit`s population\" $F_n$ is like $((1+\\sqrt{5})/2)^n$!
      #title : "mycalc
      #    * Make filename.
      #         - Unfortuntately, the titles often contain special characters.
      #             0.  Save 30% by Sagemath  (~/Manuals/SageTutorial9.0.pdf)
      #             1.  "Rabbit`s population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
      #             2.  Integral #1: $f(x)=\frac{1}{\sqrt{1-x^2}}$, $|x| < 1$.
      #             3.  Integral #2: why is f(x) = x * sqrt(1-x^2), (x in [-1,1]) easier?
      #             4.  Oval body & `tail` of @rana-aerea
      #             5.  Calculus of log|x|, x > 0.
      #             6.  mycalc
      #             7.  mycalc
      #             8.  Math par «Calcul mathématique avec Sage»
      #             9.  Sagemath 数式処理ノート
      #             10.  More mathematics $\sqrt{5$
      #             11. After Gauss [x](integer part) is a function
      #         - The rabit in "The Sage Notebook" is followed by a single quote
      #             and tail in "The Sage Notebook"  is followed by an apostrophe,
      #             which is identical to a single quote (Number 1).
      #             They are changed for embedding in script.
      #             Do not change them to the correct quote in this awk-script.
      #         - In the second example, a closing single quote is common.
      #             It is changed for embedding in script.
      #             Do not change it to the correct quote in this awk-script.
      #         - Number 6 and Number 7 are for testing duplicate titles.
      #         - Number 8 is in French and Number 9 is in Japanes Language.
      #         - Number 10 is for testing title with incorrect TeX code.
      #         - The command for listing ports and titles is
      #             * SageMath-8.9.app/sage --notebook=export --list
      #             * For handling non-ascii charactes of utf-8, we need to set
      #                       export PYTHONIOENCODING=utf-8
      #             * if the shell is /bin/bash. Consult man page for other shells.
      #             * Caution: Sagemath (python2) from interactive shell
      #                       pretends good.
      #    * Restrictions of filenames on linux, macOS, Windows imply
      #         - Ascii charcters other than [[:alnum:]_-.] should be avoided.
      #         - We cannot write all forbidden ascii characters
      #             because we cannot put a single quote in this script.
      #    * Our workaround is here:
      #         - express the set of special characters
      #             * by using octal numbers.
      #             * and replace them with "_".
      #    * After this replacement, filename still falls in forbidden formats:
      #             * lead by a period;
      #             * lead by a "-";
      #             * terminated by a period.
      #    * One more serious problem waits solution.
      #         - Different Sagenb Worksheets with the same title is allowed!
      #    * Our workaround is here:
      #         - Retain the port in the form "admin-7-".
      #             * Someone might have accounts student1, student2, ...
      #                   so that we should put a separater after the account.
      #         - Append the suffix ".ipynb",
      #             solving the previous three problems at the same time.
  if(fileclass<=0) {
    filename=port ;
    if(debug) print("#file  : " filename) ;   # Now, filename  looks like below.
      #file  : admin:1
      #file  : admin:7
    sub(":", "-", filename) ;
    if(debug) print("#file  : " filename) ;   # Now, filename  looks like below.
      #file  : admin-1
      #file  : admin-7
  } else {
    filename=$0
    if(debug) print("#file  : " filename) ;   # Now, filename  looks like below.
      #file  : admin:1-"Rabbit`s population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
      #file  : admin:7-mycalc
    sub(":", "-", filename) ;
    if(debug) print("#file  : " filename) ;   # Now, filename  looks like below.
      #file  : admin-1-"Rabbit`s population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
      #file  : admin-7-mycalc
      #    * Caution: [[:punct:]] and [[:cntrl:]] match some byte of Kanji.
    gsub("[\001-\054\057\072-\077\133-\140\173-\177]", "_", filename) ;
    if(debug) print("#file  : " filename) ; # Now, filename  looks like below.
      #file  : admin-1-_Rabbit_s_population___F_n__is_like____1__sqrt_5___2__n__
      #file  : admin-7-mycalc
  }
      #    *    convert the line into several commands,
      #             for example convert one of the  examples into
      # /Applications/SageMath-8.9.app/sage --notebook=export
      #     --ipynb="admin-7-mycalc.ipynb" admin:7
      # if [ $? == 0 ]; then  ed -s   admin-7-mycalc.ipynb  < ed-admin-7-mycalc.ipynb.ed ; fi
  edfilename="ed-" filename ".ed" ;
  if( titleclass>0) {
    print("#" space port space "|" space titlestring) ;
    print("echo port" space port space "file" space filename) ;
  }
  if (titleclass>1) printf(titlesaver, titlestring) > edfilename ;
  print(Converter space OptionHeader quote filename ".ipynb" quote space quote port quote ) ;
  if (titleclass>2) printf(titlewriter,  filename ".ipynb", edfilename) ;
 }
```

次は, この awk-スクリプトを活用するための制御スクリプトです. ipynb ファイルを展開する変換先フォルダーに awk-スクリプトとこの制御スクリプトをコピーして, そこに `cd` してから実行します.

```Console:sagenb2ipynbasf20200307.sh
#!/bin/bash
# filename: sagenb2ipynbasf.sh
# author: rana-aerea
# date: March 07 Saturday 2020
# Reference: https://qiita.com/rana-aerea/items/e820004a538a72be504d
# Tested on MacOS Mojave with "awk version 20070501".
#
# This shell-script uses the awk-script "sagenb2ipynbasf.awk"
#    for converting Sagemath's sagenb files to ipynb files.
#
Lister=/Applications/SageMath-8.9.app/sage
Options="--notebook=export --list"
# It needs this envrionment variable for correct output to pipe,
#     when `sage` runs by python2 (upto SageMath-8.9.)
export PYTHONIOENCODING=utf-8
# Debug option for /bin/bash in the third stage of the cascaded pipes.
# ConverterHandler="/bin/cat"        # Debugging mode prdducing ed-files only.
ConverterHandler="/bin/bash -v"    # Debugging mode producing ipynb-files,
# ConverterHandler="/bin/bash"       # Normal  mode producing ipynb-files.
#
# Description of how this shell-script works.
# The first stage of the cascaded pipes generates the list of ports and titles.
# The second stage of the cascaded pipes uses "sagenb2ipynbasf.awk"
#    for converting every pair of a port and a title in a series of shell-commands.
# The last stage of the cascaded pipes execute the output of the second state.
#
# Below, $Options serves as a pair of option strings.
#     while "$Options" would constitute one option string.
# Here, we mean two option strings by $Options.
# Do not ecnlose $Options by quotes.
#
$Lister $Options | awk -f sagenb2ipynbasf20200307.awk | $ConverterHandler
```

ipynb ファイルの展開する変換先フォルダーの外にスクリプトをおいて使うことができるようにするために, awk-スクリプトを 制御スクリプトの中に埋め込みました. それが次のスクリプトです.

```Console:sagenb2ipynb20200307.sh
#!/bin/bash
# filename: sagenb2ipynb.sh
# author: rana-aerea
# date: March 07 Saturday 2020
# Reference: https://qiita.com/rana-aerea/items/e820004a538a72be504d
# Tested on MacOS Mojave with "awk version 20070501".
#
# This shell-script converts Sagemath's sagenb files to ipynb files.
#
Lister=/Applications/SageMath-8.9.app/sage
Options="--notebook=export --list"
# It needs this envrionment variable for correct output to pipe,
#     when `sage` runs by python2 (upto SageMath-8.9.)
export PYTHONIOENCODING=utf-8
# Debug option for /bin/bash in the third stage of the cascaded pipes.
# ConverterHandler="/bin/cat"        # Debugging mode prdducing ed-files only.
ConverterHandler="/bin/bash -v"    # Debugging mode producing ipynb-files,
# ConverterHandler="/bin/bash"       # Normal  mode producing ipynb-files.
#
# Be careful not to terminate the script for awk by single quote or apostrophe.
AWKSCRIPT='BEGIN {
      # filename: sagenb2ipynbasf.awk
      # author: rana-aerea
      # date: March 07 Saturday 2020
      # Reference: https://qiita.com/rana-aerea/items/e820004a538a72be504d
      # Tested on MacOS Mojave with "awk version 20070501".
      #
      # Awk-script file for converting sagenb files into ipynb files.
      # This awk-script is a filter to transforming
      #    the list of ports and titles of sagenb worksheets produced by
      # myhome$ /SageMath-X.X/sage --notebook=export --list
      #    to shell-commands for converting sagenb worksheets.
      # Usage:
      # myhome$ awk -f sagenb2ipynbasf.awk port-title-list.txt
      # Assuming port-title-list.txt contains
      #    the list of ports and titles of sagenb worksheets.
      # The shell script "sagenb2ipynbasf.sh" uses this awk-script.
      # The shell script "sagenb2ipynb.sh" has this awk-script embedded.
      #
      # Caution: Do not put single quotes in side this script
      # or use the technique described below the variable named quote.
      #
  debug=1 ;  # Set 1 for debugging mode or 0 otherwise.
      # Cell of  title in ipynb file.
      # -- 4 for markdown section; 3 for code section; 0 -- 2 for none;
  titleclass=4;
      # Type of filename -- 1 for port and title; 0 for port only.  
  fileclass=1;
      #
      # Names for characters hard to write.
  space=" " ;
      # If someone modifies this awk-script to allow some special character
      #    in filenames,
      #    enclosing filenames by double quotes might help.
  quote="\"" ;
      # If single quote  is really necessary, activate the following.
      #  quote="\'\''" ; # delete this line if this awk-script is hard to debug.
      # -- double quote for awk, backslash itself, terminating single quote,
      # -- escaped single quote, opening single quote, double quote for awk
      #
      # Command for converting sagenb worksheet to ipynb file.
  Converter="/Applications/SageMath-8.9.app/sage" ;
  OptionHeader="--notebook=export --ipynb=" ;
      #
      # Shell command for inserting title to ipynb file.
  titlewriter="if [ $? == 0 ]; then  ed -s   %s  < %s ; fi\n"
      # format of insertion command of ed for ipynb file:
      # ```ed
      # 3i
      #   {
      #    "cell_type": "markdown",
      #    "metadata": {},
      #    "source": [
      #     "# <title>"
      #    ]
      #   },
      # .
      # wq
      # ```
      # Here, the <title>  is to be replaced with the title of the sagenb worksheet.
      # Alternative format saves title as python comment in code cell.
  if (titleclass>=4) {  
    titlesaver1="3i\n {\n   \"cell_type\": \"markdown\",\n" ;
    titlesaver2="   \"metadata\": {},\n"
    titlesaver3="   \"source\": [\n    \"# %s\"\n   ]\n  },\n.\nwq\n" ;
  } else if (titleclass>=2) {
    titlesaver1="3i\n {\n   \"cell_type\": \"code\",\n   \"execution_count\": null,\n" ;
    titlesaver2="   \"metadata\": {},\n    \"outputs\": [],\n" ;
    titlesaver3="   \"source\": [\n    \"# %s\"\n   ]\n  },\n.\nwq\n" ;
  } else { # if everything failed, activate this branch for titleclass==2.
    titlesaver1="!";      # shell escape
    titlesaver2=" # %s";  # to shell comment
    titlesaver3="q\n";    # leave the ipynb file and quit.
  }
  titlesaver=titlesaver1 titlesaver2 titlesaver3;
  print("#!/bin/bash");
  print("");
  print("# debug=" debug ",  titleclass=" titleclass ", fileclass=" fileclass);
 }
/:/ {
      # Change the pattern from /:/ to /^admin:/
      #     for restricting the account of conversion to admin.
      # The list of the account for sagenb files is found by
      # myhome$ ls  ~/.sage/sage_notebook.sagenb/home
      #     as folder names excepting "__store__"
      #         if SageMath is in the default settings.
      # Alternatively, one can read out accounts from the output of
      # myhome$ /Applications/SageMath-X.X.app/sage --notebook=export --list
      #
      # Description of how this awk-script works.
      #    * Skip the first line "Unique ID       | Notebook Name".
      #    * Skip also the second line consisting of many "-".
      #    * Catch every line, which looks lik the examples below.
  if(debug) print("#input : " $0) ;         # Now, $0 looks like below.
      #input : admin:1         | "Rabbit`s population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
      #input : admin:7         |  mycalc
      #       (The easiest and hardest samples among title in ascii codes.)
      #    *  Take the port.
  port =$1 ;
  if(debug) print("#port  : " port) ;         # Now, $0 looks like below.
      #input : admin:1
      #input : admin:7
  if(titleclass>0 || fileclass>0) {
      #    * Focus on the frist "|".
      #    * Remove space between the port and this  "|".
      #    * Remove one space after  this "|".
      #    * Remove this "|".
      #    * Awk of macOS Mojave support "cat|dog" meaning "cat" or "dog".
      #    * In patterns for sub and gsub functions "[|]" means "|" itself
      #    * Since [[:punct:]] and [[:cntrl:]] behaves strangely for non-ascii letter,
      #       [[:blanck:]] might also behave strangely for some non-ascii letters.
      #    * we try some alternative to
      #sub("[[:blank:]]*[|][[:blank:]]", "-", $0) ;
      #    * Replacing the last [[:blank:]] with " " is alright.
    sub("[ \t\r\n]*[|] ", "-", $0) ;
    if(debug) print("#input : " $0) ;         # Now, $0 looks like below.
      #input : admin:1-"Rabbit`s population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
      #input : admin:7-mycalc
  }
      #    * Take the title.
      #          - Unfortuntately, title might oftain contain " "
      #          - This means the title cannot be recovered from $3.
      #          - So, we take the title out of the input line, which is $0.
  if (titleclass > 0) {
    titlestring =$0 ;
      #    * The title in mark down document should fit in one line.
      #          - Web Interface "The Sage Notebook" refuses new line in title.
      #          - Akw does not catch string containing new line.
      #          - For just in case, we change possible line breaks with space.
    gsub("[\n\r]", space, titlestring) ;
    if(debug) print("#title : " titlestring) ;  # Now, titlestring looks like below.
      #title : admin:1-"Rabbit`s population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
      #title : admin:7-mycalc
      #    * Remove the port from titlestring.
    sub("^[^-]*-", "", titlestring) ;
    if(debug) print("#title : " titlestring) ;  # Now, titlestring looks like below.
      #title : "Rabbit`s population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
      #title : mycalc
      # For markdown cell of ipynb file,
      #     double quotes and backslashes need be escaped.
    gsub("\\\\", "\\\\", titlestring) ;
    if(debug) print("#title : " titlestring) ;  # Now, titlestring looks like below.
      #title : "Rabbit`s population" $F_n$ is like $((1+\\sqrt{5})/2)^n$!
      #title : "mycalc
    gsub("\"", "\\\"", titlestring) ;
    if(debug) print("#title : " titlestring) ;  # Now, titlestring looks like below.
  }
      #title : \"Rabbit`s population\" $F_n$ is like $((1+\\sqrt{5})/2)^n$!
      #title : "mycalc
      #    * Make filename.
      #         - Unfortuntately, the titles often contain special characters.
      #             0.  Save 30% by Sagemath  (~/Manuals/SageTutorial9.0.pdf)
      #             1.  "Rabbit`s population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
      #             2.  Integral #1: $f(x)=\frac{1}{\sqrt{1-x^2}}$, $|x| < 1$.
      #             3.  Integral #2: why is f(x) = x * sqrt(1-x^2), (x in [-1,1]) easier?
      #             4.  Oval body & `tail` of @rana-aerea
      #             5.  Calculus of log|x|, x > 0.
      #             6.  mycalc
      #             7.  mycalc
      #             8.  Math par «Calcul mathématique avec Sage»
      #             9.  Sagemath 数式処理ノート
      #             10.  More mathematics $\sqrt{5$
      #             11. After Gauss [x](integer part) is a function
      #         - The rabit in "The Sage Notebook" is followed by a single quote
      #             and tail in "The Sage Notebook"  is followed by an apostrophe,
      #             which is identical to a single quote (Number 1).
      #             They are changed for embedding in script.
      #             Do not change them to the correct quote in this awk-script.
      #         - In the second example, a closing single quote is common.
      #             It is changed for embedding in script.
      #             Do not change it to the correct quote in this awk-script.
      #         - Number 6 and Number 7 are for testing duplicate titles.
      #         - Number 8 is in French and Number 9 is in Japanes Language.
      #         - Number 10 is for testing title with incorrect TeX code.
      #         - The command for listing ports and titles is
      #             * SageMath-8.9.app/sage --notebook=export --list
      #             * For handling non-ascii charactes of utf-8, we need to set
      #                       export PYTHONIOENCODING=utf-8
      #             * if the shell is /bin/bash. Consult man page for other shells.
      #             * Caution: Sagemath (python2) from interactive shell
      #                       pretends good.
      #    * Restrictions of filenames on linux, macOS, Windows imply
      #         - Ascii charcters other than [[:alnum:]_-.] should be avoided.
      #         - We cannot write all forbidden ascii characters
      #             because we cannot put a single quote in this script.
      #    * Our workaround is here:
      #         - express the set of special characters
      #             * by using octal numbers.
      #             * and replace them with "_".
      #    * After this replacement, filename still falls in forbidden formats:
      #             * lead by a period;
      #             * lead by a "-";
      #             * terminated by a period.
      #    * One more serious problem waits solution.
      #         - Different Sagenb Worksheets with the same title is allowed!
      #    * Our workaround is here:
      #         - Retain the port in the form "admin-7-".
      #             * Someone might have accounts student1, student2, ...
      #                   so that we should put a separater after the account.
      #         - Append the suffix ".ipynb",
      #             solving the previous three problems at the same time.
  if(fileclass<=0) {
    filename=port ;
    if(debug) print("#file  : " filename) ;   # Now, filename  looks like below.
      #file  : admin:1
      #file  : admin:7
    sub(":", "-", filename) ;
    if(debug) print("#file  : " filename) ;   # Now, filename  looks like below.
      #file  : admin-1
      #file  : admin-7
  } else {
    filename=$0
    if(debug) print("#file  : " filename) ;   # Now, filename  looks like below.
      #file  : admin:1-"Rabbit`s population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
      #file  : admin:7-mycalc
    sub(":", "-", filename) ;
    if(debug) print("#file  : " filename) ;   # Now, filename  looks like below.
      #file  : admin-1-"Rabbit`s population" $F_n$ is like $((1+\sqrt{5})/2)^n$!
      #file  : admin-7-mycalc
      #    * Caution: [[:punct:]] and [[:cntrl:]] match some byte of Kanji.
    gsub("[\001-\054\057\072-\077\133-\140\173-\177]", "_", filename) ;
    if(debug) print("#file  : " filename) ; # Now, filename  looks like below.
      #file  : admin-1-_Rabbit_s_population___F_n__is_like____1__sqrt_5___2__n__
      #file  : admin-7-mycalc
  }
      #    *    convert the line into several commands,
      #             for example convert one of the  examples into
      # /Applications/SageMath-8.9.app/sage --notebook=export
      #     --ipynb="admin-7-mycalc.ipynb" admin:7
      # if [ $? == 0 ]; then  ed -s   admin-7-mycalc.ipynb  < ed-admin-7-mycalc.ipynb.ed ; fi
  edfilename="ed-" filename ".ed" ;
  if( titleclass>0) {
    print("#" space port space "|" space titlestring) ;
    print("echo port" space port space "file" space filename) ;
  }
  if (titleclass>1) printf(titlesaver, titlestring) > edfilename ;
  print(Converter space OptionHeader quote filename ".ipynb" quote space quote port quote ) ;
  if (titleclass>2) printf(titlewriter,  filename ".ipynb", edfilename) ;
 }
'
# Description of how this shell-script works.
# The first stage of the cascaded pipes generates the list of ports and titles.
# The second stage of the cascaded pipes uses awk command with the script   "$AWKSCRIPT"
#    for converting every pair of a port and a title in a series of shell-commands.
# The last stage of the cascaded pipes execute the output of the second state.
#
# Below, $Options serves as a pair of option strings.
#     while "$Options" would constitute one option string.
# Here, we mean two option strings by $Options.
# Do not ecnlose $Options by quotes.
#
# On the other hand, "$AWKSCRIPT" really means our awk-script is a single string.
# Do not forget the enclosing quotes
#      otherwise awk misunderstand `{` after BEGIN as a file to parse.
$Lister $Options | awk "$AWKSCRIPT" | $ConverterHandler
```