CUIの世界で暮らす場合、シェルの使いこなしで作業効率は桁違いに変わる。シェルは一種類だけではないが、シェルスクリプトの観点からはsh系とcsh系に分けられる。sh系であるbash(zshでもOK?)をメインに使ってみることにする。純粋な /bin/sh を使うと、declareが使えず、exprとなるのが面倒。

*link [#j4378a5b]
-[[ステップ・バイ・ステップ・シェルスクリプト:http://www.atmarkit.co.jp/flinux/index/indexfiles/shellsindex.html]]
-[[bashで始めるシェルスクリプト基礎の基礎:http://www.atmarkit.co.jp/flinux/rensai/theory08/theory08a.html]]
-[[シェルスクリプトの基礎:http://warp.syns.net/10/]]

*実例 [#uc500115]
あえて全部別々に記述してみた。

**jpegファイルで、大文字の拡張子を小文字にする(JPG -> jpg) [#j48fd7c2]
 for filename in *.JPG
 do
   mv ${filename} ${filename%JPG}jpg
 done

**ダメ写真を削除したなどで、連番が歯抜けとなったjpegファイル群に番号を振りなおす [#k74bfee7]
 declare -i num
 num=1
 for filename in *.jpg
 do
   mv $filename `printf "image%03d.jpg\n" $num`
   num=$num+1
 done

**プレビュー用にリサイズしたものをImagemagikで作り、previewディレクトリに保存。 [#p1c3131f]
 for file in *.jpg
 do
   convert -geometry 160x120 $file ./preview/$file
 done

**プレビュー用画像をクリックしたら元の画像が出てくるようなHTMLファイルをつくる。 [#jf630723]
 declare -i number
 declare -i residue
 number=1
 echo "<body>"
 echo "<h1>put a title in EUC....</h1>"
 echo "<hr>"
 echo ""
 echo "<table cellpadding=\"20\">"
 
 for filename in *.jpg
 do
   residue=$number%3
   if [ $residue -eq 1 ]
   then
     echo "<tr>"
   fi
   echo "  <td align=\"center\">"
   echo "    <a href=\"$filename\"><img src=\"preview/$filename\" alt=\"\" width=160 height=120 boarder=0></a>"
   echo "    <br> " ## comment
   echo "  </td>"
   if [ $residue -eq 0 ]
   then
     echo "</tr>"
   fi
   number=$number+1
 done
 
 echo "</table>"
 echo "</body>"


*基本 [#l3ba1143]

スクリプトはコマンドラインから入力しても良いが、慣れないうちは別ファイルとした方が良い。最もお手軽には、コマンドを列挙したファイル hoge.sh を用意して
 $ bash hoge.sh
などとする。あるいはファイル冒頭に
 #!/bin/bash
などとシェルへのパスを書き、ファイル自身に実行属性を与えても良い。

**引数 [#lb551019]
 $1, $2, ... 
としてコマンドラインの引数を参照できる。他にも
 $0            # スクリプトの名前
 $#            # 引数の数
 $*            # 全ての引数
など。

**変数 [#x02caf3b]
変数は基本的に文字列として扱われる。数値としたいならdeclareで指定する。変数の中身を参照する場合は冒頭に$を付す。演算子の両脇に空白を含めてはならない!
 declare -i var1
 var1=10
 var2=20
 var1=$var1+1
 var2=$var2+1
 echo var1,var2,$var1,$var2
なるスクリプトの出力結果は
 var1,var2,11,20+1
となる。var2は文字列となっていて、算術演算が反映されていない。

**パターンマッチ [#e9f19fb8]
 *     任意の文字列
 ?     任意の一文字
 [...] カッコ内のいずれか

これを利用して、文字変数valをいじったものを返すことができる。
 ${val#pattern}    val前方から最短マッチ部分を除く
 ${val##pattern}   val前方から最長マッチ部分を除く
 ${val%pattern}    val後方から最短マッチ部分を除く
 ${val%%pattern}   val後方から最長マッチ部分を除く

よく使うのは以下の2種類
 mypath=/path/to/somewhere/foo.txt
 echo ${mypath%.*}   # 拡張子を除く。/path/to/somewhere/foo 
 echo ${mypath##/*/} # ファイル名のみを得る。foo.txt


**制御文 [#f7f41b1b]

***if文 [#v67feea7]
以下の様に記述。カッコ前後には空白を挿入すること。
 str=hoge # <-- hoge or fuga
 if [ $str = hoge ]
 then
   echo hoge!
 elif [ $str = fuga ]
 then
   echo fuga!
 else
   echo neither!
 fi
条件式に使う等号は、両端に空白を挿入せねばならないみたいだ。
|s1 = s2 | 文字列s1とs2が等しい |
|s1 != s2 | 文字列s1とs2が等しくない |
|v1 -eq v2 | 数値v1とv2が等しい |
|v1 -ne v2 | 数値v1とv2が等しくない |
| -e file | fileが存在する |


***for文 [#z406f63d]
このループは、リスト中の要素に対してコマンドを繰り返し実行して、対象が無くなったら勝手に終わるので、Cなどのfor文とは雰囲気が違う。しかし例えば「カレントディレクトリ中のjpegファイル全てに対し○○○を実行する」なんて処理は以下の様に非常に簡潔に書ける。
 for filename in *.jpg
 do
    ○○○ # <--変数filenameを使って何かをする
 done
○○○を'echo $filename'などとすると、jpegファイル名が列挙されるわけである。