→ もくじ
bashlib1 - shorthand library for bash - → bash をコーディングするときの注意点 bash シェルをスクリプトとして使うときに、必要となる機能を提供するライブラリです。 事前の状況チェックや、事後のエラーチェックが不要になり、コードがシンプルになります。短いオプションや特殊記号を減らして、読めるだけでなく検索できるようになります。 エラーが発生したら停止して、その場所を表示します。(フェイル・セーフ) 許可したフォルダーの外のファイルを消そうとしたらエラーにします。(フール・プルーフ) 構造化例外処理ができるため、エラーのテストやエラー復帰ができます。 ステップ実行ができるため、不具合を早く修正できます。 #!/bin/bash -eE function  Main_func() {   echo "Hello, world!"   Pause_func } #// bashlib include sample.sh bash シェルの画面 $ ./sample.shHello, world! 続行するには Enter キーを押してください... ダウンロード → bashlib-1.0.0.tar.bz2 for bash ver3以上 (Linux, MacOS X) ライセンス → BSDライセンス - Wikipedia (Web) bashlib is provided under 3-clause BSD license.Copyright (C) 2011 Sofrware Design Gallery "Sage Plaisir 21" All Rights Reserved. 参考 無料でお使いいただけますが、無保証です。再頒布や加工もファイルに書かれた下記を削除しなければ自由にできます。 bashlib を参考にして作成したスクリプトやライブラリの著作権は、あなたにあります。 ソフトウェアデザイン館 Sage Plaisir 21 http://www.sage-p.com/ サポート先 2011/10/9 $ bashlib がベースとするシェルスクリプトの説明です bashlib のマニュアル(本書)です bashlib ヘルプ 画面で見るマニュアル シェルスクリプト(bash) インストール → インストール → アンインストール とりあえず動かしてみたいときは、 version 1 bashlib include → シェル・スクリプトを読むには Main_func Pause_func bashlib の menu を開いてください。 この文書(HTML5)を見るときは、Chrome または Mac OS X の Safari をお使いください。 UNIX & Linux ファイルやシェルの基本的なコマンドの説明です。 この文書は Snap Note を使って作られています。 上記リンク先は、 Internet Explorer 版の文書 でのみ参照できます。

もくじ

bashlib1
はじめに (scriptlib フォルダー)
シェル・スクリプトを読むには
bash を使ってコーディングするときの注意
メニュー、ショートハンド・プロンプト (menu, setup)
ユーザーインターフェース
ファイル操作
プロセス制御、関数呼び出し
テスト支援
文字列
配列
連想配列、オブジェクト
bashlib_inc.sh (bashlib のベース・システム)
エラー処理
困ったときは? (デバッグ)

