sh での変数とワイルドカードの落とし穴

sh, bash, csh, zshのようなシェルでは

echo *.sh

などというように、ワイルドカードでファイル一覧を取得することができます。
シェルで使えるワイルドカードはアスタリスク(*)、クエスチョンマーク(?)の2種類で、それぞれ、0文字以上の任意の文字列、1文字の任意の文字(英数記号など)を表します。

ワイルドカード文字を、ワイルドカードとしてではなくただの * や ? という1つの文字として扱いたい場合は、次のようにクオート記号で囲むか、エスケープします。

echo "*.sh"
echo "What's up?"

# 結果
# *.sh
# What's up? 
# が表示される

ここに変数が関係してくると、少し厄介なことになります。

# 間違った例
foo="SELECT * FROM table"
echo $foo

この結果は、

SELECT * FROM table

と表示されるかと思いきや、アスタリスクがワイルドカードとして展開されてしまうため、

SELECT FROM table

とアスタリスクが消える、または、現在の作業フォルダのファイル一覧が展開されて、

SELECT bin boot home root var FROM table

のように意図しない結果になってしまいます。

これはかなり怖いことで、この動きにより、最悪致命的なセキュリティホールや、データ消失につながる場合があります。

これを回避するためには, ワイルドカード文字を含む変数の参照時は常にダブルクオート/シングルクオートの中で参照するというテクニックを使います。

# 正しい例
foo="SELECT * FROM table"
echo "$foo"

この結果は、期待通り

SELECT * FROM table

となります。

多段になってもこの動きは変わりません。

foo="SELECT * FROM table"
boo="$foo"
echo "$boo"
# foo, boo 両方の参照をダブルクォートでくくっていることに注意

この結果も、期待通り

SELECT * FROM table

となります。ここで

echo $boo

としていると、やはり変数展開されてしまうため、注意しましょう。
お約束として、変数を参照するときで、変数に記号が入る可能性がある場合は必ずダブルクオートで囲む癖をつけると、こういう間違いがなくなるので幸せになれます。

おまけ

一歩踏み込んだ例として、変数の遅延展開の例を紹介します。

export table=book # 1
# ↑これは全く利用されない
text='SELECT * FROM ${table}' # 2
# ↑シングルクオートなので、textに入る時点ではtableが展開されない。

export table=member # 3
# ↑これが利用される。子プロセスで参照できるよう export している
query=$(sh -c "echo \"$text\"") #4
# ↑ここでtableの中身が展開されます。* の展開を防ぐため $text をダブルクォートで囲んでいることに注意
echo "$query" # 5

この結果は、

SELECT * FROM member

と出力されます。
処理の流れは以下のとおりです。

1: table 変数に book をセット
2: text 変数に SELECT * FROM ${table} をセット(table変数の中身はここでは見ない)
3: table 変数の内容を member に書き換え
4:

sh -c "echo \"$text\""

を実行、shが立ち上がり、

echo "SELECT * FROM ${table}"

を実行する。(ここでtableが初めて展開される)
query 変数に、echo が出力した SELECT * FROM member をセットする。(ここでは変数代入だけで画面には出力されない)
5: query 変数の内容、SELECT * FROM member を表示する

あまりやらないですが、こういう複雑な処理でも期待通り動作するのは、適切にクオートしているおかげですね。