初歩のシェルスクリプトで遊ぶ[端末の256色カラーセレクタ(2)]
シェルスクリプトの簡易起動ランチャー
シェルスクリプトを起動する、簡易ランチャーです。
カラーセレクタじゃないけれど、中身はだいぶ共通してます。
引数を与えずに、単純に実行するだけ、のスクリプトを、中身を簡単に確認してから選択実行、ができます。
外部ファイルでスクリプトを書き、サブシェル入り関数で1ファイルにまとめる
スクリプトはファイル1枚にまとめてますが、制作中は細かくファイルを分けてみました。
というか、これについてはシェル関数をサブシェルで書いてはいたんですが、終盤でファイルに分けました。分けてミスに気付いたりもした。
「# ====[shMerge]====」なんてついてるのが、その目印。雑ですけど、まぁ。
- 機能を、外部の独立したシェルスクリプトファイルで作り始めて、単体で動作テストを済ませる。
- そのまま外部ファイルで構わないなら、無理にファイルをまとめない。
- ファイルを統合するなら、シェル関数のサブシェルに入れる。サブシェルの必要が無くてもサブシェルにする。
- サブシェルでないシェル関数は、必要なときのみ使う。
ってルールにしてみました。
作業用に、PATHを通したディレクトリを作り、その中にシェルスクリプトをファイルをまとめます。ファイル名は、そのままシェル関数名に使える名前、つまり拡張子とかは無しで作る。スクリプトの中でも、そのファイル名で指定しておく。
pressKeySh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # キー入力を1回のみ受け取る stty_BAK=`stty -g` stty -echo raw dd count=1 2>/dev/null stty "${stty_BAK}" ) } # ====[shMerge</ins>]====
たとえばこういう、キー入力をする機能を外部の独立したシェルスクリプトで書いて作り、外部で単独で動作するようにします。でもってこれを、中身をそのまんま、サブシェルのシェル関数へコピーする。この作業はシェルスクリプトファイルで行って、定形作業にしてしまう。
サブシェルだから、独立したスクリプトファイルとは違って、トラブルもありますけど。
bashの機能を使う前提だと、トラブルは多くなるかもしんないです。変数を読み出し専用に設定してしまうと、サブシェルの中では変更できなくなったり、だとか。あと、配列も面倒かも。
「外部ファイルはスクリプトに統一する」って方針なので、たとえば、ドットコマンドで読み込むような、シェル関数の設定ファイルも、独立したシェルスクリプトにしてます。
このスクリプトだと、起動するスクリプトファイルのパスを書き込んだ「scriptListSh」など。登録するスクリプトのページを増やしても、メインのスクリプトで保持するシェル変数が増えずに済む。
#!/bin/sh #!/usr/bin/zsh #!/usr/bin/ksh #!/usr/bin/posh #!/bin/busybox sh #!/bin/bash #!/usr/bin/yash #!/bin/dash # ======================================================================================== # ■ シェルスクリプト起動ランチャー_方向キー操作、1ページの登録数無制限 v2.0 # https://twitter.com/beta_reverse_2 # ======================================================================================== # ▼ 必須の設定 # シェル変数「scriptPageN」に、登録して実行したいスクリプトファイルへのパスを書き込む。 # # ▼ 主要操作 # ・ カーソル移動 # ↑ カーソル↑,8,k # ↓ カーソル↓,2,j # → カーソル→,6,l # ← カーソル←,4,h # ・ スクリプト実行 # Enter,Ctrl+M # ・ このスクリプトを終了 # /,q,Ctrl+C # ・ ページ切り替え # 0,スペース,Tab # # ▼ 拡張操作 # ・ テキストファイル表示 ページスクロール下 # PageDown,shift+カーソル↓,3 # ・ テキストファイル表示 ページスクロール上 # PageUp, shift+カーソル↑,9 # ・ テキストファイル表示 ページスクロール リセット # カーソル移動するとリセットされる # ・ スクリプトに引数を与えて実行 # : # ・ スクリプトのパスを表示 # p # ・ スクリプトをテキストエディタで開く # w # # ▼ highlightコマンドでスクリプトソースを色分け # (1) sudo apt install highlight # (2) ユーザ設定_拡張で、該当部分のシェル変数をコメントアウト # # # # PATH="./lib:${PATH}" # ====[shMerge<disable>]==== #_shMergeDisabled # ************************************************************************************** # ユーザ設定_必須 scriptListSh() # ************************************************************************************** # # ====[shMerge]==== scriptListSh 20201022_191249 scriptListSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # 実行したいスクリプトや実行ファイルを、シェル変数に書き込む。 # 1コマンドを1行に。 # 絶対パス指定、相対パス指定、コマンド名で指定する。 # そのまま端末に打ち込んで実行するのと同様に実行される。 # ただし、「*.sh」などのパス展開は、実行時には使えない。(set -f 設定済み) # ディレクトリは、指定しても使えない。 # ディレクトリ内のファイルを指定するには、コマンド置換を使う。 # シェル変数名は「scriptPage0」「scriptPage1」「scriptPage2」……とする。 # 「0,1,3」とすると、「0,1」は使えるが、「3」は使えなくなる。 # 普通に代入する標準の方法 # scriptPageN='date # /home/user/dir/script.sh # ./script.sh' # ヒアドキュメントで代入 # scriptPageN=`cat - << ----PATH_LIST # date # cal # ----PATH_LIST` # 外部設定ファイルを作って読み込む # scriptPageN=`cat ./pathListFile.txt` # 指定ディレクトリの全てのファイル # scriptPageN=`ls /home/user/dir/*` # 指定ディレクトリの全てのスクリプトファイル # scriptPageN=`ls /home/user/dir/*.sh` # 終了をキーで選択してEnterで行う # 「exit」を入れる。 # 一時的にページ設定「scriptPageN」を無効にする # 変数名を変える。例えば「_scriptPageN」。 cd $(dirname `readlink -f "${0}"`) # ${1}:ページ番号 scriptPage0=`cat - << ----PATH_LIST /bin/ls sudo apt update sudo apt upgrade stty size exit echo "123 567" exec pwd ----PATH_LIST` scriptPage1=`cat - << ----PATH_LIST date pwd ls cal sl ----PATH_LIST` scriptPage2=`ls /home/user/shell/*` # 引数に指定された番号のシェル関数を標準出力する # 存在しない番号を指定されたとき、exit 1 eval 'XX=`printf "%s\n" "${scriptPage'${1}'}"`' if test -n "${XX}" ; then eval 'printf "%s\n" "${scriptPage'${1}'}"' exit 0 else exit 1 fi # [EOF] ) } # ====[shMerge</ins>]==== # ************************************************************************************** # ユーザ設定_拡張 # ************************************************************************************** # ファイルビューアの行数 viewLine=26 # コマンドビューアのカラムの横数 3から6程度を推奨 columnTD=4 # 空白「____」に指定するコマンド_エイリアス作成 alias "____=\date" # テキストファイルの色付けをするコマンドをひとつ選択 #viewFilter='' # 色をつけない viewFilter='sed' # sedでコメントのみ簡易着色 #viewFilter='highlight' # highlightコマンドで着色 # テキストエディタ_テキストファイルを開くときに使う userTextEditor='nano' # # ====[shMerge]==== helpMessageSh 20201022_191249 helpMessageSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # 簡易操作説明_不要ならprintfをコメントアウト printf "\033[7m%s\033[0m %s \033[7m%s\033[0m %s \033[7m%s\033[0m %s \033[7m%s\033[0m %s\n" '矢印' '選択' 'SPACE' '頁切替' 'Enter' '実行' 'q /' '終了' true ) } # ====[shMerge</ins>]==== # ====================================================================================== # シェル関数_外部へ分離可能 # ====================================================================================== # # ====[shMerge]==== pressKeySh 20201022_191249 pressKeySh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # キー入力を1回のみ受け取る stty_BAK=`stty -g` stty -echo raw dd count=1 2>/dev/null stty "${stty_BAK}" ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== pressKeyScanSh 20201022_191249 pressKeyScanSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh #!/usr/bin/zsh # キー入力を受け取り、入力キーを判定して、結果を文字で出力する # 文字出力は、シェル関数の名前に使える文字列のみで構成すること eval "`printf \ 'ESC="\033" ctrl_plus_C="\003" ENTER_KEY="\015" TAB______KEY="\011"'`" # 操作キー UP____KEY="${ESC}[A" DOWN__KEY="${ESC}[B" RIGHT_KEY="${ESC}[C" LEFT__KEY="${ESC}[D" SPACE_KEY=' ' INS_KEY="${ESC}[2~" PAGE_UP___KEY="${ESC}[5~" PAGE_DOWN_KEY="${ESC}[6~" SHIFT_UP___KEY="${ESC}[1;2A" SHIFT_DOWN_KEY="${ESC}[1;2B" stty_BAK=`stty -g` stty -echo raw while true ; do # キー入力を1回のみ受け取る case "`dd count=1 2>/dev/null`" in ( "${ENTER_KEY}" ) echo '_ENTER' break 1 ;; ( ':' ) echo '_COLON' break 1 ;; ( 0 | "${SPACE_KEY}" | "${TAB______KEY}" | "${INS_KEY}" ) echo '_TAB' break 1 ;; ( "${UP____KEY}" | 8 | k ) echo '_UP' break 1 ;; ( "${DOWN__KEY}" | 2 | j ) echo '_DOWN' break 1 ;; ( "${LEFT__KEY}" | 4 | h ) echo '_LEFT' break 1 ;; ( "${RIGHT_KEY}" | 6 | l ) echo '_RIGHT' break 1 ;; ( "${PAGE_DOWN_KEY}" | 3 | "${SHIFT_DOWN_KEY}" ) echo '_PAGE_DOWN' break 1 ;; ( "${PAGE_UP___KEY}" | 9 | "${SHIFT_UP___KEY}" ) echo '_PAGE_UP' break 1 ;; ( '/' | 'q' | "${ctrl_plus_C}" ) echo '_QUIT' break 1 ;; ( p ) echo 'p' break 1 ;; ( w ) echo 'w' break 1 ;; ( * ) true ;; esac done stty "${stty_BAK}" exit 0 ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== scriptSourceViewSh 20201022_191249 scriptSourceViewSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh #!/usr/bin/zsh #!/bin/bash scriptPath="${1}" # 表示するスクリプトのパス_正規化前の生データ viewFilter="${2}" # テキストファイルのハイライトコマンド if test "`file --mime --brief "${scriptPath}" | cut -d '/' -f 1`" = 'text' ; then # 単純にテキストファイルならば、ソース表示処理に進む_実行ファイルでなくとも true elif ! scriptPath=`which "${scriptPath}"` ; then # 実行ファイルではない exit 1 elif ! test -z "${scriptPath%%/*}" ; then # whichの終了コードは0だが、その結果が「/なんとか」ではないならば(zshのビルトイン) echo "${scriptPath}" exit 0 elif ! scriptPath=`readlink -f "${scriptPath}"` ; then # 実行ファイルだが実体が見つからない exit 1 elif ! test "`file --mime --brief "${scriptPath}" | cut -d '/' -f 1`" = 'text' ; then # 実行ファイルで実体はあるが、テキストファイルではない ls -alF --color=always "${scriptPath}" 2>&1 file "${scriptPath}" | tr ',' '\n' exit 2 else # 実行テキストファイルと見なし、ソース表示処理に進む true fi # スクリプトソース表示 # あとで1行単位で切るので、着色も1行単位で完結すること case "${viewFilter}" in ( highlight ) highlight --out-format=xterm256 --input="${scriptPath}" ;; ( sed ) termCols=`tput cols` tensionPole=`printf "%${termCols}s" ' '` # 空白行を詰めて、コメントのみ簡易着色 sed -n -e " # 空行を削除 /^$/{ d } # コメント部分に着色 /#/{ s/#[^#]*$/\o033[38;5;229m\o033[48;5;235m&/1 } { # 全行を空白含めて塗りつぶす # 塗りつぶし棒を横幅いっぱいに半角空白で敷き詰め、右端でリセット # キャリッジリターン「\o015」で戻し、 # 行の内容「&」を色付きで書き込み、 # 端末右端へカーソル移動(しないとoverWriteで消去される) s/.*/\o033[38;5;231m\o033[48;5;237m${tensionPole}\o033[0m\o015\ \o033[38;5;231m\o033[48;5;237m&\o033[0m\o033[${termCols}G/ } p " "${scriptPath}" ;; ( * ) tr -s '\n' 0< "${scriptPath}" ;; esac exit 0 ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== scriptSourceLineFix3sh 20201022_191249 scriptSourceLineFix3sh(){ ( # ====[shMerge<ins>]==== #!/bin/sh #set -x # ソース表示を指定行数に固定する #Origin="${1:-1}" # テキストファイル表示時の表示開始行 #viewLine="${2:-15}" # 出力行数 awk -v Origin="${1:-1}" -v viewLine="${2:-15}" ' BEGIN{ # } ( NR >= Origin && viewLine > 0 ){ # 行番号をつける # printf( "%03d %s\n" ,NR ,$0 ) # 標準表示 printf( "%s\n" ,$0 ) viewLine = viewLine - 1 } END{ while ( viewLine > 0 ) { print( "_" ) viewLine = viewLine - 1 } }' 2>&1 exit 0 ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== scriptPageCoverSedSh 20201022_191249 scriptPageCoverSedSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # 登録スクリプト変数の行数を、${columnTD}の整数倍に増やす # 空白行を「____」に入れ替える # 行の内容は、そのまま実行できるPATHに保つこと # ${1} ${columnTD} カラム列数 columnTD="${1:-4}" # 空行をエイリアス文字列に置換 # 余剰行を追加 # テーブル列を「7」の文字数でカウント。Hに「777...」を蓄積する sed --posix -n -e ' /^$/{ # 空行ならば「____」を入れる #s/.*/__空行__/ s/.*/____/ # ↓つづく } { # テーブル列をカウント x s/$/7/ /7\{'${columnTD}'\}/ { # もしP「7777」ならば、消去 s/7*// } # p x } # 元空行の「____」も含めて全行を出力 p # ========最終行処理============ ${ # 余剰行を追加_最終行は出力済 # P「____」H「7???」 # PH交換 # P「7???」 H「____」 # ラベルloopStart # もしPが「」ならば、元データでちょうど割り切れた # 何も出力せずに終了 # もしPが「7777」ならば、補完ループの役目が終わった # 何も出力せずに終了 # 「7が半端な数だけある(列の出力が途中)」ならば # P-H交換、Hに「7」連続を退避 # echo ____ # PH交換 # P末尾に「7」を追加 # ジャンプloopStart #s/.*/__補完__/ s/.*/____/ x : loopStart /^$/ { q } /7\{'${columnTD}'\}/ { q } x p x s/$/7/ b loopStart }' ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== scriptLabelViewSedSh 20201022_191249 scriptLabelViewSedSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # 実行するスクリプトのラベルを表示する # stdin:scriptPageN_列数の整数倍に行数を調整済み # ${1}:選択中の行番号 # ${2}:横に何列で表示するのか tdWidth='17' tdSelect=${1:-1} tdCount=${2:-4} # ホールドスペースに、以下の形式で行内容を貯める # # 7777 (列数を文字数で) # aaa.sh (列データ1) # bbb.sh # ccc.sh # ddd.sh # # 入力1行ごとに、「7」を一つずつ増やす # 行内容は改行追記してゆく # # 先頭が/7\{指定数\}/になったとき、 # /7\{指定数\}/を削除 # 改行を削除 # 行内容全出力 # H,Pともに削除 sed --posix -n -e '#n # ディレクトリ部分を削除してファイル名のみ残す s:.*/:: # 行の末尾に半角空白を追加(手抜き) s/$/ / # 先頭から指定幅の文字数を切り出す s/^\(.\{'${tdWidth}'\}\).*/ \1/ # フォーカス行に色をつける '${tdSelect}'s/.*/\o033[97;44m&\o033[0m/ # === ここまでで出力してからprコマンドに繋ぐことも可能 ======= # Hを呼び出し、Pの入力内容をHへ退避 x # カウンタ文字「7」をHの先頭(行)に追加 s/^/7/ # 入力内容をHから改行追加 G # もしカウンタ文字が列数だけ貯まっているならば /^7\{'${tdCount}'\}/{ # カウンタ文字を削除 s/^7\{'${tdCount}'\}// # 改行を削除 s/\n//g # テーブル1行分をまとめて出力 p # Hの内容(この行の読み込み分)を削除 x s/.*// # Pを全削除して次行へ x d } # カウンタ文字と行内容蓄積分をHへ退避 x ' # 表形式表示を行う #| pr --omit-header --columns=${2} --across --separator=' ' --indent=1 --output-tabs=' '1 ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== messageColorSh 20201022_191249 messageColorSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # メッセージ表示に色をつける sed -e "s/.*/\o033[30;102m&\o033[0m/" ) } # ====[shMerge</ins>]==== # # ====[shMerge]==== overWriteSh 20201022_191249 overWriteSh(){ ( # ====[shMerge<ins>]==== #!/bin/sh # ちらつき防止 一行書き換え #printf '\033[1;1H''\033[?25l' #sed --posix -e 's/ / /g' -e "s/$/\o033[0K/1" #printf '\033[?0J''\033[?25h' sed --posix -n -e ' # [エスケープシーケンス][1行目] 改行 1 { s/^/\o033[1;1H\o033[?25l&/ } # [最終行][エスケープシーケンス] 改行 $ { s/$/\o033[?0J\o033[?25h/ } # 行の水平タブを半角空白に入れ替え s/ / /g # 行の行末に、行の右側消去を仕込む s/$/\o033[0K/1 # エスケープシーケンスも含めて行を一気に出力 p ' ) } # ====[shMerge</ins>]==== # ====================================================================================== # シェル関数_外部へ分離できない_キー実行アクション # ====================================================================================== keyActionFunc__ENTER(){ # スクリプトを実行する set -f eval "${scriptPath}" set +f echo 'キーを押すと続行します。' | messageColorSh 1>&2 pressKeySh 1>/dev/null } keyActionFunc__COLON(){ # スクリプトに引数を与えて実行する echo '実行するスクリプト:'"${scriptPath}" 1>&2 echo '引数を入力' 1>&2 read YY set -f eval "${scriptPath} ${YY}" set +f echo 'キーを押すと続行します。' | messageColorSh 1>&2 pressKeySh 1>/dev/null } keyActionFunc__TAB(){ # ページを切り替える scriptPageNo=$(( ${scriptPageNo} + 1 )) scriptPageNo=$(( ( ${scriptPageNo} + ${scriptPageMax} + 1 ) % ( ${scriptPageMax} + 1 ) )) scriptPathX='0' scriptPathY='0' } keyActionFunc__UP(){ # ↑ scriptPathY=$(( ( ${scriptPathY} - 1 + ${columnTR} ) % ${columnTR} )) viewLineOrigin='1' } keyActionFunc__DOWN(){ # ↓ scriptPathY=$(( ( ${scriptPathY} + 1 ) % ${columnTR} )) viewLineOrigin='1' } keyActionFunc__RIGHT(){ # → scriptPathX=$(( ( ${scriptPathX} + 1 ) % ${columnTD} )) viewLineOrigin='1' } keyActionFunc__LEFT(){ # ← scriptPathX=$(( ( ${scriptPathX} - 1 + ${columnTD} ) % ${columnTD} )) viewLineOrigin='1' } keyActionFunc__PAGE_DOWN(){ # ソース表示 下スクロール viewLineOrigin=$(( ${viewLineOrigin} + ( ${viewLine} / 2 ) )) } keyActionFunc__PAGE_UP(){ # ソース表示 上スクロール viewLineOrigin=$(( ${viewLineOrigin} - ( ${viewLine} / 2 ) )) if test "${viewLineOrigin}" -le 0 ; then viewLineOrigin='1' fi } keyActionFunc__QUIT(){ # 終了する echo '終了します。' | messageColorSh 1>&2 exit 0 } keyActionFunc_p(){ # whichコマンドでパスを表示する echo "${PS1}"'which' which "${scriptPath}" echo "${PS1}"'readlink -f' readlink -f $(which "${scriptPath}") echo 'キーを押すと続行します。' | messageColorSh 1>&2 pressKeySh 1>/dev/null } keyActionFunc_w(){ # テキストエディタで開く eval "${userTextEditor}" '"${scriptPath}"' } # ====================================================================================== # シェル関数_外部へ分離できない # ====================================================================================== debug1Sub(){ ( printf "%s\n" 'pwd:'`pwd` printf "%s\n" 'scriptPageMax:'"${scriptPageMax}" NN=0 while test ${NN} -le ${scriptPageMax} ; do eval 'echo "---- scriptPage'${NN}'"' scriptListSh ${NN} NN=$(( ${NN} + 1 )) done exit 0 ) } debug2Sub(){ ( printf '\033[0J' echo 'pwd:'`pwd` for XX in scriptPageNo scriptPathX scriptPathY columnTD columnTR \ viewLineOrigin scriptPathNo ; do eval 'echo ${XX}:"${'${XX}'}"' done echo '${PRESS_KEY_SCAN}:'`printf "%s" "${PRESS_KEY_SCAN}" | od -tc -An -v` ) } # ====================================================================================== # スタートアップ処理 # ====================================================================================== cd "$( dirname "`readlink -f "${0}"`" )" # ページ番号_初期設定 scriptPageNo='0' # スクリプト選択座標_初期設定 scriptPathX='0' scriptPathY='0' # スクリプトファイルビューアの表示開始行番号_初期設定 viewLineOrigin='1' #ESC=`printf "\033"` # 「scriptPageMax」scriptPageNの最大値を取得する NN='-1' while true ; do NN=$(( ${NN} + 1 )) if scriptListSh "${NN}" 1>/dev/null ; then scriptPageMax="${NN}" else break fi done #debug1Sub ; exit # 【debug】 # ====================================================================================== # メインループ # ====================================================================================== while true ; do # ---- ページ情報 読み込み処理 ------------------------ # 処理するスクリプトのリストを読み込む scriptPageCurrent="`scriptListSh ${scriptPageNo} | scriptPageCoverSedSh "${columnTD}"`" # ラベル表示のカラム行数を取得 columnTR=$(( `printf "%s\n" "${scriptPageCurrent}" | wc -l` / ${columnTD} )) # ---- ページ情報から1行を抜き出す -------------------- # 現在選択中のスクリプト番号を取得 scriptPathNo=$(( ${scriptPathX} + ${scriptPathY} * ${columnTD} + 1 )) # スクリプト番号でパス文字列を抜き出す scriptPath="`printf "%s\n" "${scriptPageCurrent}" | sed -n ${scriptPathNo}p `" { # 画面表示ブロック helpMessageSh # テキストかバイナリかを判定して、 # テキストなら冒頭のみ表示、バイナリならfileを実行 scriptSourceViewSh "${scriptPath}" ${viewFilter} | scriptSourceLineFix3sh ${viewLineOrigin} ${viewLine} # 実行するコマンドのパスを表示 # 文字色と背景色を決めて 横幅一杯にマーカーラインを引き キャリッジリターン\015で戻し # パス文字列を上書き 右端へ移動してから 色指定リセット 改行 NN=`tput cols` printf '\033[30;106m%'${NN}'s\015 %s\033['${NN}G'\033[0m\n' ' ' "${scriptPath}" # スクリプト選択表を表示する echo "${scriptPageCurrent}" | scriptLabelViewSedSh ${scriptPathNo} ${columnTD} } | overWriteSh 1>&2 #debug2Sub # 【debug】 # キー入力を受け取る PRESS_KEY_SCAN=`pressKeyScanSh` # ---- 入力キーアクション実行 ---------------------------------------------------- keyActionFunc_${PRESS_KEY_SCAN} done exit 255 # EOF
スクリプト一括合成スクリプトの例
要するに、テキストファイルを、しおりで指定した場所に差し込むスクリプトです。
必要なパスを絶対パスで書き込んで使います。
やっつけ作業だし、再帰をしちゃってたりで、これで大丈夫か自信無いですけど。
ファイルの中身は削除しないようにしてます。不要部分はコメントアウトするだけです。分離も簡単。
#!/bin/sh : << '#__________comment' スクリプト合成 合成位置タグ タブ区切りでこの後に外部スクリプトのファイル名を書き込む コードの行末に置かずにタグだけで1行にすること。 # ====[shMerge]==== scriptNameSh コード無効化タグ 行末に置くと、置いた行がコメントアウトされる # ====[shMerge<disable>]==== コード有効化タグ 行末に置くと、置いた行のコメントアウトが削除される # ====[shMerge<enable>]==== #__________comment # 主スクリプトのフルパス mainScript='/home/' # ライブラリの入ったディレクトリ libDir='/home/' # 新規作成シェルスクリプト名 newScriptName='launchB20_merge' # 出力先ディレクトリ newScriptDir='/home/' true ${mainScript:?} true ${libDir:?} true ${newScriptName:?} true ${newScriptDir:?} cd $( dirname `readlink -f "${0}"` ) mainSub(){ ( # 再帰呼び出し処理するメイン処理 # マージ機能を有効にするならtrue margeTagScanFlag="${1:-false}" # 作成日時 dateTime=`date +"%Y%m%d_%H%M%S"` LINE_COUNT='0' while IFS='' read -r LINE ; do # -----------------------------------------------------↓ LINE_COUNT=$(( ${LINE_COUNT} + 1 )) #sleep 0.01 case "${LINE}" in # ---------------------------------------------------------------------- ( '# ====[shMerge]===='* ) # 合成位置タグ # ---------------------------------------------------------------------- case "${margeTagScanFlag}" in ( true ) # 外部スクリプト合成 # シェル関数の名前を取得 functionName=`echo "${LINE}" | cut -d ' ' -f 2` echo "# ${LINE} ${dateTime}" echo "${functionName}"'(){ ( # ====[shMerge<ins>]====' #echo "${functionName}"'(){ # ====[shMerge<ins>]====' # 外部スクリプト読み込み、存在判定 # いったんコマンド置換で変数に入れることで、 # 末尾の連続改行を縮める。 if funcData=`cat "${libDir}/${functionName}"` ; then # サブシェルで再帰呼び出し # 1行処理繰り返しループが終わるまで出力を続ける # 外部スクリプト合成指示タグは無視する printf '%s\n' "${funcData}" | mainSub 'false' else printf '%s' "${LINE_COUNT}:" 1>&2 exit 2 fi # サブシェルからの出力が終わったら、シェル関数の括弧を閉じる echo ') } # ====[shMerge</ins>]====' #echo ' } # ====[shMerge</ins>]====' ;; ( * ) printf '%s\n' "# ${LINE}" ;; esac ;; # ---------------------------------------------------------------------- ( *'# ====[shMerge<disable>]====' ) # コメントアウトタグ # ---------------------------------------------------------------------- # 先頭にコメントアウトを付加して出力 printf '%s\n' '# '"${LINE}"' #_shMergeDisabled' ;; # ---------------------------------------------------------------------- ( *'# ====[shMerge<enable>]====' ) # コード有効化タグ # ---------------------------------------------------------------------- # 先頭の「#」と、続く空白を削除して出力 printf '%s\n' "${LINE}" | sed -e 's/^#//1' -e 's/^ *//1' -e 's/$/ #_shMergeEnabled/1' ;; # ---------------------------------------------------------------------- ( * ) # 通常のコードならば # ---------------------------------------------------------------------- # タグではなく、コード有効化も無効化も指示されていないならば、そのまま出力 printf '%s\n' "${LINE}" ;; esac done # ------------------------------------------------------------------------------↑ ) } dateTime=`date +"%Y%m%d_%H%M%S"` if ! cat "${mainScript}" ; then exit 2 elif ! test -f "${newScriptDir}/${newScriptName}${dateTime}.sh" ; then exit 2 else true fi | mainSub true 2>> ./shMergeErr.log | tee "${newScriptDir}/${newScriptName}${dateTime}.sh" chmod 554 "${newScriptDir}/${newScriptName}${dateTime}.sh"