はじめに (scriptlib フォルダー) bashlib を使うと、ファイル操作やプログラムの起動などを行う、スクリプトの作成(プログラミング)が簡単になり、いろいろなことが自動化できます。 そして、作成したスクリプトを、 スクリプトの発動が素早くできるようになります。 ショートハンド・プロンプト の形式にすれば、 複雑な処理を自動化 シェル・スクリプト パソコン 解凍(コピー)するだけで、 scriptlib インストール bashlib で作られたスクリプトのインストールは、スクリプト・ファイルと一緒に scriptlib フォルダーをコピーするだけです。 このとき、bashlib パッケージをインストールしたり、パスを通したりする必要はありません。 bashlib パッケージのインストールは、ダウンロードした圧縮ファイルを解凍 アンインストール bashlib の圧縮ファイルを解凍してできたフォルダーを削除してください。scriptlib フォルダーを別の場所にコピーしていたら、それも削除してください。 するだけです(下記を参照)。とりあえず動かしてみたいときは、インストール bashlib の menu を開いてください。 Mac OS X (Snow Leopard) から圧縮ファイルを解凍する Ubuntu (lucid) のデスクトップから圧縮ファイルを解凍する シェルから圧縮ファイルを解凍する → Mac OS X のテキストエディットを使って編集するには cd <bashlib フォルダーを格納するフォルダー>tar xvf bashlib-1.0.0.tar.bz2 ダウンロードした bashlib-1.0.0.tar.bz2 圧縮ファイルをダブルクリックすると、解凍されて、bashlib フォルダーができます。 参考 ダウンロードした bashlib-1.0.0.tar.bz2 圧縮ファイルを右クリックして、[ ここに展開する ] を選ぶと、bashlib フォルダーができます。 関連 後、 開発方針 ・100ページの説明をするより、1つでも典型的でシンプルな動くサンプルを提供すること・結果が同じであれば、エラーにしないこと。 例:ファイルが無いパスを指定して削除したとき ・文書の大項目はユースケースであること。 レアケースである細かい仕様を主役にしないこと ・文書はユーザーが行う操作の順に書くこと。 「ただし、~してあること」を無くすこと ・ユーザーから見ると意味が異なる、ボトムアップから定義された用語は使わないこと ・メインのコードの前提条件と、何を行っているのかについて、コメントを母国語で書くこと ・エラーが発生して復帰するまでは、次のエラーが発生しても、最初のエラーを表示すること ・シンボルの大文字と小文字のコーディング・ルールは Java に標準化すること。 ・ユーザーの体験が、シンプル&ビジュアルであり、代償によって自由を奪わないこと。 シェル・スクリプトを読むには 参考 → bash の特殊記号一覧 bashlib が提供する関数の末尾は、_func または _method に統一されています。これにより、関数であるかコマンド(実行ファイル)であるかを識別できます。 関数の内容を知りたいときは、bashlib のドキュメントを検索するか、scriptlib フォルダー の中を検索してください。 なお、 一部、デバッグ専用の関数は、その内容を理解している本人しか見ないコードであることと、キーボードをタイプする量を減らすために、_func を付けていません。 関数のヘルプやソースコードを見る、またはコマンドのヘルプを見る bash の特殊記号の意味を知る Main_func 関数から読む。bashlib の起動順序を知る bashlib を使ったシェル・スクリプト・ファイルを実行すると、その最後にある bashlib のインクルード を実行すると、 Main_func 関数 が呼ばれます。 Main_func 関数 が呼ばれたら、bashlib が提供する関数を使うことができます。 参考 → bash における関数の記述方法 → デバッグ 動かしてみて、デバッグ用関数を使って、状況を知る 参考 他の人からシェル・スクリプトを入手したときに、その内容をソースコードから理解するときのコツを紹介します。 末尾が _method の関数の場合、*Class.*_method というオブジェクト指向を意識した関数名になります。 ピリオドが含まれていますが、bash では特別な文法的意味はなく、 英文字と同じく関数名の一部です。 _func や _method が付いていない実行ファイルの仕様は、which や man を使って 調べてください。 からファイルの検索が menu の SearchFile コマンド できます。 スクリプトが bashlib を使っていれば、デバッグ用関数を使って内部の状況を知ることができます bash を使ってコーディングするときの注意 bashlib は、エラーが発生すると即時に中断(フェールセーフ)するため、終了ステータスを毎回チェックする必要がありません。 しかし、一部のケースで、エラーを無視して続きを実行してしまうことがあります。 (これは、終了ステータスを毎回チェックするコーディングに戻しても発生します。) この問題を避けるため、いくつかのコーディングの注意点があります。 ループの終わりの done_func $? bashlib は、エラーが発生したとき、trap した関数の中から break ジャンプをしています。(関数名が見つからないなど、終了ステータスが 0 のままエラーが発生する場合に も構造化例外処理に対応できるようにするため。) 正常時の break と区別するため、ループの終わりの done の直後に done_func を呼び 出すようにしてください。 そうしないと、エラーが発生しても、続きを実行してしまい ます。 while true; do CheckWritable_func "$PWD/out/a.txt" break done ; done_func $? done_func $? → done_func 参考 呼び出し(echo 出力の取得)は、必ず変数に代入する local var=`FuncA_func` local var ; var=`FuncA_func` local 変数の宣言と関数を使った代入を同時に行うと、右辺からエラーが発生しても、続きを実行してしまいます。 宣言と代入を別の文に分けてください。 (これは、代入文が、local コマンドの引数になっているためと思われます。) var ; var パイプの終了ステータスをチェックする パイプを使うと、パイプの左(最も右以外)で終了ステータスが 0 以外でも(エラーが発生しても)、続きを実行してしまいます。 これを避けるため、パイプを使った文の 直後に、CheckPipeStatus_func を呼び出す必要があります。 make V=1 2>&1 | tee make.log CheckPipeStatus_func "${PIPESTATUS[@]}" CheckPipeStatus_func "${PIPESTATUS[@]}" → CheckPipeStatus_func 参考 if [ "`gcc -dumpversion`" != "4.4.3" ];then 条件文の中から、関数呼び出しを行うと、関数内からエラーが発生しても、続きを実行してしまいます。 一時的な変数に代入してから、条件文に渡してください。 (これは、条件文の前の [ が test コマンドの別名であるため、条件文の中は test コマ ンドの引数になっているためと思われます。) 関数やコマンド(A_func とする)の引数の中に、関数(B_func とする)の呼び出しを記述すると、B_func の中で発生したエラーを無視して、続きを実行してしまいます。 B_func の echo 出力を、一時的な変数に代入してから、引数に渡してください。 A_func "`B_func`" local a1a1=`B_func` A_func "$a1" 終了ステータスの取得は、|| を使う bashlib を使った場合、終了ステータスが 0 以外になると(エラーが発生すると)、フェールセーフのため、続きを実行しません。 まれに、エラーが発生しても、 続きのコードから終了ステータスの値を使った処理をしたいことがありますが、 そのときは、|| を使います。 local status=0 command || status=$? local status=0 ls --unknown_option > /dev/null 2>&1 || status=$? echo "$status" サンプル → TryStart_func, TryEnd1_func, TryEnd2_func 関連 NG OK → echo による返り値 参考 OK NG OK NG local a1a1=`B_func` if [ "$a1" != "4.4.3" ];then a1=`B_func` a1=`B_func` local 変数の初期値に呼び出しがあるときは、宣言と代入を別の文に分ける 条件文の中から呼び出しをしない … command からエラーが発生しても続きを実行する 構造化例外処理 → 呼び出し(echo 出力の取得)は、必ず変数に代入する → local 変数の初期値に呼び出しがあるときは、宣言と代入を別の文に分ける → 条件文の中から呼び出しをしない → ループの終わりの done_func $? → パイプの終了ステータスをチェックする → 終了ステータスの取得は、|| を使う Mac OS X のテキストエディットを使って編集する Mac OS X のテキストエディットを使って、シェルスクリプト(テキストファイル)を開こうとしたときに、「日本語には対応していません」というエラーが出るときは、 文字コードが自動判定になっているために、UTF-8 の文字コードを認識できない可能性が あります。 [ テキストエディット | 環境設定 | 開く/保存 ] の [ ファイルを開くとき ] [ ファイルを 保存するとき ] を UTF-8 に設定してください。 メニュー、ショートハンド・プロンプト (menu, setup) メニューを開くと、ショートハンド・プロンプト(CUI)が表示され、そこにコマンドを入力すると、用意されたスクリプトを素早く起動することができます。 ドキュメントにコマンド名が書かれて いれば、専門家でなくてもコンピューターに処理させたい作業を実行することができるように なります。 → InputCommand_func ショートハンド・プロンプトを起動する関数(開発者向け) $ ./menu------------------------------------------------- 1. 作業Aを行う [CommandA] 2. 作業Bを行う [CommandB] 3. 作業Cを行う [CommandC] 99. 終了 番号またはコマンド >2 ------------------------------------------------- ログ・ファイルのパス >~/log.txt ~/log.txt を出力しました。 → bashlib_menu bashlib が提供するツールのメニュー → メニューを開くには ~/log.txt 2 上記の黄色い部分が、ユーザーが入力する部分です。プロンプト(InputCommand_func)には、シェルのコマンドも入力できます。 → ショートハンド・プロンプトの活用 ./menu メニューを開くには メニューを開くには、menu シェル・スクリプト・ファイルを実行します。場合によっては、setup など別のファイル名になっている可能性もあります。 ただ、必ずしもシェルから起動する必要はありません。 → メニューから選び、対話的に入力する つづき → シェルから専用コマンドを実行する 関連 シェルからメニューを開く cd bashlib./menu Mac OS X (Snow Leopard) からメニュー(シェル・スクリプト)を開く Mac OS X で bash シェルを開くには、Finder [ 移動 | アプリケーション | ユーティリティ| ターミナル ] を Dock にドラッグ&ドロップして、できたアイコンをクリックします。 Ubuntu (lucid) のデスクトップからメニュー(シェル・スクリプト)を開く Ubuntu でシェルを開くには、[ アプリケーション | アクセサリ | 端末 ] を選びます。 Ubuntu (lucid) のデスクトップからシェル(端末)を開く Mac OS X (Snow Leopard) からシェル(端末)を開く menu ファイル(シェル・スクリプト・ファイル)をダブルクリックして、「端末内で実行する」を選んでください。 menu ファイル(シェル・スクリプト・ファイル)をダブルクリックしてください。 → Ubuntu のデスクトップからシェル(端末)を開く (下記) → Mac OS X からシェル(端末)を開く (下記) → InputCommand_func 参考 入力できる専用コマンドは、多くの場合、アプリケーションのマニュアルに書かれています。自分がしたいことをマニュアルの目次から見つけたら、その本文から専用コマンドを探して ください。 画面で見るマニュアルを見ているのなら、専用コマンドをコピー&ペーストできます。 ペーストするときは、右クリックして [ 貼り付け ] をクリックしてください。 よく使うコマンドは、付箋紙や Snap Note のノート・バーなどに一時的に貼り付けておくと 便利に使えます。 テンポラリ・フォルダーを開く OpenTemp 画面で見るマニュアル vbslib を使ったスクリプトを実行すると、その実行のために内部的に必要になるファイルが、テンポラリ・フォルダーに作られることがあります。 menu ここを選択して、右クリックして [ コピー ] をクリック、ショートハンド・プロンプトに貼り付け。 ショートハンド・プロンプトに入ります 1. 作業Aを行う [CommandA]2. 作業Bを行う [CommandB] 3. 作業Cを行う [CommandC] 99. 終了 番号またはコマンド >2 ------------------------------------------------- ログ・ファイルのパス >~/log.txt ~/log.txt を出力しました。 ~/log.txt 2 OS によっては、ファイルをプロンプトにドラッグ&ドロップすることで、パスを入力できます。 上記の黄色い部分が、ユーザーが入力する部分です。 コマンド名は、大文字と小文字を区別しません。 つまり、コマンド名の大文字の文字が小文字 になっていたとしても起動できます。 コマンドを入力した後、パラメーターやファイルへのパスの入力を求められることがあります。 使い勝手の悪い専門的なプログラムは、それを支援するスクリプトを作成して、アプリケーション・プロンプトの形にして、画面で見るマニュアル を用意すると、使いやすくなります。 画面で見るマニュアルから、専用コマンドをコピー&ペーストしたり、ショートカットから 起動することで、次々と魔術(スクリプト)を繰り出す速記原典(ショートハンド)のように、 次々と処理を発動させることができるようになります。 使いやすさは、CUI や GUI に関わらず、電子マニュアルの構成によって、大きく左右され ます。 検索可能なユースケース・ベースのハイパーテキストでできたマニュアルを、Snap Note などで作成するとよいでしょう。 画面で見るマニュアル とのコラボレーション 既存のプログラムを使いやすくする テンポラリ・フォルダー ショートハンド・プロンプトの活用 マニュアルを作成するときは、ユーザーがしたいこと(ユースケース)ごとの章と、コマンドごとの章(索引)の両方を用意するとよいでしょう。 → メニューを開くには 参考 メニューから選び、対話的に入力する メニューに表示されていないコマンドを起動することもできます。 → メニューから選び、対話的に入力する → 画面で見るマニュアル とのコラボレーション → シェルからコマンドを実行する → 既存のプログラムを使いやすくする シェルからメニューを表示しないでコマンドを実行する alias menu='$HOME/bashlib/menu' 起動するコマンドを短縮するには、たとえば、下記のようにエイリアスを作ってください。 ~/bashlib/menu SearchFile ~/source "*.txt" "" menu を起動するコマンドラインに、入力する内容を並べると、メニューを表示しないで、すぐにコマンドを実行します。 * を含む入力をするときは、" " で囲んでください。何も入力しない項目には、"" を指定してください。 bashlib の menu bashlib が提供するサンプル・コマンドを起動するメニューです。 bashlib menu - shorthand prompt1. ヘルプ(SVG形式)を開く (Google Chrome や Snap Note で見えます) [Help] 2. ファイルを検索する [SearchFile] 3. 新規作成する - bashlib が使えるシェル・スクリプト・ファイル [NewSh] 4. 新規作成する - ショートハンド・プロンプトのメニュー [NewMenu] 5. 圧縮ファイルを解凍する。ファイル名を一覧する [Extract] 6. 拡張子から実行属性を設定/解除する [chmod_x] 7. bashlib の自動テストを実行する [Test] 99. 終了 が表示されます。 ショートハンド・プロンプト → メニューを開くには → ショートハンド・プロンプトの活用 メニューに表示されていないコマンドを起動することもできます。 メニューを開くと、入力を要求する サンプル・コマンドのヘルプ → SearchFile コマンド → NewSh コマンド → Extract コマンド → chmod_x コマンド → NewMenu コマンド SearchFile NewMenu NewSh Extract chmod_x SearchFile コマンド [ 親: bashlib の menu ] 番号またはコマンド >SearchFile ((( SearchFile_sth_func ))) Enter のみ: /home/user1 検索するフォルダーのパス >~/bashlib Enter のみ: すべてのファイル ファイル名のフィルター 例 *.txt >*.txt Enter のみ: 内容を検索しない キーワード > ------------------------------------------------------------------------------- Search in /home/user1/bashlib/*.txt /home/user1/bashlib/README.txt /home/user1/bashlib/はじめて使うときは.txt ~/bashlib *.txt SearchFile ファイル名、またはファイルの内容を指定して、ファイルを検索します。 内部で、Linux の find コマンドと grep コマンドを使っています。 テスト → T_Menu.sh # [T_SearchFile_func] NewSh コマンド [ 親: bashlib の menu ] bashlib が使えるシェル・スクリプト・ファイルを新規作成します。 テスト → T_Menu.sh # [T_NewSh_func] → T_UI_Manually.sh # [T_NewShFolder_func] 番号またはコマンド >NewSh ((( NewSh_sth_func ))) Enterのみ:カレントの a.sh 新しいファイルのパス > /home/user1/a.sh を作成しました。 /home/user1/scriptlib フォルダーを作成しました。 NewSh scriptlib フォルダー が参照できる場所でなければ、scriptlib フォルダーも作成して すぐに実行できる状況にします。 NewMenu コマンド が起動するシェル・スクリプト・ファイルを テスト 番号またはコマンド >NewMenu ((( NewMenu_sth_func ))) Enterのみ:カレントの a.sh 新しいファイルのパス > /home/user1/a.sh を作成しました。 /home/user1/scriptlib フォルダーを作成しました。 NewMenu scriptlib フォルダー が参照できる場所でなければ、scriptlib フォルダーも作成して すぐに実行できる状況にします。 [ 親: bashlib の menu ] メニュー(ショートハンド・プロンプト) 新規作成します。 → T_UI_Manually.sh # [T_NewMenuFolder_func] Extract コマンド [ 親: bashlib の menu ] 番号またはコマンド >Extract ((( Extract_sth_func ))) 圧縮ファイルのパス >~/sample.tar.bz2 Enterのみ: ファイル名を一覧する [Ctrl]+[C] : 中断 解凍先フォルダーのパス >~/sample 圧縮ファイル(*.tar.bz2, *.tar.gz, *.zip) の中のファイルを一覧して展開します。 テスト → T_Menu.sh # [T_Extract_sth_func] 関連 → Extract_func Extract ~/sample.tar.bz2 ~/sample chmod_x コマンド [ 親: bashlib の menu ] 番号またはコマンド >chmod_x ((( chmod_x_sth_func ))) Enterのみ:/home/user1/bashlib/test 処理を行うフォルダーのパス > Enterのみ:*.sh 実行属性を設定するファイル("."=設定しない, CSV形式)> Enterのみ:*.txt, *.html, *.svg 実行属性を解除するファイル("."=解除しない, CSV形式)> 複数のファイルの実行属性をまとめて設定または解除します。 chmod_x テスト → T_Menu.sh # [T_chmod_x_func] 関連 → chmod_x_func ユーザーインターフェース EchoOn_func EchoOff_func Pause_func Input_func 実行するコマンドを表示しながら実行します。 EchoOn_func で表示するモードを、元に戻します。 シェルにメッセージを表示して、Enter キーを押すまで待ちます。 シェルにメッセージを表示して、ユーザーに入力を求めます。 echo_line_func echo_e_func ColorText_func SetAutoInputFromMainArg_func InputPath_func InputCommand_func 水平線を表示します。 エスケープがあるテキストを表示します。 echo -e と同じです。 テキストに色を付ける。 キーボードを自動入力します。 ショートハンド・プロンプトを開きます。 ユーザーにファイルやフォルダーのパスの入力を求めます。 EchoOn_func EchoOn_func 実行するコマンドを表示しながら実行します。 呼び出した関数の中のコマンドは表示しません。 EchoOn_func str="ABC" str="${str}DEF" EchoOff_func サンプル 関連 → EchoOff_func → T_Err_Manually.sh # [T_EchoStep_func] テスト EchoOff_func EchoOff_func EchoOn_func で表示するモードを、元に戻します。 関連 → EchoOn_func → T_Err_Manually.sh # [T_EchoStep_func] テスト echo_line_func echo_line_func 水平線を表示します。 テスト → InputCommand_func echo_e_func echo_e_func <Text> エスケープ(\)があるテキストを表示します。 echo -e と同じです。 【引数】 Text エスケープがあるテキスト Linux の echo -e と同じです。Mac OS X Snow Leopard には、 echo -e が使えませんが echo $'...' が使えます。 echo $'...' は、どちらの OS でも使えますが、Text に変数の参照を含めると eval を使った複雑な記述が必要になってしまうので、echo_e_func を用意しました。 ColorText_func を使って色がついたテキストを表示するときに echo_e_func を使います。 テスト → T_UI_Manually.sh # [T_ColorText_func] \n は改行に、\t はタブ文字になります。 ColorText_func ColorText_func "Pass." "Green" "Bold" echo_e_func "$g_Ret" "Red""Green" "Yellow" "Blue" "Magenta" "Cyan" "White" "RedBack" "GreenBack" "YellowBack" "BlueBack" "MagentaBack" "CyanBack" "WhiteBack" "Bold" + + 文字の色 背景の色 太字 サンプル ColorText_func <Text> <Color> ... ; <EscapeText>="$g_Ret" テキストに色を付けます。 【引数】 Text 色を付けるテキスト Color ... 色の名前、背景の色、太字かどうかの羅列 テスト → T_UI_Manually.sh # [T_ColorText_func] ColorText_func "ERROR!" "Red" "Bold" echo_e_func "$g_Ret" echo_e_func 標準エラー出力にリダイレクトして出力すると、色は付きません。 を付けて、g_Ret を表示すると、色が付きます。 g_Ret (出力)echo_e_func に渡す色付き文字列 Pass. ERROR! Pause_func Pause_func [--time_out=<Sec>] シェルにメッセージを表示して、Enter キーを押すまで待ちます。 続行するには Enter キーを押してください . . . メッセージの内容: 【引数】 Sec タイムアウトするまでの秒数。 省略時=タイムアウトなし サンプル Pause_func → T_UI_Manually.sh # [T_Pause_func] テスト サンプル Pause_func --time_out=10 Input_func Input_func <Prompt> ; <Key>="$g_Ret" シェルにメッセージを表示して、ユーザーに入力を求めます。 番号を入力してください > 画面の内容: Input_func "番号を入力してください >" ; num="$g_Ret" サンプル 【引数】 Prompt 表示する内容 g_Ret (出力) ユーザが入力した文字列、入力なし="" → SetAutoInputFromMainArg_func 関連 キーボードの自動入力 入力を求められている間に、Ctrl+C を押すと、プログラムは中断します。エラーにもなりません。 構造化例外処理も行われません。 サンプル Input_func "続けますか[Y/N] >" ; key1="$g_Ret" if [ "$key1" != "y" -a "$key1" != "Y" ];then echo "中断しました。" ; exit 1 ;fi Yes か No かの入力 テスト → InputCommand_func SetAutoInputFromMainArg_func SetAutoInputFromMainArg_func シェル・スクリプトを起動した時のパラメーターを、キーボードの自動入力に使うようにします。 SetAutoInputFromMainArg_funcInput_func "番号を入力してください >" ; num="$g_Ret" echo "num=$num" サンプル $ ./sample.sh 2num=2 画面の内容: テスト → T_Menu.sh # [T_InputCommandOpt_func] InputPath_func InputPath_func <Prompt> [ <Option> ... ] ; <Path>="$g_Ret" シェルにメッセージを表示して、ユーザーにファイルやフォルダーのパスの入力を求めます。 【引数】 Prompt 表示する内容 Option オプション(下記) (出力) ユーザが入力したパスの絶対パス、入力なし="" g_Ret テスト → T_UI_Manually.sh # [T_InputPath_func] 相対パスを入力したとき、基準は、 --AllowEnterOnly --ChkFileExists --ChkFolderExists Option 引数 Enter のみの入力を許可する 入力したパスにファイルが無ければ、再度入力する 入力したパスにフォルダーが無ければ、再度入力する サンプル InputPath_func "ファイルのパス >" --ChkFileExists ; path="$g_Ret" g_StartInPath 変数 の値になります。 --ChkNotExists 入力したパスに何かあれば、再度入力する InputCommand_func InputCommand_func <self> <Prompt> <Option> <AppKey> ショートハンド・プロンプトを開いて、コマンドに対応する関数を呼び出します。 【引数】 self InputCommandOpt クラスのオブジェクトの名前 Option Main_func の第1引数 プロンプトのガイド文字列 Prompt if [ "${BASH_VERSINFO[0]}" -ge "4" ];then declare_AssociativeArrayClass="declare -A" else declare_AssociativeArrayClass="declare" fi $declare_AssociativeArrayClass g_InputCommandOpt function Main_func() { local AppKey="$2" local obj="g_InputCommandOpt" #//=== call InputCommand_func SetAttr_func $obj Class "InputCommandOpt" SetAttr_func $obj Lead "bashlib menu - shorthand prompt" SetAttr_as_AssociativeArrayName_func $obj CommandReplace \ "1" "Help" \ "2" "SearchFile" \ "3" "NewSh" \ "5" "Extract" \ "7" "Test" \ \ "Help" "Help_sth_func" \ "SearchFile" "SearchFile_sth_func" \ "NewSh" "NewSh_sth_func" \ "Extract" "Extract_sth_func" \ "Test" "Test_sth_func" \ SetAttr_as_AssociativeArrayName_func $obj MenuCaption \ "1" "ヘルプ(SVG形式)を開く (Google Chrome や Snap Note で見えます) [Help]" \ "2" "ファイルを検索する [SearchFile]" \ "3" "新規作成する - bashlib が使えるシェル・スクリプト・ファイル [NewSh]" \ "5" "圧縮ファイルを解凍する。ファイル名を一覧する [Extract]" \ "7" "bashlib の自動テストを実行する [Test]" \ InputCommand_func $obj "" "$1" "$AppKey" } サンプル AppKey Main_func の第2引数 InputCommandOpt クラス string .Lead .CommandReplace AssociativeArray AssociativeArray .MenuCaption リード文 コマンド名→関数名の連想配列 コマンド名→メニュー項目の表示内容の連想配列 テスト → T_UI_Manually.sh # [T_InputCommand_func] メニュー項目の番号、コマンド名、関数名を入力できます。シェルのコマンドも入力できます。 → T_Menu.sh # [T_InputCommandOpt_func] 関連 → ArrayClass.remove_method パラメーターをシフトします。 InputOption_func InputOption_func <in_out_OptionName> [...] [--comment="<CommentCSV>"] 長い名前のオプションの設定値をユーザーに求めるモードに入ります。 【引数】 in_out_OptionName オプション名と同じ名前の変数名 サンプル InputOption_func word message --comment="ワーク, メッセージ" 画面の例: 現在の設定:--work="/home/user1" ワーク --message="temporary" メッセージ Enterのみ、または . :設定完了 オプションを入力してください(例:--work="abc"、--flag)> テスト → T_UI_Manually.sh # [T_InputOption_func] → T_Menu.sh # [T_InputOptionAuto_func] CommentCSV コメント、CSV 形式 --flag のように値を指定しなかったときは、値が 1 になります。--flag のようなオプションを取り消したいときは、値を "" か "0" にしてください。 ファイル操作 AppKeyClass.newWritable_method CheckWritable_func mkdir_func mkdir_for_it_func rm_func sudo_func MakeSymbolicLink_func readlink_func chown_it_and_parent_func Extract_func ListUpIn_func ExpandWildcard_func ReplaceTextFile_func ReplaceTextFileLineRange_func GetAbsPath_func GetParentAbsPath_func SearchParent_func 書き込み可能なパスを設定します。 指定したパスが書き込み可能でなければ、エラーにします。 指定したフォルダーを作成します。 指定したファイルを格納するフォルダーを作成します。 指定したファイルまたはフォルダーを削除します。 指定した関数を呼び出し、root ユーザーで実行します。 指定したパスと、その親フォルダーの所有者を変更します。 シンボリック・リンクを作成します。 リンクを含まない絶対パスに変換します。 圧縮ファイルを解凍します。 圧縮ファイルに入っているファイルのパスを一覧表示します。 ワイルドカードを展開して、パスを一覧します。 ファイルの内容を置き換えます。 複数行のテキストを置き換えます。 相対パスを絶対パスにします。 親フォルダーの絶対パスを返します。 親フォルダーの方向にファイルを探します。 AppKeyClass.newWritable_method 重要なファイルを誤って消したり上書きすることを防ぎます。スクリプトを実行する場合、ヒューマン・エラーによって予想と異なる処理をしてしま うことがあるため、sudo を使った本人確認によるOSの保護だけでは不十分です。 サンプル function Main_func(){ local AppKey="$2" ; AppKeyClass.newWritable_method "$AppKey" "$PWD" } function Main_func(){ local AppKey="$2" ; AppKeyClass.newWritable_method "$AppKey" "sub1" "sub2" } サンプル 複数指定 CheckWritable_func 関数 を使って、書き込み可能なパスかどうかをチェックできます。 rm_func 関数 など、bashlib が提供している、ファイルにライトする関数を使うときは、 AppKeyClass.newWritable_method を呼び出す必要があります。 AppKeyClass.newWritable_method <AppKey> <Path> ... 書き込み可能なパスを設定します。 【引数】 AppKey Main_func 関数の第2引数 Path ... 書き込み可能なパスの羅列。サブ・フォルダーも書き込み可能 テスト → T_File.sh # [T_Writable_func] 関連 → AppKeyClass.enableInstall_method CheckWritable_func CheckWritable_func <Path> 指定したパスが書き込み可能でなければ、エラーにします。 【引数】 Path ファイルまたはフォルダーのパス サンプル CheckWritable_func "sub/a.txt" テスト → T_File.sh # [T_Writable_func] mkdir_func mkdir_func <FolderPath> 指定したフォルダーを作成します。 すでに存在していてもエラーになりません。 【引数】 FolderPath 作成するフォルダーのパス サンプル mkdir_func "sub" テスト → T_File.sh # [T_mkdir_func] mkdir_for_it_func mkdir_for_it_func <FilePath> 指定したファイルを格納するフォルダーを作成します。 【引数】 FilePath ファイル・パス サンプル mkdir_for_it_func "sub/a.txt" #// sub フォルダーを作成します。 テスト → T_File.sh # [T_mkdir_func] rm_func rm_func <Path> ... 指定したファイルまたはフォルダーを削除します。 【引数】 Path ... ファイルまたはフォルダーのパスの羅列 書き込み禁止であっても、削除します。空文字を指定したときは、何もしません。 一時的に root ユーザーになって削除する サンプル local AppKey="$2" ; AppKeyClass.newWritable_method "$AppKey" "$PWD" rm_func "a.txt" sudo_func rm_func "a.txt" サンプル → T_File.sh # [T_rm_func] テスト sudo_func に対応しています。 sudo_func sudo_func ... 指定した関数を呼び出し、その中で実行するシェル・コマンドを root ユーザーで実行します。 【引数】 ... sudo_func に対応した関数と、パラメーター 呼び出す関数が、g_TemporarySudo 変数(下記)に対応している必要があります。 g_TemporarySudo 変数 サンプル sudo_func rm_func "a.txt" sudo_func を経由して呼び出した関数では、g_TemporarySudo="sudo" に、そうではないときは、g_TemporarySudo="" になります。 function rm_func(){ local Path="$1" $g_TemporarySudo rm -r "$Path" } 関数定義サンプル テスト → T_File_Manually.sh # [T_Sudo_func] chown_it_and_parent_func chown_it_and_parent_func [<Option>] <Owner> <Path> 指定したパスと、その親フォルダーの所有者を変更します。 上記サンプルは、chown_it_and_parent_func を呼び出して、ファイルやフォルダーの作成やすでにあるファイルの修正を行うことができるようにして、 修正が終わったら、所有者を(元の)root に戻しています。 Path にファイルまたはフォルダーが存在するときは、そのファイルまたはフォ ルダーの所有者を Owner 引数に指定したユーザーに変更します。 存在しないときは、Path の位置に何かを作ることはしません。 Path の親フォルダーが存在しないときは、Owner 引数に指定したユーザーを 所有者としたフォルダーを作成します。 【引数】 Option -R オプション(サブ・フォルダーにも適用)、または省略 Owner 新しい所有者:所有グループ Path ファイルまたはフォルダーのパス → chown 参考 サンプル sudo_func chown_it_and_parent_func $USER:$USER "sub/sub2/file.txt"echo "text" > "sub/sub2/file.txt" sudo_func chown_it_and_parent_func root:root "sub/sub2/file.txt" Owner に指定したユーザーが自分であれば、Path に指定したファイルが存在していても存在していなくても、Path にファイルの内容をライトできるように なります。 → T_File.sh # [T_chown_it_and_parent_func] テスト chmod_x_func chmod_x_func <FolderPath> <SetCsv> <UnsetCsv> フォルダーの中のファイルの実行属性を設定、および、設定解除します。 【引数】 FolderPath 処理を行うフォルダー SetCsv 実行属性を設定するファイル名(ワイルドカード) UnsetCsv 実行属性を設定解除するファイル名(ワイルドカード) サンプル chmod_x_func "." "*.sh" "*.txt, *.log, *.html, *.svg" テスト → T_Menu.sh # [T_chmod_x_func] FolderPath に指定したフォルダーのサブ・フォルダーにあるファイルも処理対象になります。 → chmod_x コマンド 関連 MakeSymbolicLink_func MakeSymbolicLink_func <LinkSrcPath> <Target> シンボリック・リンクを作成します。 【引数】 Target リンク先。 相対パスなら基準はLinkSrcPath が入ったフォルダー LinkSrcPath シンボリック・リンクのリンク元のパス → ln 参考 CheckWritable_func によるチェックがあります。 サンプル MakeSymbolicLink_func "library.so" "library.so.1.0.0" LinkSrcPath にリンク元がすでに存在していても上書きします。上書きするリンク元がフォルダーをリンクしていても、そのリンク元を上書きします。 ln コマンドと、引数の順番が逆なので注意してください。ただし、cp の方向や、ls -l で表示されるリンクの方向と同じです。 → T_File.sh # [T_MakeSymbolicLink_func] テスト readlink_func readlink_func <Path> ; <PhysicalPath>="$g_Ret" シンボリック・リンクを含むパスから、リンクを含まない絶対パスに変換します。 【引数】 Path パス g_Ret (出力)シンボリック・リンクを含まない Path。物理パスの絶対パス サンプル readlink_func "folder_link/file" #// folder_link -> folderAssert_func '"$g_Ret" == "/home/user1/folder/file"' → T_File.sh # [T_MakeSymbolicLink_func] テスト Extract_func Extract_func <PackagePath> <DstFolder> [--transform_from="<From>"] 圧縮ファイルを解凍します。 【引数】 PackagePath 圧縮ファイルのパス(*.tar.bz2, *.tar.gz, *.zip) DstFolder 解凍してできるフォルダーのパス → T_File.sh # [T_Extract_func] テスト → T_File.sh # [T_ExtractGZ_func] フォルダーを圧縮したファイルを解凍するとき、DstFolder 引数のフォルダーの中にフォルダーができるのではなく、DstFolder に圧縮前の内容が解凍されます。 このとき、元のフォルダー名と違う名前に解凍することもできます。 tar cvjf pack.tar.bz2 packrm -r pack Extract_func "pack.tar.bz2" "pack" #// tar xvf pack.tar.bz2 -C "." と同じ Extract_func "pack.tar.bz2" "pack2" #// 改名することも可能 圧縮したときのフォルダー名を探すために、Extract_func の内部では、ファイルの一覧を取得しています。 Linux では、ファイルの一覧を取得するのに、解凍するときと同じ時間が、 かかってしまうため、 tar コマンドを使って直接解凍するより Extract_func を実行する 時間は、2倍の時間がかかります。 tar コマンドと同じ時間に短縮するには、--transform_from オプションを使って、圧縮した ときのフォルダー名を指定してください。 圧縮時にフォルダー名の前に ./ を付けたときは、 --transform_from オプションにも同様に ./ を付けてください。 From 圧縮したときのフォルダーのパス。省略時=自動判定 tar cvjf pack.tar.bz2 packrm -r pack Extract_func "pack.tar.bz2" "pack" --transform_from="pack" tar cvjf pack.tar.bz2 ./packrm -r pack Extract_func "pack.tar.bz2" "pack" --transform_from="./pack" ListUpIn_func ListUpIn_func <PackagePath> 圧縮ファイルの中に入っているファイルのパスを一覧表示します。 【引数】 PackagePath 圧縮ファイルのパス(*.tar.bz2, *.tar.gz, *.zip) → T_File.sh # [T_Extract_func] テスト サンプル ListUpIn_func "sample.tar.bz2" ExpandWildcard_func → T_File.sh # [T_ExpandWildcard_func] ExpandWildcard_func <WildcardPath> <out_FolderAbsPath> <out_StepPaths> [<Options> ...] ワイルドカードを展開して、パスを一覧します。 【引数】 WildcardPath out_FolderAbsPath ワイルドカード (*) を含むパス (出力) 基準フォルダーの絶対パス (出力) 相対パスの配列 out_StepPaths Options ... オプション(下記) Options 引数 --File --Folder --SubFolder テスト ファイルを一覧する フォルダーを一覧する --Link シンボリック・リンクを一覧する サブ・フォルダーも含めて一覧する --File、--Folder、--Link は、同時に複数指定できます。--File、--Folder、--Link の1つも指定しないときは、--File、--Folder、--Link のすべてを 指定したときと同じになります。 サンプル local folder local step_paths local path ExpandWildcard_func "T_*" folder step_paths --File --SubFolder #//[out] folder, step_paths for path in "${step_paths[@]}" ;do echo "$folder/$path" done ; done_func $? ReplaceTextFile_func ReplaceTextFile_func <Path> <FromText> <ToText> [<Option>] テキスト・ファイルの内容を置き換えます。 【引数】 Path 内容を置き換えるファイルのパス 置き換える前の文字列。sed の正規表現。改行は\n FromText サンプル MultiLine_func "\n" \ "def" \ "ghi" from="$g_Ret" MultiLine_func "\n" \ "xx" \ "yy" \ "zz" to="$g_Ret" ReplaceTextFile_func "sample.txt" "$from" "$to" 置き換えた後の文字列。改行は\n ToText Option 省略時=デフォルト動作。 "-i" FromStr の大文字小文字を区別しない defghi xxyy zz に置き換えます 2行まで → T_File.sh # [T_ReplaceTextFile_func] テスト ReplaceTextFile_func "sample.txt" "def\nghi" "xx\nyy\nzz" 上記コードは、下記コードと同じです。 Option="-i" は、Mac OS X Snow Leopard では使えません。 → ReplaceTextFileLineRange_func 行の削除、複数行の置き換え 関連 FromText にマッチする文字列が複数あるときは、そのすべてを置き換えます。 MultiLine_func 参考 → sed → StringEscapeUtilsClass.escapeSed_method 正規表現をエスケープする サンプル ReplaceTextFile_func "sample.txt" "value=0" "value=1" value=0 を value=1 に置き換えます。 複数行を置き換えます。 サンプル ReplaceTextFile_func "sample.h" '^.*SymbolA.*$' "" に置き換えます abc abc abc#define SymbolA 1 abc 正規表現を使った行の置き換え ReplaceTextFileLineRange_func ReplaceTextFileLineRange_func <Path> <StartOfFromText> <EndOfFromText> <ToText> [<Option>] テキスト・ファイルの中の複数行のテキストを置き換えます。 【引数】 Path 内容を置き換えるファイルのパス 置き換える前の先頭行の一部にマッチする sed の正規表現 StartOfFromText 置き換えた後の文字列。 ""=行を削除する ToText Option 省略時=デフォルト動作。"-i" = 置き換える前の大文字小文字を区別しない 置き換える前の最終行の(同上)。 ""= 1行のみ置き換える EndOfFromText → T_File.sh # [T_ReplaceTextFileLineRange_func] テスト サンプル MultiLine_func "\n" \ "x" \ "y" local a1="$g_Ret" ReplaceTextFileLineRange_func "_tmp.txt" "c" "f" "$a1" abccde efg ghi なら xy ghi に置き換わります 関連 → MultiLine_func 複数行の文字列を作ります 置き換えた後の複数行の改行文字は、"\n" を指定してください。最終行の末尾に改行文字は付けないでください。 置き換える前の複数行を削除するときは、ToText="" を指定してください。 置き換えた後を1行分の空行にするときは、ToText="\n" を指定してください。 Option="-i" は、Mac OS X Snow Leopard では使えません。 に置き換わります abcghi なら abccde ghi efg サンプル ReplaceTextFileLineRange_func "_tmp.txt" "e" "" "" 1行ずつ削除する 参考 → sed → StringEscapeUtilsClass.escapeSed_method 正規表現をエスケープする GetAbsPath_func GetAbsPath_func <StepPath> [<BasePath>] ; <AbsPath>="$g_Ret" 相対パスを絶対パスにします。 【引数】 StepPath 相対パス BasePath 基準パス、省略時=カレント・フォルダー g_Ret (出力)絶対パス サンプル GetAbsPath_func "a.txt"Assert_func '"$g_Ret" == "/home/user1/a.txt"' → T_File.sh # [T_GetAbsPath_func] テスト サンプル GetAbsPath_func "a.txt" "/home/user1/sub"Assert_func '"$g_Ret" == "/home/user1/sub/a.txt"' 関連 → readlink_func リンクを含まない絶対パスに変換します。 GetParentAbsPath_func GetParentAbsPath_func <Path> ; <ParentPath>="$g_Ret" 親フォルダーの絶対パスを返します。 【引数】 Path 任意のパス (出力)Path 引数の親フォルダーの絶対パス g_Ret サンプル GetParentAbsPath_func "a.txt"Assert_func '"$g_Ret" == "$PWD"' → T_File.sh # [T_GetParentAbsPath_func] テスト 関連 → SearchParent_func 親フォルダーの方向にファイルを探します。 SearchParent_func SearchParent_func <Path> ; <ParentPath>="$g_Ret" 親フォルダーの方向にファイルを探して見つかった絶対パスを返します。 【引数】 Path 任意のパス (出力)Path 引数の親フォルダーの絶対パス g_Ret サンプル cd "/home/user1/bashlib/sub/sub"GetParentAbsPath_func "menu" Assert_func '"$g_Ret" == "/home/user1/bashlib/menu"' 上記の結果になる状況: /home/user1/bashlib/sub/sub/menu が無く、 /home/user1/bashlib/sub/menu が無く、 /home/user1/bashlib/menu があるとき Path="sub/file" のときは、sub フォルダーから親の方向に file を探します。見つからないときは、g_Ret="" になります。 → T_File.sh # [T_SearchParent_func] テスト 関連 → GetParentAbsPath_func 親フォルダーの絶対パスを返します。 その他 → AddIfNotExist_func PATH や INCLUDE を編集する プロセス制御、関数呼び出し GetLongOptions_func CheckArgCount_func CheckMinArgCount_func CheckMaxArgCount_func CheckOutParamIsConflictToLocal_func CheckEvalParamIsConflictToLocal_func local_func SetOutput_func SetOutputAsArray_func GetUsableCommands_func GetUsableApplicationsForMac_func IsMac_func IsInstalled_func InstallIfNot_func Uninstall_func 長い名前のオプションを連想配列にまとめます。 関数に渡された引数の数が異なるときはエラーにします。 関数に渡された引数の数が少ないときはエラーにします。 関数に渡された引数の数が多いときはエラーにします。 出力変数名がローカル変数名と衝突していたらエラー。 eval する内容がローカル変数名と衝突していたらエラー。 ローカル変数を、まとめて宣言します。 出力変数に値を設定します。 出力変数に配列を設定します。 シェルから使えるコマンドを取得します。 アプリを起動するコマンドを取得します。 Mac OS X 用。 OS が Mac かどうかを返します。 パッケージがインストールされているかどうかを返します。 インストールやアンインストールを許可するかどうか。 インストールされていなければ、インストールします。 アンインストールします。 AppKeyClass.enableInstall_method GetLongOptions_func GetLongOptions_func <out_Arguments> <out_OptAssocArray> <AllArguments> ... 長い名前のオプション(GNU形式)を連想配列にまとめます。 【引数】 out_Arguments (出力) 長い名前のオプション以外の AllArguments out_OptAssocArray (出力) 長い名前のオプションの連想配列。 キーは -- をカット AllArguments, ... すべての引数(下記) サンプル function T_GetLongOptions_func(){ T_GetLongOptionsSub_func --opt1 arg1 --opt2=a,b --opt3="a b" arg2 arg3 } function T_GetLongOptionsSub_func() { local arguments $declare_AssociativeArrayClass option GetLongOptions_func arguments option "$@" #//[out] arguments, option Assert_func '"${arguments[0]}" == "arg1"' Assert_func '"${arguments[1]}" == "arg2"' Assert_func '"${arguments[2]}" == "arg3"' Assert_func '${#arguments[@]} == 3' Attr_func option opt1 ; Assert_func '"$g_Ret" == "1"' #// 値の指定が無ければ1 Attr_func option not ; Assert_func '"$g_Ret" == ""' #// オプション指定が無ければ "" Attr_func option opt2 ; Assert_func '"$g_Ret" == "a,b"' Attr_func option opt3 ; Assert_func '"$g_Ret" == "a b"' } テスト → T_Str.sh # [T_GetLongOptions_func] declare_AssociativeArrayClass GetLongOptions_func コマンドラインに指定されたオプションなら "$g_Arguments[@]"関数に指定されたオプションなら "$@" AllArguments 引数 --flag --value="ABC" out_OptAssocArray 引数に得られる連想配列の要素 連想配列のキーは、"flag"、対応する値は、"1" (未指定) 連想配列のキーは、"value"、対応する値は、"ABC" 任意のキーに対応する値は、"" Attr_func Assert_func --opt1 arg1 --opt2=a,b --opt3="a b" arg2 arg3 opt1not opt2 opt3 関連 → getopts コマンド 短い名前のオプション(POSIX 形式)を取得する CheckArgCount_func CheckArgCount_func <Count> <AllArguments> 関数に渡された引数の数が、指定した数と異なるときはエラーにします。 【引数】 Count 要求する引数の数 すべての引数 AllArguments サンプル CheckArgCount_func 2 "$@" テスト → T_Str.sh # [T_CheckArgCount_func] 関連 → CheckMinArgCount_func → CheckMaxArgCount_func CheckMinArgCount_func CheckMinArgCount_func <MinCount> <AllArguments> 関数に渡された引数の数が、指定した数より少ないときはエラーにします。 【引数】 MinCount 最小の引数の数 すべての引数 AllArguments サンプル CheckMinArgCount_func 2 "$@" テスト → T_Str.sh # [T_CheckArgCount_func] 関連 → CheckArgCount_func CheckMaxArgCount_func CheckMaxArgCount_func <MaxCount> <AllArguments> 関数に渡された引数の数が、指定した数より多いときはエラーにします。 【引数】 MaxCount 最大の引数の数 すべての引数 AllArguments サンプル CheckMaxArgCount_func 2 "$@" テスト → T_Err.sh # [T_CheckMaxArgCount_func] → T_Str.sh # [T_CheckArgCount_func] 関連 → CheckArgCount_func CheckOutParamIsConflictToLocal_func CheckOutParamIsConflictToLocal_func <OutParamName> <LocalNames> ... 出力変数の実体の名前がローカル変数名と衝突していたらエラーにします。 【引数】 OutParamName 出力変数の実体の名前 すべてのローカル変数の名前 LocalNames, ... function Sample_func(){ local local_a="aaa" OutX_func local_a #//[out] local_a } function OutX_func() { local out_Var="$1" local local_a local locals="out_Var local_a locals" CheckOutParamIsConflictToLocal_func $out_Var $locals SetOutput_func $out_Var "bbb" #// Sample_func の local_a 変数に格納できない } サンプル CheckOutParamIsConflictToLocal_func → local_func 引数とローカル変数を宣言し、出力変数名の衝突をチェックします 関連 → SetOutput_func 出力変数に設定します SetOutput_func テスト → T_Err.sh # [T_CheckOutParamIsConflictToLocal_func] bash では、呼び出し元の関数が使える変数を参照することができます。呼び出し元の関数のローカル変数も参照することができます。 これを利用すると、引数への出力ができます。 しかし、local を使って宣言したローカル変数と同じ名前の、呼び出し元が使える変数は、 参照することができません。 呼び出し元が指定した出力引数の変数名がローカル変数の名前と 衝突していたら、出力することができません。 CheckOutParamIsConflictToLocal_func は、 それを検出します。 CheckEvalParamIsConflictToLocal_func CheckEvalParamIsConflictToLocal_func <EvalExpression> <LocalNames> ... eval に渡す変数名がローカル変数名と衝突していたらエラーにします。 【引数】 EvalExpression eval に渡す内容 すべてのローカル変数の名前 LocalNames, ... サンプル function Sample_func(){ local local_a="aaa" EvalX_func 'local_a is $local_a' } function EvalX_func() { local EvalExpr="$1" local local_a local locals="EvalExpr local_a locals" CheckEvalParamIsConflictToLocal_func $EvalExpr $locals eval echo "$EvalExpr" #// 衝突しているため Sample_func の "aaa" が参照できない } CheckEvalParamIsConflictToLocal_func eval を内部で呼び出している関数である下記の EvalX_func 関数は、ローカル変数 EvalExpr と local_a を宣言しており、EvalX_func の内部から Sample_func 関数の local_a 変数を 参照できなくなっているため、Sample_func 関数は、EvalX_func 関数の EvalExpr 引数 (第1引数)に、EvalExpr や local_a という名前の変数名を渡すことはできません。 CheckEvalParamIsConflictToLocal_func 関数は、そのときにエラーにします。 → CheckOutParamIsConflictToLocal_func 参考 テスト → T_Err.sh # [T_CheckEvalParamIsConflictToLocal_func] → Watch local_func 関数の中でのみ使えるローカル変数を、まとめて宣言します。 テスト → T_Str.sh # [T_local_func] … --local の値は、ローカル変数の名前の羅列(CSV形式) … --arg の値は、引数の名前の羅列(CSV形式) local a1="$1"local a2="$2" local tmp1 local tmp2 上記は下記のコードと同じです。 local_func \ --arg=" a1, a2 " \ --local=" tmp1, tmp2 " \ "$@" ; eval "$g_Ret" 関連 → CheckOutParamIsConflictToLocal_func 出力引数の名前の衝突チェック 出力引数は、引数の名前の前に (out) を付けてください。入出力引数は、引数の名前の前に (in_out) を付けてください。 出力引数や入出力引数の名前の衝突があれば、エラーにします。 local_func \ --arg=" (in_out)a1, (out)a2 " \ --local=" tmp1, tmp2 " \ "$@" ; eval "$g_Ret" 変数が多いときは、改行することができます。これは、" " で囲まれた文字列を途中で改行することができる bash の仕様です。 local_func \ --local=" tmp1, tmp2 tmp3 " \ "$@" ; eval "$g_Ret" local_func [--arg="<Argument> [, ...]" ] [--local="<Local> [, ...]" ] <AllArguments><Code>="$g_Ret" 【引数】 Argument, ... 引数の変数名の羅列 引数の変数の名前の羅列 Local, ... AllArguments "$@" を指定してください g_Ret (出力)変数宣言を行うソースコード サンプル SetOutput_func SetOutput_func <out_Var> <Value> 指定した変数名の、出力変数に値を設定します。 【引数】 out_Var (出力)変数 (=変数名を入力) 変数に設定する値 Value サンプル function Caller_func(){ local tmp Called_func tmp #//[out] tmp : 出力を格納する変数 $不要 echo $tmp #// "output" } function Called_func() { local out="$1" CheckEvalParamIsConflictToLocal_func "$out" out SetOutput_func $out "output" #// 出力を格納 } 参考 $out "output" $不要 Called_func の中でも、呼び出し元の tmp 変数を参照できることを利用して、tmp 変数に値を設定しています。 → 引数に出力する (bash) CheckEvalParamIsConflictToLocal_func → echo 出力を取得する関数の中のグローバル変数の変更 関連 → SetOutputAsArray_func 出力変数に配列を設定します。 → CheckOutParamIsConflictToLocal_func 出力引数の名前の衝突チェック 呼び出し側が echo 出力を取得する関数呼び出し(` ` や $( ) )をした場合、出力することはできません。 テスト → T_Err.sh # [T_CheckOutParamIsConflictToLocal_func] SetOutputAsArray_func SetOutputAsArray_func <out_Var> <Value> ... 指定した変数名の、出力変数に配列を設定します。 【引数】 out_Var (出力)変数 (=変数名を入力) 配列である出力変数の全ての要素 Value ... → SetOutput_func 参考 サンプル SetOutputAsArray_func "$out" "A" "B" "C" out="var" のとき、var=( "A" "B" "C" ) と同じです。 出力変数に値を設定します。 テスト → T_Str.sh # [T_SetOutputAsArray_func] 呼び出し側が echo 出力を取得する関数呼び出し(` ` や $( ) )をした場合、出力することはできません。 GetUsableCommands_func GetUsableCommands_func <out_FoundCommands> <CommandCandidates> 指定した候補の中から、シェルから使えるコマンドを取得します。 【引数】 out_FoundCommands (出力) 使えるコマンドの配列 サンプル → T_File.sh # [T_GetUsableCommands_func] テスト CommandCandidates 使うコマンドの候補の羅列 local apps local commands apps=( "ls" "not-found" "cp" ) GetUsableCommands_func commands "${apps[@]}" #//[out] commands Assert_func '"${#commands[@]}" == "2"' Assert_func '"${commands[0]}" == "ls "' Assert_func '"${commands[1]}" == "cp "' GetUsableApplicationsForMac_func GetUsableApplicationsForMac_func <out_FoundCommands> <Applications> インストールされているアプリケーションを起動するコマンドを取得します。 Mac OS X 用。 【引数】 out_FoundCommands Applications (出力) インストールされているアプリの起動コマンドの配列 調べるアプリケーション名の羅列 local apps=( "Google Chrome" "Safari" )GetUsableApplicationsForMac_func cmds "$apps[@]" Assert_func "${cmds[0]}" == "open -a \"Safari\" " サンプル → MacWiki - コマンド-open (Web) 参考 → T_File.sh # [T_GetUsableApplicationsForMac_func] テスト IsMac_func if IsMac_func ;then OS が Mac かどうかを返します。 【引数】 (出力)Mac=1、Mac以外=0 g_Ret サンプル IsMac_func ; if [ "$g_Ret" == "1" ];then echo "Mac" else echo "not Mac" fi テスト → T_File_Manually.sh # [T_Install_func] Mac なら、InstallIfNot_func が使えないという表示がされます。 IsInstalled_func IsInstalled_func <PackageSymbol> ; <Is>="$g_Ret" パッケージがインストールされているかどうかを返します。 【引数】 PackageSymbol Debian パッケージのシンボル名 → T_File_Manually.sh # [T_Install_func] テスト IsInstalled_func "lha-sjis"echo "$g_Ret" サンプル g_Ret (出力)インストールされている=1、いない=0 apt-get が使えないとエラーになります。 Mac ではエラーになります。 AppKeyClass.enableInstall_method AppKeyClass.enableInstall_method <AppKey> インストールやアンインストールを許可する。 関連 → InstallIfNot_func → Uninstall_func → T_File_Manually.sh # [T_Install_func] テスト → AppKeyClass.newWritable_method 【引数】 AppKey Main_func 関数の第2引数 サンプル function Main_func(){ local AppKey="$2" ; AppKeyClass.enableInstall_method "$AppKey" } InstallIfNot_func InstallIfNot_func <PackageSymbol> もし、パッケージがインストールされていなければ、インストールします。 【引数】 PackageSymbol Debian パッケージのシンボル名 テスト function Main_func(){ local AppKey="$2" ; AppKeyClass.enableInstall_method "$AppKey" Sub_func } function Sub_func() { sudo_func InstallIfNot_func "lha-sjis" } サンプル → T_File_Manually.sh # [T_Install_func] apt-get が使えないとエラーになります。 Mac ではエラーになります。 AppKeyClass.enableInstall_method を呼び出してから使えます。 Uninstall_func Uninstall_func <PackageSymbol> パッケージをアンインストールします。 【引数】 PackageSymbol Debian パッケージのシンボル名 テスト function Main_func(){ local AppKey="$2" ; AppKeyClass.enableInstall_method "$AppKey" Sub_func } function Sub_func() { sudo_func Uninstall_func "lha-sjis" } サンプル → T_File_Manually.sh # [T_Install_func] apt-get が使えないとエラーになります。 Mac ではエラーになります。 AppKeyClass.enableInstall_method を呼び出してから使えます。 その他 SetOutput_func 指定した変数名の出力変数に値を設定します。 AddIfNotExist_func PATH などの変数に定義されていなければ追加します。 → 終了ステータスの取得は、|| を使う テスト支援 Assert_func EchoTestStart_func 指定した条件を満たさなかったときは、エラーにします。 テスト・シンボルを表示します。 Assert_func Assert_func <Condition> 指定した条件を満たさなかったときは、エラーにします。 【引数】 Condition 条件式。 ' ' で囲むこと。 if などの [ ] の中 サンプル var1="abc"Assert_func '"$var1" == "def"' <ERROR msg="Assert failed"><Expression><![CDATA["$var1" == "def" ]]></Expression><Result><![CDATA[ abc == def ]]></Result></ERROR> 表示例: 注意 条件式に、引数参照に相当する $1, $2, $3, … を指定すると、正しく判定できません。 補足 ' ' の中に変数の参照や、関数の呼び出しがある場合、Assert_func 関数の中から参照や呼び出しが行われます。 Assert_func 関数を呼び出している関数のローカル 変数は、Assert_func 関数の中からも参照することができるため、正常に動作します。 Assert '"$2" == ""' BAD サンプル Assert_func '-e "$path"' ファイルか何かがあること EchoTestStart_func EchoTestStart_func <TestName> テスト・シンボルを表示します。 【引数】 TestName テスト・シンボル サンプル EchoTestStart_func "${FUNCNAME[0]}" ((( Main_func ))) 表示例: その他 → CheckArgCount_func → CheckOutParamIsConflictToLocal_func 文字列 → String (Java Platform SE 6) (Web) 参考 LF, Tab AddIfNotExist_func IsNumeric_func StringClass.length_method StringClass.substring_method StringClass.trim_method StringClass.right_method StringClass.cutLastOf_method LeftOfStr_func LeftOfLastStr_func RightOfStr_func RightOfLastStr_func StringClass.indexOf_method StringClass.lastIndexOf_method StringClass.replace_method StringClass.toLowerCase_method StringClass.toUpperCase_method StringEscapeUtilsClass.escapeGrep_method StringEscapeUtilsClass.escapeSed_method StringEscapeUtilsClass.escapeBashDoubleQuot_method StringEscapeUtilsClass.escapeBashParam_method StringEscapeUtilsClass.escapeBashReplace_method MultiLine_func 改行文字、タブ文字 文字列が含まれていなければ、追加したものを返します。 文字列の中から指定の文字列を後ろから検索します。 数値かどうかを返します。 文字列の長さを返します。 文字列の一部を抽出します。 両端の空白、タブ、改行をカットした文字列を返します。 文字列の末尾から数文字抽出します。 文字列の末尾が指定した文字ならカットします。 指定したキーワードより左側の部分文字列を返します。 最後のキーワードより左側の部分文字列を返します。 指定したキーワードより右側の部分文字列を返します。 最後のキーワードより右側の部分文字列を返します。 文字列の中から指定の文字列を検索します。 指定した文字列を置き換えます。 指定した文字列を小文字にして返します。 指定した文字列を大文字にして返します。 grep 用にエスケープします。 sed 用にエスケープします。 変数置換用にエスケープします。 " " で囲む用にエスケープします。 " " や ' ' で囲まないエスケープをします。 複数の文字列を複数行の文字列にして返します。 LF, Tab $LF $Tab 改行文字 タブ文字 var="abc${LF}def${LF}"WS var サンプル AddIfNotExist_func AddIfNotExist_func <WholeStr> <AddStr> <Separator> ; <NewStr>="$g_Ret" WholeStr に AddStr が含まれていなければ、先頭に AddStr を追加したものを返します。 【引数】 WholeStr 文字列の列挙 AddStr 追加する文字列 Separator 文字列の列挙を区切る文字列 サンプル AddIfNotExist_func "A,B,C" "D" "," ; Assert_func '"$g_Ret" == "D,A,B,C"'AddIfNotExist_func "A,B,C" "A" "," ; Assert_func '"$g_Ret" == "A,B,C"' (出力)新しい文字列の列挙 g_Ret AddIfNotExist_func "$PATH" "$HOME/bin" ":" ; PATH="$g_Ret" → T_File.sh # [T_AddIfNotExist_func] テスト サンプル コンマで区切られた文字列の列挙に追加する 環境変数 PATH にパスに追加する IsNumeric_func IsNumeric_func <Value> ; <Is>="$g_Ret" 指定した値が数値かどうかを返します。 【引数】 Value 文字列または数値 (出力)数値=1、数値以外=0 g_Ret サンプル IsNumeric_func $key ; if [ "$g_Ret" == "1" ];then echo "$key is number" else echo "$key is not number" fi → T_Str.sh # [T_IsNumeric_func] テスト StringClass.length_method StringClass.length_method <self> ; <Length>="$g_Ret" 指定した文字列の長さを返します。 【引数】 self 文字列 (出力)文字列の長さ。 日本語の文字も1文字単位 g_Ret サンプル StringClass.length_method "ABCD" ; Assert_func "$g_Ret" == "4" StringClass.length_method "あいう" ; Assert_func "$g_Ret" == "3" → T_Str.sh # [T_StringClass_length_func] テスト StringClass.substring_method StringClass.substring_method <self> <StartIndex> <EndIndex> ; <SubString>="$g_Ret" 文字列の一部を抽出します。 【引数】 self 全体の文字列 StartIndex 抽出を開始する文字の位置。先頭=0 EndIndex 抽出を終了する次の文字の位置。省略=最後まで サンプル local var="ABCDE"StringClass.substring_method "$var" 2 ; Assert_func "$g_Ret" = "CDE" StringClass.substring_method "$var" 2 4 ; Assert_func "$g_Ret" = "CD" (出力)部分文字列 g_Ret → T_Str.sh # [T_StringClass_substring_func] テスト StringClass.trim_method StringClass.trim_method <self> ; <Str>="$g_Ret" 両端の空白、タブ、改行をカットした文字列を返します。 【引数】 self 全体の文字列 g_Ret (出力)両端の空白、タブ、改行をカットした文字列 サンプル local var=" ABCDE "StringClass.trim_method "$var" ; Assert_func "$g_Ret" = "ABCDE" → T_Str.sh # [T_StringClass_trim_func] テスト StringClass.right_method StringClass.right_method <String> <Length> ; <PartStr>="$g_Ret" 文字列の末尾から数文字抽出します。 【引数】 String 全体の文字列 Length 抽出する文字数 サンプル local var="ABCDE"StringClass.right_method "$var" 3 ; Assert_func "$g_Ret" = "CDE" (出力)部分文字列 g_Ret → T_Str.sh # [T_StringClass_right_func] テスト StringClass.cutLastOf_method StringClass.cutLastOf_method <String> <LastStr> ; <PartStr>="$g_Ret" 文字列の末尾が指定した文字ならカットします。 【引数】 String 全体の文字列 サンプル local var="folder/"StringClass.cutLastOf_method "$var" "/" ; Assert_func "$g_Ret" = "folder" 末尾にあればカットする文字列 LastStr (出力)部分文字列 g_Ret → T_Str.sh # [T_StringClass_cutLast_func] テスト LeftOfStr_func LeftOfStr_func <String> <Key> ; <PartStr>="$g_Ret" 指定したキーワードより左側の部分文字列を返します。 【引数】 String 全体の文字列 Key キーワード g_Ret (出力)キーワードより左側の部分文字列 サンプル LeftOfStr_func "index.html#main" "#" ; Assert_func '"$g_Ret" == "index.html"' → T_Str.sh # [T_LeftRightOfStr_func] テスト LeftOfLastStr_func LeftOfLastStr_func <String> <Key> ; <PartStr>="$g_Ret" 指定したキーワードの最後のキーワードより左側の部分文字列を返します。 【引数】 String 全体の文字列 Key キーワード g_Ret (出力)最後のキーワードより左側の部分文字列 サンプル LeftOfLastStr_func "/folder/sub/index.html" "/" ; Assert_func '"$g_Ret" == "/folder/sub"' → T_Str.sh # [T_LeftRightOfStr_func] テスト RightOfStr_func RightOfStr_func <String> <Key> ; <PartStr>="$g_Ret" 指定したキーワードより右側の部分文字列を返します。 【引数】 String 全体の文字列 Key キーワード g_Ret (出力)キーワードより右側の部分文字列 サンプル RightOfStr_func "folder/sub/index.html" "/" ; Assert_func '"$g_Ret" == "sub/index.html"' → T_Str.sh # [T_LeftRightOfStr_func] テスト RightOfLastStr_func RightOfLastStr_func <String> <Key> ; <PartStr>="$g_Ret" 指定したキーワードの最後のキーワードより右側の部分文字列を返します。 【引数】 String 全体の文字列 Key キーワード g_Ret (出力)最後のキーワードより右側の部分文字列 サンプル RightOfLastStr_func "/folder/sub/index.html" "/" ; Assert_func '"$g_Ret" == "index.html"' → T_Str.sh # [T_LeftRightOfStr_func] テスト StringClass.indexOf_method StringClass.indexOf_method <self> <Keyword> <StartIndex> ; <Index>="$g_Ret" 文字列の中から指定の文字列を検索します。 【引数】 self 全体の文字列 Keyword 検索キーワード StartIndex 検索を開始する文字の位置。省略時=先頭 サンプル local var="ABCDEABCDE"StringClass.indexOf_method "$var" "BC" ; Assert_func $g_Ret = 1 StringClass.indexOf_method "$var" "BC" 5 ; Assert_func $g_Ret = 6 StringClass.indexOf_method "$var" "X" ; Assert_func $g_Ret = -1 StringClass.indexOf_method "$var" "X" 5 ; Assert_func $g_Ret = -1 (出力)見つかった位置。先頭=0。見つからなかった=-1 g_Ret → T_Str.sh # [T_StringClass_index_func] テスト StringClass.lastIndexOf_method StringClass.indexOf_method <self> <Keyword> <StartIndex> ; <Index>="$g_Ret" 文字列の中から指定の文字列を後ろから検索します。 【引数】 self 全体の文字列 Keyword 検索キーワード StartIndex 検索を開始する文字の位置。省略時=末尾 サンプル local var="ABCDEABCDE"StringClass.lastIndexOf_method "$var" "BC" ; Assert_func '$g_Ret == 6' StringClass.lastIndexOf_method "$var" "BC" 5 ; Assert_func '$g_Ret == 1' StringClass.lastIndexOf_method "$var" "X" ; Assert_func '$g_Ret == -1' StringClass.lastIndexOf_method "$var" "X" 5 ; Assert_func '$g_Ret == -1' (出力)見つかった位置。先頭=0。見つからなかった=-1 g_Ret → T_Str.sh # [T_StringClass_index_func] テスト StringClass.replace_method StringClass.replace_method <self> <From> <To> ; <Str>="$g_Ret" 指定した文字列を置き換えます。 【引数】 self 置き換えられる文字列 置き換える前の部分文字列(正規表現ではない) From サンプル StringClass.replace_method "ABCDE" "C" "xx" ; Assert_func '"$g_Ret" == "ABxxDE"' g_Ret (出力)置き換えた後の文字列 置き換えた後の部分文字列 To From にマッチする部分が無かったときは、g_Ret=$self になります。 → T_Str.sh # [T_StringClass_replace_func] テスト → T_Speed_Manually.sh # [T_StringReplaceSpeed_func] StringClass.toLowerCase_method StringClass.toLowerCase_method <self> ; <LowerStr>="$g_Ret" 指定した文字列を小文字にして返します。 【引数】 self 文字列 (出力)小文字に置き換えた文字列 g_Ret サンプル StringClass.toLowerCase_method "ABCDE" ; Assert_func '"$g_Ret" == "abcde"' → T_Str.sh # [T_StringClass_toLoHi_func] テスト StringClass.toUpperCase_method StringClass.toUpperCase_method <self> ; <UpperStr>="$g_Ret" 指定した文字列を大文字にして返します。 【引数】 self 文字列 (出力)大文字に置き換えた文字列 g_Ret サンプル StringClass.toUpperCase_method "abcde" ; Assert_func '"$g_Ret" == "ABCDE"' → T_Str.sh # [T_StringClass_toLoHi_func] テスト StringEscapeUtilsClass.escapeGrep_method StringEscapeUtilsClass.escapeGrep_method <self> ; <EscapedStr>="$g_Ret" 指定した文字列を grep の検索キーワードに使えるようにエスケープします。 【引数】 self 文字列 (出力)エスケープした文字列 g_Ret サンプル StringEscapeUtilsClass.escapeGrep_method '[out]' #// $g_Ret == '\[out\]'grep -r "$g_Ret" * テスト → T_Str.sh # [T_StringEscapeUtilsClass_func] StringEscapeUtilsClass.escapeSed_method StringEscapeUtilsClass.escapeSed_method <self> ; <EscapedStr>="$g_Ret" 指定した文字列を sed の置換前に使えるようにエスケープします。 【引数】 self 文字列 (出力)エスケープした文字列 g_Ret サンプル StringEscapeUtilsClass.escapeSed_method '[out]' #// $g_Ret == '\[out\]'sed -e "s/$g_Ret/xxxx/g" a.txt テスト → T_Str.sh # [T_StringEscapeUtilsClass_func] → sed 次の関数やコマンドに指定することができます。 → ReplaceTextFile_func → ReplaceTextFileLineRange_func StringEscapeUtilsClass.escapeBashReplace_method StringEscapeUtilsClass.escapeBashReplace_method <self> ; <EscapedStr>="$g_Ret" 指定した文字列を変数の内容を置換する前の文字列に使えるようにエスケープします。 【引数】 self 文字列 (出力)エスケープした文字列 g_Ret サンプル StringEscapeUtilsClass.escapeBashReplace_method '(out)'var="${var//$g_Ret/xxxx}" #// $g_Ret == '\(out\)' テスト → T_Str.sh # [T_StringEscapeUtilsClass_func] StringEscapeUtilsClass.escapeBashDoubleQuot_method StringEscapeUtilsClass.escapeBashDoubleQuot_method <self> ; <EscapedStr>="$g_Ret" 指定した文字列を " " で囲まれたリテラル文字列に使えるようにエスケープします。 【引数】 self 文字列 (出力)エスケープした文字列 g_Ret サンプル #// Search '\*'StringEscapeUtilsClass.escapeGrep_method '\*' #// $g_Ret == '\\*' grep -r "$g_Ret" * StringEscapeUtilsClass.escapeBashDoubleQuot_method "$g_Ret" #// $g_Ret == '\\\\\*' echo "Write \"$g_Ret\" at grep keyword." grep -r "\\\\\*" * #// 上の grep と同じ テスト → T_Str.sh # [T_StringEscapeUtilsClass_func] StringEscapeUtilsClass.escapeBashParam_method StringEscapeUtilsClass.escapeBashParam_method <self> ; <EscapedStr>="$g_Ret" 指定した文字列を " " や ' ' で囲まないリテラル文字列に使えるようにエスケープします。 【引数】 self 文字列 (出力)エスケープした文字列 g_Ret サンプル out="a b.txt"cp a.txt "$out" StringEscapeUtilsClass.escapeBashParam_method "$out" #// $g_Ret == 'a\ b.txt' echo "Write $g_Ret at cp parameter." cp a.txt a\ b.txt #// 上の cp と同じ テスト → T_Str.sh # [T_StringEscapeUtilsClass_func] out="a b.txt"StringEscapeUtilsClass.escapeBashParam_method "$out" #// $g_Ret == 'a\ b.txt' eval echo '"a"' > "$g_Ret" #// echo "a" > a\ b.txt サンプル MultiLine_func MultiLine_func <LineFeedChar> <Lines> ... ; <String>="$g_Ret" 複数の文字列を複数行の文字列にして返します。 【引数】 LineFeedChar 改行文字 1行の文字列の羅列 Lines ... サンプル MultiLine_func "$LF" \ "abc" \ "def" \ "ghi" Assert_func '"$g_Ret" == "abc${LF}def${LF}ghi"' (出力)複数行の文字列 g_Ret 最終行の末尾に改行は入りません。 最終行の末尾に改行を入れるときは ` ` の外に "$LF" を記述してください。 サンプル MultiLine_func "$LF" \ "abc" \ "def" \ "ghi" Assert_func '"$var$LF" == "abc${LF}def${LF}ghi${LF}"' 最終行の末尾に改行は入りません。 MultiLine_func "\n" \ "abc" \ "def" \ "ghi" Assert_func '"$g_Ret" == "abc\ndef\nghi"' サンプル テスト → T_Str.sh # [T_MultiLine_func] 関連 → ArrayClass.fromLines_method その他 → WS 指定した変数の値をダンプします。 → ArrayClass.fromCSV_method CSV 形式の文字列を、配列にします。 配列 参考 → Array (Java Platform SE 6) (Web) → ArrayList (Java Platform SE 6) (Web) ArrayClass.getLength_method ArrayClass.get_method ArrayClass.set_method ArrayClass.remove_method ArrayClass.clear_method ArrayClass.fromLines_method ArrayClass.fromCSV_method IsSameArrayOutOfOrder_func 配列の要素数を返します。 配列の要素を返します。 配列の要素を設定します。 配列の要素を1つ削除して、後にある要素は前に詰めます。 配列の要素をすべて削除します。 複数行の文字列を1行ずつの配列にします。 CSV 形式の文字列を、配列にします。 配列の要素が同じかどうかを返します。 ArrayClass.getLength_method ArrayClass.getLength_method <self> ; <Length>="$g_Ret" 配列の要素数を返します。 【引数】 self 配列変数の名前 g_Ret (出力)配列の要素数 テスト → T_Str.sh # [T_ArrayClass_getLength_func] arr=( "a" "b" "c" ) ArrayClass.getLength_method arr Assert_func '$g_Ret == 3' サンプル ${#arr[@]} 補足 bash では、配列の要素数は、下記のように取得できます。ArrayClass.getLength_method は、配列の名前が変数に入っているときに使うと、 読みやすくなります。 ArrayClass.get_method ArrayClass.get_method <self> <Index> ; <Element>="$g_Ret" 配列の要素を返します。 【引数】 self 配列変数の名前 Index 配列番号 (出力)配列の要素 g_Ret arr=( "a b" "c" "d" ) ArrayClass.get_method arr 0 Assert_func '"$g_Ret" == "a b"' サンプル テスト → T_Str.sh # [T_ArrayClass_get_set_func] ${arr[0]} 補足 bash では、配列の要素は、下記のように取得できます。ArrayClass.get_method は、配列の名前が変数に入っているときに使うと、 読みやすくなります。 ArrayClass.set_method ArrayClass.set_method <self> <Index> <Value> 配列の要素を設定します。 【引数】 self 配列変数の名前 Index 配列番号 設定する配列の要素 Value arr=( "a" "b" "c" ) ArrayClass.set_method arr 1 "xx" ArrayClass.get_method arr 1 Assert_func '"$g_Ret" == "xx"' サンプル テスト → T_Str.sh # [T_ArrayClass_get_set_func] arr[0]="xx" 補足 bash では、配列の要素は、下記のように設定できます。ArrayClass.set_method は、配列の名前が変数に入っているときに使うと、 読みやすくなります。 bash では、配列番号 0, 1, 2 の要素があるときに、配列番号 4 の要素を代入すると、配列番号 0, 1, 2, 4 となり、要素数は 4 になりますが、 ArrayClass.set_method では、配列番号 3 に空文字が入り、要素数は 5 になります。 ArrayClass.remove_method ArrayClass.remove_method <self> <Index> 配列の要素を1つ削除して、後にある要素は前に詰めます。 【引数】 self 配列変数の名前 Index 削除する要素の配列番号(0以上) unset arr[0] 補足 bash では、配列の要素は、下記のように削除できます。ただし、後にある要素は前に詰められません。 arr=( "a" "b" "c" ) ArrayClass.remove_method arr 1 Assert_func '"${arr[0]}" == "a"' Assert_func '"${arr[1]}" == "c"' サンプル テスト → T_Str.sh # [T_ArrayClass_remove_func] サンプル ArrayClass.remove_method g_Arguments 1 コマンドラインの第1パラメーターを削除します。 g_Arguments ArrayClass.clear_method ArrayClass.clear_method <self> 配列の要素をすべて削除します。 【引数】 self 配列変数の名前 arr=( "a" "b" "c" ) ArrayClass.clear_method arr サンプル テスト → T_Str.sh # [T_ArrayClass_clear_func] ArrayClass.fromLines_method ArrayClass.fromLines_method <out_Array> <Lines> 複数行の文字列を1行ずつの配列にします。 【引数】 Lines 複数行の文字列。改行は LF(0x0A) (出力) 配列。 要素は1行 out_Array 関連 → MultiLine_func テスト → T_Str.sh # [T_ArrayClass_Lines_func] lines=`ls -1`ArrayClass.fromLines_method arr "$lines" #//[out] arr サンプル ArrayClass.fromCSV_method ArrayClass.fromCSV_method <out_Array> <CSV> CSV 形式の文字列を、配列にします。 【引数】 CSV out_Array CSV 形式の文字列 (出力) 配列 サンプル テスト → T_Str.sh # [T_ArrayFromCSV_func] local arr local csv="ABC, DEF, \"GHI \"" ArrayClass.fromCSV_method arr "$csv" #//[out] arr Assert_func '"${arr[0]}" == "ABC"' Assert_func '"${arr[1]}" == "DEF"' Assert_func '"${arr[2]}" == "GHI "' IsSameArrayOutOfOrder_func IsSameArrayOutOfOrder_func <ArrayALength> <ArrayA> <ArrayB> ; <Is>="$g_Ret" 配列の要素が同じかどうかを返します。 要素の順番は違っても同じとします。 【引数】 ArrayA ArrayALength 配列A 配列Aの要素数 サンプル テスト → T_Str.sh # [T_IsSameArrayOutOfOrder_func] arr1=( "a" "b" "c" ) arr2=( "b" "c" "a" ) IsSameArrayOutOfOrder_func ${#arr1[@]} "${arr1[@]}" "${arr2[@]}" if [ "$g_Ret" == "0" ];then Error_func ;fi ArrayB g_Ret 配列B (出力)同じ=1、違う=0 その他 → CopyArray_func 連想配列、オブジェクト declare_AssociativeArrayClass Attr_func SetAttr_func AssociativeArrayClass.getLength_method AssociativeArrayClass.getKeys_method AssociativeArrayClass.getItems_method AssociativeArrayClass.destroy_method CopyArray_func CopyArrayAttr_func SetAttr_as_ArrayName_func SetAttr_as_AssociativeArrayName_func 連想配列の宣言を行います。 連想配列の値を返します。 連想配列の値を、設定します。 連想配列にあるすべてのキーを返します。 連想配列の要素数を返します。 連想配列にあるすべての値を返します。 連想配列を削除します。 配列、または、連想配列をコピーします。 配列型の属性の値を取得します。 配列を生成して連想配列に代入します。 連想配列を生成して別の連想配列に代入します。 bashlib を使ったオブジェクト指向記述法 - bashool 参考 → bash によるオブジェクト指向 [bashool] if [ "${BASH_VERSINFO[0]}" -ge "4" ];then declare_AssociativeArrayClass="declare -A" #// bash ver4 else declare_AssociativeArrayClass="declare" #// bash ver3 fi #// global variables as associative arrray $declare_AssociativeArrayClass g_ObjectA $declare_AssociativeArrayClass g_ObjectB g_Objects=( "g_ObjectA" "g_ObjectB" ) function InitObjs_func() { local obj obj=g_ObjectA #// obj=&g_ObjectA SetAttr_func $obj "Attr1" "Value1" #// obj->Attr1 = "Value1" SetAttr_func $obj "Attr2" "Value2" #// obj->Attr2 = "Value2" obj=g_ObjectB #// obj=&g_ObjectB SetAttr_func $obj "Attr1" "Value1" SetAttr_func $obj "Attr2" "Value4" } function Main_func() { InitObjs_func for obj in ${g_Objects[@]};do Attr_func $obj "Attr2" #// obj->Attr2 echo "$g_Ret" done ; done_func $? } オブジェクトへのポインター declare を関数内で実行するとローカル、関数の外で実行するとグローバルになります。関数の引数には指定できません。 なので、オブジェクトはグローバルにせざるを得ない でしょう。 → オブジェクトへのポインター 属性(メンバー変数) メソッド(メンバー関数) → 多態性(ポリモアフィズム) bashlib を使った多態性(ポリモアフィズム) if [ "${BASH_VERSINFO[0]}" -ge "4" ];then declare_AssociativeArrayClass="declare -A" #// bash ver4 else declare_AssociativeArrayClass="declare" #// bash ver3 fi #// global variables as associative arrray $declare_AssociativeArrayClass g_ObjectA $declare_AssociativeArrayClass g_ObjectB g_Objects=( "g_ObjectA" "g_ObjectB" ) function InitObjs_func() { local obj obj=g_ObjectA SetAttr_func $obj "Class" "AClass" SetAttr_func $obj "Attr1" "Value1" SetAttr_func $obj "doA_method" "AClass.doA_method" obj=g_ObjectB SetAttr_func $obj "Class" "BClass" SetAttr_func $obj "Attr1" "Value1" SetAttr_func $obj "doA_method" "BClass.doA_method" } function Main_func() { local obj InitObjs_func for obj in ${g_Objects[@]};do Attr_func $obj "doA_method" ; $g_Ret $obj done } function AClass.doA_method() { local self="$1" echo "AClass.doA_method( $self )" } function BClass.doA_method() { local self="$1" echo "BClass.doA_method( $self )" } プロセス内の呼び出しの場合 → プロセス内の呼び出しの場合 子プロセスを呼び出す場合 → 子プロセスを呼び出す場合 g_SetupFileNames=( "./Child1.sh" "./Child2.sh" ) function Main_func() { local name_sh ; local result for name_sh in ${g_SetupFileNames[@]};do $name_sh --FuncA_func --Param1=Value1 --Param2=Value2 done for name_sh in ${g_SetupFileNames[@]};do result=`$name_sh --FuncB_func --Param1=Value1` echo "\$result = $result" done } Main.sh g_AllArguments="$@"g_FuncName=${1#--*} #// cut -- from $1 function Main_func() { $g_FuncName $g_AllArguments } function FuncA_func() { echo "FuncA_func in Package1.sh" } function FuncB_func() { echo "FuncB_func in Package1.sh" } Child1.sh g_AllArguments="$@"g_FuncName=${1#--*} #// cut -- from $1 function Main_func() { $g_FuncName $g_AllArguments } function FuncA_func() { echo "FuncA_func in Package1.sh" } function FuncB_func() { echo "FuncB_func in Package1.sh" } Child2.sh 関数名は、クラス名+Class+ピリオド+メソッド名_method。クラス名は、単語の先頭は大文字、後は小文字。(Java準拠) メソッド名は、先頭の単語はすべて小文字、後の単語は先頭文字のみ大文字。(Java準拠) メソッドに相当する関数の第1引数は、オブジェクトの名前(=連想配列の名前)。 関数内では、self という変数名にする。 function SampleClass.doSample_method(){ local self="$1" サンプル オブジェクトの名前を、ファイル名、または、第1パラメーターにします。メソッド名を、関数名と同じ、パラメーターなしのオプションにします。 メソッドのパラメーターを、パラメーターありのオプションにします。 declare_AssociativeArrayClass 連想配列の宣言を行います。declare -A は、bash ver3 では使えませんが、declare_AssociativeArrayClass は、 bash ver3 と bash ver4 のどちらでも使えます。 サンプル $declare_AssociativeArrayClass object #// 変数 object を連想配列として宣言する if [ "${BASH_VERSINFO[0]}" -ge "4" ];then declare_AssociativeArrayClass="declare -A" #// bash ver4 else declare_AssociativeArrayClass="declare" #// bash ver3 fi $declare_AssociativeArrayClass object サンプル メイン・スクリプト・ファイルのグローバル変数 declare_AssociativeArrayClass は、メイン・スクリプト・ファイルの末尾にある bashlib include が行われてから使えますが、その前であるメイン・スクリプト・ファイルの グローバルに連想配列を宣言するときは、その前に、declare_AssociativeArrayClass の 実装をコピーしてください。 Attr_func Attr_func <self> <AttrName> ; <Value>="$g_Ret" 連想配列の値(オブジェクトの属性の値)を、返します。 【引数】 self 連想配列の変数名=オブジェクト名 属性名=連想配列のキー AttrName サンプル $declare_AssociativeArrayClass object_a object_a["Attr1"]="Value1" Attr_func object_a "Attr1" echo "$g_Ret" (出力)属性の値=連想配列の値 g_Ret 4(src) 参考 → bash によるオブジェクト指向 [bashool] 3(src) SetAttr_func SetAttr_func <self> <AttrName> <Value> 連想配列の値(オブジェクトの属性の値)を、設定します。 【引数】 self 連想配列の変数名=オブジェクト名 属性名=連想配列のキー。 英数字から始めること AttrName サンプル $declare_AssociativeArrayClass object_a local obj obj="object_a" SetAttr_func $obj "Attr1" "Value1" 属性の値=連想配列の値 Value 4(src) 参考 → bash によるオブジェクト指向 [bashool] bash ver3 では、連想配列の代わりに、bashlib が用意した擬似連想配列を使います。bash ver4 でも、連想配列の名前を変数に代入した変数から、連想配列の値を参照 するには、SetAttr_func 関数の中で行っている複雑な記述が必要になります。 C言語的に表現すると、構造体のポインターからメンバー変数への代入になります。 obj = &object_a;obj->Attr1 = "Value1"; サンプル $declare_AssociativeArrayClass object_a SetAttr_func object_a "Attr1" "Value1" C言語的に表現すると、構造体のメンバー変数への代入になります。 object_a.Attr1 = "Value1"; object_a[Attr1]="Value1" bash ver4 的に表現すると、 → T_Str.sh # [T_Attr_func] テスト → T_Str.sh # [T_Attr3_func] サンプル function Main_func(){ $declare_AssociativeArrayClass object_a Sub_func object_a Attr_func object_a "Attr1" ; echo "$g_Ret" Attr_func object_a "Attr2" ; echo "$g_Ret" } function Sub_func() { local AssocArrayName="$1" SetAttr_func $AssocArrayName "Attr1" "Value 1" SetAttr_func $AssocArrayName "Attr2" "Value 2" } 連想配列の名前を代入した変数から、連想配列の値を参照する 関数の中で、連想配列に代入する → T_Str.sh # [T_SetAttrInFunc_func] 3(src) AssociativeArrayClass.getLength_method AssociativeArrayClass.getLength_method <self> ; <Length>="$g_Ret" 連想配列の要素数を返します。 【引数】 self 連想配列の変数名=オブジェクト名 (出力)要素数 g_Ret $declare_AssociativeArrayClass assoc_arr SetAttr_func assoc_arr "Key1" "Value1" SetAttr_func assoc_arr "Key2" "Value2" AssociativeArrayClass.getLength_method assoc_arr echo "Length=$g_Ret" サンプル 4(src) 3(src) テスト → T_Str.sh # [T_CopyAssociativeArray4_func] → T_Str.sh # [T_CopyAssociativeArray3_func] AssociativeArrayClass.getKeys_method 4(src) AssociativeArrayClass.getKeys_method <self> ; <Keys>=( $g_Ret ) 連想配列にあるすべてのキー(オブジェクトのすべての属性名)を返します。 【引数】 self 連想配列の変数名=オブジェクト名 (出力)すべてのキー(フィールド形式) g_Ret $declare_AssociativeArrayClass assoc_arr SetAttr_func assoc_arr "Key1" "Value1" SetAttr_func assoc_arr "Key2" "Value2" AssociativeArrayClass.getKeys_method assoc_arr local keys=( $g_Ret ) local key for key in "${keys[@]}" ;do Attr_func assoc_arr "$key" echo "$key = $g_Ret" done ; done_func $? サンプル → T_Str.sh # [T_Attr3_func] テスト 3(src) 値に空白文字を含む場合、正しく動作しません。 → T_Str.sh # [T_CopyAssociativeArray4_func] → T_Str.sh # [T_CopyAssociativeArray3_func] AssociativeArrayClass.getItems_method AssociativeArrayClass.getItems_method <self> ; <Keys>=( $g_Ret ) 連想配列にあるすべての値(オブジェクトのすべての属性値)を返します。 【引数】 self 連想配列の変数名=オブジェクト名 (出力)すべてのキー(フィールド形式) g_Ret $declare_AssociativeArrayClass assoc_arr SetAttr_func assoc_arr "Key1" "Value1" SetAttr_func assoc_arr "Key2" "Value2" AssociativeArrayClass.getItems_method assoc_arr local values=( $g_Ret ) サンプル 値に空白文字を含む場合、正しく動作しません。AssociativeArrayClass.getKeys_method のサンプルのように、キーのループを 使ってください。 4(src) 3(src) テスト → T_Str.sh # [T_CopyAssociativeArray4_func] → T_Str.sh # [T_CopyAssociativeArray3_func] AssociativeArrayClass.destroy_method 4(src) 3(src) AssociativeArrayClass.destroy_method <self> 連想配列(オブジェクト)を削除します。 【引数】 self 連想配列の変数名=オブジェクト名 $declare_AssociativeArrayClass assoc_arr SetAttr_func assoc_arr "Key1" "Value1" SetAttr_func assoc_arr "Key2" "Value2" AssociativeArrayClass.destroy_method assoc_arr サンプル テスト → T_Str.sh # [T_CopyAssociativeArray4_func] → T_Str.sh # [T_CopyAssociativeArray3_func] CopyArray_func 4(src) CopyArray_func <DstArray> <SrcArray> 配列、または、連想配列をコピーします。 【引数】 DstArray (出力)コピー先の配列変数名 コピー元の配列変数名 SrcArray $declare_AssociativeArrayClass ObjectA SetAttr_func ObjectA "Attr1" "Value1" SetAttr_func ObjectA "Attr2" "Value2" SetAttr_func ObjectA "Attr3" "Value3" local arr=ObjectA $declare_AssociativeArrayClass ObjectB CopyArray_func ObjectB $arr サンプル → T_Str.sh # [T_CopyArray_func] テスト → T_Str.sh # [T_CopyAssociativeArray4_func] 3(src) → T_Str.sh # [T_CopyAssociativeArray3_func] CopyArrayAttr_func CopyArrayAttr_func <self> <AttrName> <out_Array> 指定したオブジェクトの、配列型の属性の値を取得します。 【引数】 self 連想配列の変数名=オブジェクト名 属性名=連想配列のキー AttrName (出力) 属性の配列のコピー out_Array $declare_AssociativeArrayClass objlocal arr SetAttr_as_ArrayName_func obj "AttrA" "a" "b" "c" CopyArrayAttr_func obj "AttrA" arr #//[out] arr Assert_func '"${arr[0]}" = "a"' Assert_func '"${arr[1]}" = "b"' Assert_func '"${arr[2]}" = "c"' サンプル → T_Str.sh # [T_CopyArrayAttr_func] テスト SetAttr_as_ArrayName_func SetAttr_as_ArrayName_func <self> <AttrName> <Params> ... 配列を生成して、その配列を、連想配列のアイテムに代入します。 【引数】 self 連想配列の変数名=オブジェクト名 属性名=連想配列のキー AttrName 生成する配列の要素の並び Params ... $declare_AssociativeArrayClass objlocal arr SetAttr_as_ArrayName_func obj "AttrA" "a" "b" "c" CopyArrayAttr_func obj "AttrA" arr #//[out] arr Assert_func '"${arr[0]}" = "a"' Assert_func '"${arr[1]}" = "b"' Assert_func '"${arr[2]}" = "c"' サンプル → T_Str.sh # [T_CopyArrayAttr_func] テスト SetAttr_as_AssociativeArrayName_func SetAttr_as_AssociativeArrayName_func <self> <AttrName> <Params> ... 連想配列を生成して、その連想配列を、別の連想配列のアイテムに代入します。 【引数】 self 連想配列の変数名=オブジェクト名 属性名=別の連想配列のキー AttrName サンプル $declare_AssociativeArrayClass object local obj=object SetAttr_as_AssociativeArrayName_func $obj "Assoc" \ AttrA "ValueA" \ AttrB "ValueB" Attr_func $obj "Assoc" ; assoc="$g_Ret" Attr_func $assoc "AttrA" Assert_func "$g_Ret" == "ValueA" Attr_func $assoc "AttrB" Assert_func "$g_Ret" == "ValueB" 上記のサンプルを、C言語的に表現すると、 obj->Assoc->AttrA = "ValueA";obj->Assoc->AttrB = "ValueB"; assoc = obj->Assoc; ASSERT( assoc->AttrA == "ValueA" ); ASSERT( assoc->AttrB == "ValueB" ); Params ... 生成する連想配列のキーと値の並び → T_Str.sh # [T_Attr3_func] テスト bashlib_inc.sh (bashlib のベース・システム) bashlib を使うには、メイン・スクリプト・ファイルの末尾に、bashlib をインクルードする十数行のコードを配置する必要があります。 menu から新規作成 スクリプト・ファイルには、そのコードが含まれています。 scriptlib main sample メイン・スクリプトの末尾にある bashlib include のコードから、bashlib_inc.sh をインクルードする bashlib_inc.sh bashlib.sh bashlib3.sh bashlib_inc.sh から、すべてのライブラリをインクルードする (NewShなど) インクルードの構成 メイン・スクリプト・ファイルがあるフォルダーに scriptlib フォルダーがあれば、それを使いますが、無ければ、親フォルダーの方向に scriptlib を探します。 その動きは SearchParent_func と同じです。 Main_func g_StartInPath g_Arguments bashlib を使うスクリプトは、Main_func から内容を記述します。 プロセス起動時のカレント・フォルダーの絶対パス。 プロセスを起動した時のコマンドライン。 してできた Main_func Main_func <Option> <AppKey> bashlib を使うスクリプトは、この Main_func から内容を記述します。 【引数】 Option AppKey 未定義 アプリケーション・キー ユーザ定義関数です。引数はあってもなくてもかまいませんが、ファイルに出力するスクリプトは、 AppKeyClass.newWritable_method を呼ぶ必要があります。 AppKey 引数を使った Main_func 関数を定義しているスクリプト・ファイルの中に、(bashlib をインクルードして Main_func を呼び出すコード)が必要です。 bashlib include のコード Main_func 関数が呼ばれるときのカレント・フォルダーは、スクリプトがあるフォルダーになります。 起動したときのカレント・フォルダーは、 に入っています。 g_StartInPath → bashlib/sample フォルダ サンプル g_StartInPath declare g_StartInPath as string プロセス起動時のカレント・フォルダーの絶対パス。 Main_func 関数を呼ばれたときのカレント・フォルダーは、メイン・シェルスクリプト・ファイルがあるフォルダーになります。 プロセス起動時のカレント・フォルダーは、g_StartInPath から 取得する必要があります。 このように変更した理由は、書かれたスクリプトが、汎用コマンドとして使われることより、 スクリプトがあるフォルダーに対して固有の処理を行うことが多いためです。 g_StartInPath を変更すると、 cd "$g_StartInPath" サンプル → T_File.sh # [T_StartIn_func] テスト の基準フォルダーを変更することができます。 InputPath_func g_Arguments declare g_Arguments as array of string プロセスを起動した時のコマンドライン。 ./sample.sh -o opt_param --option="param X" Param1 "Param 2" サンプル 起動したときのコマンド: g_Arguments の値: "${g_Arguments[0]}" == "/home/user1/sample.sh""${g_Arguments[1]}" == "-o" "${g_Arguments[2]}" == "opt_param" "${g_Arguments[3]}" == "--option=param X" "${g_Arguments[4]}" == "Param1" "${g_Arguments[5]}" == "Param 2" → ArrayClass.remove_method 関連 パラメーターをシフトします。 テスト サンプル ArrayClass.remove_method g_Arguments 1 コマンドラインの第1パラメーターを削除します。 → T_File.sh # [T_StartIn_func] サンプル local here ; here=`dirname "${g_Arguments[0]}"` スクリプトが入っているフォルダーのパス エラー処理 CheckPipeStatus_func done_func Error_func g_Err_Desc g_ExitStatus TryStart_func, TryEnd1_func, TryEnd2_func ErrClass.raiseOverwrite_method ErrClass.clear_method パイプを使った処理において、エラーを検出します。 ループの直後に、; done_func $? を記述してください。 エラーを発生させます。 エラーメッセージ。 終了ステータスのコピー。 エラーをキャッチします。構造化例外処理。 処理中のエラーを、上位の TryStart に投げます。 エラーをクリアします。 CheckPipeStatus_func CheckPipeStatus_func <PipeStatus> 直前のパイプを使った処理において、1つでも終了ステータスが 0 以外ならエラーにします。 【引数】 PipeStatus "${PIPESTATUS[@]}" を指定してください サンプル true | false CheckPipeStatus_func "${PIPESTATUS[@]}" → T_Err.sh # [T_CheckPipeStatus_func] テスト サンプル line=`cat "not_found" | head --lines=1 ; CheckPipeStatus_func "${PIPESTATUS[@]}"` CheckPipeStatus_func を使わないと、パイプの最後の段のエラーしか検出しません。 done_func while true; do CheckWritable_func "$PWD/out/a.txt" break done ; done_func $? ; done_func $? ループの直後(break コマンドのジャンプ先)に、; done_func $? 記述してください。エラーが発生した状態にも関わらず、ループのすぐ外から続きを実行することが、 なくなります。 → T_Err.sh # T_NotTryLoop_func テスト → T_NotTryLoopSub_func → T_Err.sh # T_DoneNoParam_func → T_Err.sh # T_DoneInErrMode_func → T_DoneInErrModeSub_func Error_func Error_func [<Message>] エラーを発生させます。 標準エラー出力にメッセージを表示します。 【引数】 Message メッセージ。省略時=メッセージなしのエラー サンプル Error_func "エラー" <ERROR msg="エラー"/> 表示例: 表示例: <ERROR msg="エラー" value="123"/> value="123"Error_func "<ERROR msg=\"エラー\" value=\"$value\"/>" サンプル <ERROR> タグを指定すると、そのまま表示します。 エラーメッセージは、<ERROR> タグを付けて表示されます。 エラーメッセージは、g_Err_Desc 変数に格納されます。終了ステータス(return値)は、1になります。 テスト 表示例: <ERROR/> Error_func サンプル 引数を省略したとき → T_ErrorInErrModeSub_func → T_Err.sh # T_ErrorInErrMode_func g_Err_Desc declare g_Err_Desc as string エラーメッセージ。 Error_func を呼び出すと、g_Err_Desc にエラーメッセージが格納されます。 Error_func 以外で発生したエラーでは、メッセージは格納されません。 サンプル echo "$g_Err_Desc" >&2 g_ExitStatus declare g_ExitStatus as integer 終了ステータスのコピー。 TryStart_func ~ TryEnd2_func 終了ステータスが 0 以外になると、TryEnd2_func にジャンプして、g_ExitStatus にその終了ステータスが格納されます。 bashlib を使う環境では、 ErrClass.clear_method を呼び出すと、0 にクリアされます。 エラー処理モードなら、1~255 のどれかが、通常モードなら 0 が入っています。 の間で実行したコマンドの サンプル if [ "$g_ExitStatus" == "2" ];then TryStart_func, TryEnd1_func, TryEnd2_func while TryStart_func; do CheckWritable_func "$PWD/out/a.txt" TryEnd1_func; done ;TryEnd2_func $? if [ "$g_ExitStatus" == "0" ]; then Error_func "Error exptected" ;fi echo "${g_Err_Desc}${LF}This error is OK"; ErrClass.clear_method TryStart_func TryEnd1_func TryEnd2_func $? テスト → T_Err.sh # T_TryLv0_func → T_Err.sh # T_TryLv1_func → T_Err.sh # T_TryLv1Clear_func → T_Err.sh # T_TryLv2_func → T_Err.sh # T_TryLv2Change_func → T_Err.sh # T_TryLv1Proc1_func → T_Err.sh # T_NotTryLoop_func エラーをキャッチします。エラーが発生してもシェル・スクリプトを終了させたくないときに使います。 サンプル while TryStart_func; do CheckWritable_func "$PWD/out/a.txt" TryEnd1_func; done ;TryEnd2_func $? #// Finally if [ "$g_ExitStatus" != "0" ]; then ErrClass.raiseOverwrite_method ;fi エラーであることのテスト サンプル Finally ブロック。 正常時でもエラー時でも実行するブロック。 TryStart_func ~ TryEnd2_func の間で実行したコマンドの終了ステータスが 0 以外になると、TryEnd2_func にジャンプして、終了ステータスを g_ExitStatus 変数に格納し、 エラー処理モードに入ります。 エラーが発生したときは、必要なエラー処理を行った後で、ErrClass.clear_method を 呼び出してエラー処理モードから通常モードに戻すか、Error_func または ErrClass.raiseOverwrite_method を呼び出して、コールスタックの浅い方にある TryStart_func ~ TryEnd2_func にジャンプするか、シェル・スクリプトを正しく異常終了 する必要があります。 bashlib は、終了ステータスをエラー番号として扱い、終了ステータスが 0 以外なら エラーが発生したものとみなします。 これは、bash の -e オプションや、make と同じ 仕様です。 → 終了ステータスの取得は、|| を使う 関連 → T_TryLv2Sub_func → T_TryLv1ClearSub_func → T_TryLv1Sub_func → T_TryLv0Sub_func → T_TryLv2ChangeSub_func → T_TryLv1Proc1Sub_func → T_NotTryLoopSub_func → T_Err.sh # T_DoneInErrMode_func → T_DoneInErrModeSub_func → T_ErrorInErrModeSub_func → T_Err.sh # T_ErrorInErrMode_func TryStart_func TryEnd1_func TryEnd2_func $? ErrClass.raiseOverwrite_method ErrClass.raiseOverwrite_method [<ErrMessage>] 処理中のエラーを、上位の TryStart に投げます。 【引数】 ErrMessage 上書きするエラーメッセージ。 省略=上書きしない テスト → T_Err.sh # T_TryLv2Change_func → T_TryLv2ChangeSub_func while TryStart_func; do CheckWritable_func "$PWD/out/a.txt" TryEnd1_func; done ;TryEnd2_func $? #// Finally if [ "$g_ExitStatus" != "0" ]; then ErrClass.raiseOverwrite_method ;fi サンプル ErrClass.raiseOverwrite_method TryStart_func ErrClass.clear_method エラーをクリアします。 ErrClass.clear_method g_ExitStatus 変数、g_Err_Desc 変数の値はクリアされます。 テスト → T_Err.sh # T_TryLv1Clear_func → T_TryLv1ClearSub_func while TryStart_func; do CheckWritable_func "$PWD/out/a.txt" TryEnd1_func; done ;TryEnd2_func $? if [ "$g_ExitStatus" == "0" ]; then Error_func "Error exptected" ;fi echo "${g_Err_Desc}${LF}This error is OK"; ErrClass.clear_method サンプル ErrClass.clear_method TryStart_func その他 → CheckArgCount_func 関数に渡された引数の数をチェックします 困ったときは? (デバッグ) エラーメッセージを見ても分からないときは、スクリプトの開発者に質問してください。あなたが開発者であるときは、下記のデバッグ用の関数を使ってください。 debugger, de es Pause_func ステップ実行を開始します。 開始地点で、コールスタックを表示します。 指定した変数の値をダンプします。 Enter キーを押すまで、実行途中で止めます。 ec 現在位置と、変数の値を表示します。 BashSyntax " ' ` の対応関係が誤っている箇所を探します。 vbslib4 が提供するツールです。gedit でも、" ' ` の対応関係は、文字の色から分かります。 ソフトウェアデザイン館 Sage Plaisir 21 http://www.sage-p.com/ サポート ホームページにあるサポート掲示板かメールアドレスへ de, debugger debugger ステップ実行を開始します。 開始地点で、コールスタックを表示します。de は、debugger の別名です。 どちらを使ってもかまいません。 コールツリー:(global) ./T_Err_Manually.sh:120 FuncA_func() ./T_Err_Manually.sh:105 --- デバッガ情報 --------------------------- ステップ実行 … Enter キーを押してください 変数の値を表示 … 変数名を入力 ---------------------------------------------------- FuncA_func() ./T_Err_Manually.sh:105 105: local self="$1" FuncA_func() ./T_Err_Manually.sh:106 106: local Prompt="$2" FuncA_func() ./T_Err_Manually.sh:107 107: local Opt="$3" Prompt $Prompt = 番号 > サンプル function FuncA_func(){ de local self="$1" local Prompt="$2" local Opt="$3" } FuncA_func "obj" "番号 >" de 画面の様子 105, 106 行目では Enter を押しています。107 行目では、Prompt と入力して Enter を押しています。 → T_Err_Manually.sh # [T_Step_func] テスト → T_Err_Manually.sh # [T_EchoStep_func] de ec ec <Expression> 現在の位置と、引数の内容を表示します。 引数に指定した式も表示します。 【引数】 Expression 式、' ' で囲むこと。 省略可 サンプル ec ec> FuncA_func() sample.sh(115) 表示例 標準エラー出力へ出力します。 表示例 ec> FuncA_func() sample.sh(115)ec> $VAR = "ABC" ec '$VAR' サンプル echo の代わりに Watch を使うと、echo による返り値を返す関数の中でも表示されます。 echo for debug es es <Name> 指定した変数の値をダンプします。 【引数】 Name 変数名。 先頭に $ を付けないこと サンプル local var="AB DE" es var "$var"="AB DE"00000000 41 42 20 20 44 45 |AB DE| 00000006 表示例 → T_Err.sh # T_DumpVar_func テスト 標準エラー出力へ出力します。 echo string for debug