シェルスクリプトのデバッグは typeset または declare を使うと良いかも
はじめに
つい最近知った便利なデバッグ方法
(長年シェルスクリプトを書いているのに知らなかった。これが常識だったら恥ずかしい…)
シェルスクリプトのデバッグでは echo で変数の中身を見るという原始的な方法をよく使うかと思います。
いわゆる プリントデバッグ というやつですね。
もう少し詳しいデバッグが必要な場合は、 set -x と set +x でデバッグしたい部分を囲むという方法もあります。
今回は プリントデバッグ で使う echo の代わりに typeset or declare を使うと良いというお話です。
プリントデバッグは typeset or declare を使おう
typeset or declare は変数宣言などでよく使うコマンドですが、変数の中身を見るのにも使えます。
echo と比べて何が良いのかというと、変数の中身はもちろん変数名や変数の型も表示してくれ、配列の場合にも良い感じに表示してくれるのです。
わざわざ echo "hoge=$hoge" とか echo "hoge=${hoge[@]}" などとしなくても良い。
typeset or declare はビルトインコマンドなので使用するシェルによって若干の違いがあります。
今回は私がよく使う sh/bash と zsh について触れます。
(sh と bash は今回の場合は同じなので区別しません)
sh/bash
bash では、typeset は obsolete となっています。
declare が使用できる環境では declare を使うと良いでしょう。
(typeset の方が馴染み深いかもしれませんが)
## 文字列 $ str='This is a pen.' $ declare -p str declare -- str="This is a pen." ## 配列 $ array=('This is' a pen.) $ declare -p array declare -a array='([0]="This is" [1]="a" [2]="pen.")'
変数名や型が表示されていて、配列も見やすく整形されているのが分かりますね。
zsh
zsh では typeset と declare は全く同じです。
## 文字列 $ str='This is a pen.' $ typeset str str='This is a pen.' $ typeset -p str typeset str='This is a pen.' ## 配列 $ array=('This is' a pen.) $ typeset array array=('This is' a pen.) $ typeset -p array typeset -a array array=('This is' a pen.) # 配列内のデータが多い場合などは以下のようにして改行区切りで表示させると良いでしょう # j フラグについては後述 $ echo "${(j:\n:)array[@]}" This is a pen ## 連想配列 $ typeset -A associative $ associative=(key1 val1 key2 val2) $ typeset associative associative=(key1 val1 key2 val2 ) $ typeset -p associative typeset -A associative associative=(key1 val1 key2 val2 )
zsh だと 型情報なしですが -p オプションを付けなくても表示されるので、タイプ数が少なくてすみますね。
(連想配列の表示はもう少し良い感じに表示してくれても良いのではと思わなくもない。
というか、配列もbashの方が分かりやすいような気も…)
それと、気づいた人もいるかもしれませんが、typeset or declare の出力をそのままファイルに保存して、あとでそのファイルを source することでデータを復元することができます。(sh/bash, zsh ともに可能)
(というか元々こういう用途なのかもしれない)
シリアアライズのようなものですね。
色々と使い道がありそうです。
おまけ - zsh の変数展開のデバッグについて
ここからはちょっと濃い話になります。
zsh を使わない方でも、zsh の凄さが分かるので、処理結果だけでも見てみて下さい。
zsh には、フラグを使った変数展開という機能があります。
とても便利な機能なのですが、デバッグするのが非常に大変です。
見慣れてない人だと、場合によっては発狂しかねません。
そこで今回紹介するのが、変数展開の q フラグです。
実際に例を見てみましょう。
例えば、以下の様な複数行のカンマ(,)区切りのデータがあるとします。
$ data='1,2,3 a,b,c höge,fuga,piyo' $ echo "$data" 1,2,3 a,b,c höge,fuga,piyo
このデータを改行で分割したい。
→ f フラグを使うと改行で分割できます。
$ echo "${(f)data}" 1,2,3 a,b,c höge,fuga,piyo
これで改行で分割できました。出来てるはずです。
…でもこれじゃ改行は消えてるけど、どこで分割されてるのか分からないですよね。
では、qフラグを使ってみます。
$ echo "${(qqqf)data}" "1,2,3" "a,b,c" "höge,fuga,piyo"
分割された部分がダブルコーテーションで囲われて分かりやすくなっていますね。
これが q フラグです。
さらにカンマでも分割したい。
→ j フラグ(join)を使うと、j:,: のように書いて、要素をカンマ(,)で結合できます (任意の文字でも可)
→ s フラグ(split)は s:,: のように書いて、要素をカンマ(,)で分割できます (任意の文字でも可)
$ echo "${(j:,:s:,:qqq)${(f)data}}" "1" "2" "3" "a" "b" "c" "höge" "fuga" "piyo"
いかがでしょう。
非常に分かりやすいですよね。
最後に応用編です。
for文で1個ずつ処理をしたい場合。
$ for val in "${(j:,:s:,:)${(f)data}}"; do echo "Hello $val san."; done Hello 1 san. Hello 2 san. Hello 3 san. Hello a san. Hello b san. Hello c san. Hello höge san. Hello fuga san. Hello piyo san.
行ごとに & カラムごとに別の処理をしたい。
i=0 for row in "${(f)data}"; do (( i++ )) echo -n "$i: " uppers=() for val in "${(s:,:)row}"; do uppers+=("${(U)val}") done echo "${(j:, :)uppers}" done # 結果 (行番号を入れつつ、大文字に変換してみました) # 1: 1, 2, 3 # 2: A, B, C # 3: HÖGE, FUGA, PIYO
※ 上記の例では qqq を使用しましたが、q の数によって使用されるコーテーションが変わります。
q が1つの場合はバッククォート(`)、2つの場合はシングルコーテーション(')、3つの場合はダブルコーテーション()"、4つの場合は $' ' でクォートされます。
このように zsh はとても高機能です。
外部コマンドを使わずとも、シェルの機能だけでこれだけのことが出来てしまいます。
今回紹介したものも、ほんの一部にすぎません。
みなさんも対話シェル (Interactive shell) としてだけでなく、シェルスクリプトでも zsh を使ってみてはいかがでしょうか。
zsh についてもっと詳しく知りたい方は、以下の書籍 "zshの本" が超おすすめです。
zsh でシェルスクリプトを書かない方でも、zsh ユーザなら絶対に持っておきたい1冊です。
zsh の代名詞である補完関数についても詳しく書かれています。
zshの本 (エッセンシャルソフトウェアガイドブック) 広瀬 雄二 技術評論社 2009-06-17 売り上げランキング : 231694
|
メモリ使用率を表示する (Mac/Linux+出力カスタマイズ+精度指定)
以前、以下の記事にてメモリ使用率を表示するスクリプトを紹介しましたが、あれからもう少し汎用的なスクリプトに改良したので紹介します。
前回はMacでしか動作せず、メモリ使用率(%)を表示するのみでしたが、今回からMacに加えてLinuxでも動作するようにしました。
また、出力も使用率(%)だけでなく、使用量(GB)、空き率(%)、空き量(GB)、総量(GB)なども表示できるようにしました。
さらに、出力形式のカスタマイズや精度(小数点第何位まで求めるか)なども指定できるようにしました。
インストール方法
下記Githubからcloneまたはダウンロードし、PATHの通った所に置きます。
$ git clone https://github.com/yonchu/used-mem.git $ mv used-mem/used-mem ~/bin
gitが使用できない場合は
ここから直接ダウンロードして下さい。
使用方法
Usage: used-mem [出力フォーマット] フォーマット用文字列: #f : 空きメモリ率(%). #u : 使用メモリ率(%). #F : 空きメモリ量(GB). #U : 使用メモリ量(GB). #T : 総メモリ(GB).
出力フォーマットが未指定の場合は、メモリ使用率(%)とメモリ使用量(GB)/総メモリ(GB)が表示されます。
$ used-mem 77.8%(6.2G/8.0G)
フォーマットを指定した場合、
$ used-mem '#f%(#FG/#TG)' 22%(2G/8G) $ used-men '#.1u%' 77.8% $ used-mem 'Free: #.2f % (#.3F GB) | Used: #.2u % (#.3U GB) | Total: #.3T GB' Free: 38.32 % (3.065 GB) | Used: 61.68 % (4.933 GB) | Total: 7.998 GB
精度の指定は、#.Nu のように指定し、小数点以下N桁まで表示します。
C言語やシェルスクリプトのprintf文と同じような指定方法です。
精度指定の最大値はデフォルトでは小数点以下3桁までです。
4桁以上指定したい場合は以下のように実行します。
$ MAX_SCALE=5 used-mem '#.5u%' 97.73180%
tmuxで使用
tmuxで使用するとこんな感じになります。
- $ used-mem 'Ⓜ #.1u%'
- $ used-mem 'Ⓜ #.1u%(#.1UG/#.1TG)'
- $ used-mem 'Free: #.2f % (#.3F GB) | Used: #.2u % (#.3U GB)'
蛇足
精度指定の最大値がデフォルト3桁とやや小さめになっているのは、検証不足のためです (-_-;)
32bitの環境で、bcコマンドが使用できない場合に、どこまで最大精度を大きくできるか分からなかったんですよね。
(手元に32bitの環境がなくて… / 今時の環境はbcコマンドぐらい入ってると思うけど…)
bash/zshで16色(ANSI カラーコード)と256色のカラーパレットを表示
なんだか定期的に色を変えたくなる…
プロンプトの色やtmux/screenのステータスバーの色変更時のお共に。
yonchu/shell-color-pallet · GitHub
256色カラーパレットを表示
こんな感じに表示されます。