変数とクオート
変数は name="値" の形で代入します。= の前後にスペースを入れない点が要注意です。値を取り出すときは $name または ${name} と書きます。文字列をダブルクオート "..." で囲むと中の変数は展開され、シングルクオート '...' で囲むと展開されずそのままの文字になります。空白や特殊文字を含む値は、意図しない分割を防ぐため "$name" のように常にダブルクオートで囲むのが安全です。
スクリプトの中で値を覚えておきたいとき、その値に名前を付けて入れておく入れ物が変数です。ファイル名・回数・ユーザからの入力など、後で何度も使う値を変数にしておくと、一か所を直すだけで全体に反映でき、読みやすさも保守のしやすさも上がります。bash の変数はとても手軽に使え、あらかじめ「これは数値」「これは文字列」と型を宣言する必要はありません。基本的にすべて文字列として扱われ、必要に応じて数値としても解釈される、というゆるやかな仕組みになっています。
代入は = の前後に空白を入れない
変数への代入は 名前=値 と書きます。ここで最も大切な注意点が、= の前後に空白を入れてはいけないということです。たとえば name="太郎" は正しい代入ですが、name = "太郎" のように空白を挟むと、bash は name をコマンド名だと誤解してしまい、name: コマンドが見つかりません といったエラーになります。これは初学者が非常によくつまずく落とし穴なので、「代入のイコールは前後をくっつけて書く」と体で覚えてください。値に空白を含めたいときは name="山田 太郎" のようにクオートで囲みます。複数の単語からなる値を裸で書くと、空白のところで切れて意図しない代入になってしまいます。
参照は $name または ${name}
代入した値を取り出す(参照する)ときは、変数名の前に $ を付けて $name と書きます。echo "$name さん、こんにちは" のように使うと、$name の部分が代入された値に置き換わって表示されます。変数名のすぐ後ろに文字を続けたいときは、波カッコ付きの ${name} を使います。たとえば ${name}san と書けば「name という変数の値に san を足したもの」と解釈されますが、波カッコ無しで $namesan と書くと、bash は namesan という別の変数名だと受け取ってしまい、空の値に化けてしまいます。「どこまでが変数名か」をはっきりさせたいときは ${...} を使う、と覚えておくと安全です。${name}_bak のようにアンダースコアを挟む場合も同様に波カッコが効きます。
ダブルクオートとシングルクオートの違い
文字列を囲むクオートには2種類あり、中の変数の扱い方が決定的に違います。ダブルクオート "..." は、中に書いた $変数 を値に置き換えて(変数展開して)から処理します。一方シングルクオート '...' は、中身をいっさい解釈せず、書いたままの文字として扱います。つまり $ すら特別な意味を失い、ただの文字になります。具体例で見ると違いがはっきりします。
$ name="太郎" としたうえで、echo "$name さん" は「太郎 さん」と表示しますが、echo '$name さん' は「$name さん」とそのまま表示します。「変数の中身を使いたいときはダブルクオート、記号や $ をそのまま文字として見せたいときはシングルクオート」という対比で理解すると整理できます。スクリプトの中で固定の文字列を出すだけならシングルクオート、値を埋め込みたいならダブルクオート、という使い分けが基本です。
なぜ変数は "$name" と囲むのが安全なのか
変数を参照するときは、できるだけ "$name" のようにダブルクオートで囲むのが安全な書き方です。理由は、値に空白や特殊文字が含まれていると、囲まずに裸で $name と書いた場合にシェルがその空白で値を複数の単語に分割してしまうからです。たとえば file="my report.txt" という変数を裸で rm $file と渡すと、シェルはこれを rm my report.txt と解釈し、my と report.txt という2つの別物を消そうとしてしまいます。"$file" と囲んでおけば、全体が1つの値として正しく扱われ、こうした事故を防げます。空になり得る変数を条件式などで裸のまま使うと式そのものが壊れることもあるため、「変数参照は基本的にダブルクオートで囲む」を初期設定の習慣にしておくと、後々のトラブルを大きく減らせます。
環境変数と export
変数には、そのシェルの中だけで通用する普通の変数と、そこから起動した子プロセス(別のコマンドやスクリプト)にも引き継がれる環境変数の区別があります。普通の変数を環境変数に格上げするには export を使い、export NAME=値 あるいは既存の変数に対して export NAME と書きます。代表的な環境変数には、コマンドの探索先を並べた PATH や、ホームディレクトリを指す HOME があります。たとえば自分の変数をスクリプトの中の別コマンドにも渡したいときは export しておく必要があり、export せずに定義した変数は呼び出した先のプロセスからは見えません。「このシェル限りで使うなら普通の変数、呼び出す先にも伝えたいなら export」と押さえておくと、設定が思ったとおりに伝わらないときの切り分けに役立ちます。
入力を受け取る read と、よくある失敗
利用者にキーボードから値を打ち込んでもらい、それを変数に受け取りたいときは read コマンドを使います。read name と書くと、その行で入力された文字が変数 name に入ります。質問文を一緒に出したいときは read -p "お名前は? " name のように -p で促し文を付けると、表示と入力を1行にまとめられます。echo と組み合わせて echo -n "続けますか? "(-n は改行しない指定)のように促してから read する書き方もよく見かけます。最後に、変数まわりでつまずきやすい点を整理しておきます。第一に、代入の = の前後に空白を入れてしまう誤り。第二に、参照のときに $ を付け忘れて変数名をそのまま文字として出してしまう誤り。第三に、空白を含む値を "$var" と囲まずに使って単語に分割されてしまう誤り。この3つが代表的なつまずきどころで、変数が思った値にならないときは、まずここを疑うと早く原因にたどり着けます。