SC2046: クォートされていないコマンド置換

$()コマンド置換をクォートしてワードスプリッティングのバグを回避。bashスクリプトでコマンド出力を適切にクォートする方法を解説します。

Quoting

詳細な説明

コマンド置換のクォーティング

$()やバッククォートによるコマンド置換は、通常の変数展開と同じワードスプリッティングとグロビングのルールが適用されます。クォートされていないコマンド置換は予期しない結果を生む可能性があります。

問題

# 悪い例: クォートされていないコマンド置換
current_dir=$(pwd)
cd $current_dir  # パスにスペースが含まれると壊れる

# 良い例: クォートされたコマンド置換
current_dir=$(pwd)
cd "$current_dir"

実世界の例

# 悪い例: スペースを含むファイル名で壊れる
for file in $(find . -name "*.log"); do
  echo "Processing $file"
done

# 良い例: find -exec または while read を使用
find . -name "*.log" -exec echo "Processing {}" \;

# またはプロセス置換とreadを使用
while IFS= read -r file; do
  echo "Processing $file"
done < <(find . -name "*.log")

ネストされたコマンド置換

$()がバッククォートより優れている点は、クリーンなネストです:

# $()でのクリーンなネスト
echo "Kernel: $(uname -r) on $(hostname)"

# バッククォートでは面倒(エスケープが必要)
echo "Kernel: \`uname -r\` on \`hostname\`"

クォートしない場合

コマンド出力を意図的に配列要素に分割する場合:

# 意図的: コマンド出力から配列を構築
files=($(ls *.txt))  # 注意: 特殊文字では問題あり

この場合でも、任意のファイル名を処理するにはwhile-readループの方が安全です。

ユースケース

コマンド出力をキャプチャして後続のコマンドで使用するビルドスクリプト、デプロイパイプライン、自動化ツール。find、ls、その他のコマンドから返されるファイルパスを処理するスクリプトで特に重要です。

試してみる — Shell Script Linter

フルツールを開く