本文へスキップ
BecomeCoder

シェルスクリプトコース · 第7章 安全に書く · レッスン30

クォートの罠 ― スペース入りのファイル名

ブラウザで完結

導入

私の 資料.txt のような、スペースを含むファイル名。Windowsから持ち込まれたファイルには珍しくありません。クォートを忘れたスクリプトは、こういうファイルで静かに壊れます。

説明

変数を囲まないと、スペースの位置で別々の値に割れます

#!/bin/bash

file="my report.txt"
echo "内容" > "$file"

echo "=== 囲んだ場合 ==="
cat "$file"

echo "=== 囲まなかった場合 ==="
cat $file

囲まなかったほうは myreport.txt という2つのファイルを探しに行き、両方とも見つからずエラーになります。

flowchart TD
    A["file=\"my report.txt\""] --> B["cat $file(囲まない)"]
    A --> C["cat \"$file\"(囲む)"]
    B --> D["cat my report.txt<br/>→ 2つのファイルを探す → エラー"]
    C --> E["cat 'my report.txt'<br/>→ 正しく1つのファイル"]

覚えるルールは1つだけです。変数を使うときは、必ず " で囲む

# よくない
rm $file
cp $src $dst
for f in $(ls); do

# よい
rm "$file"
cp "$src" "$dst"
for f in *; do

最後の for f in $(ls)for f in * も重要です。ls の出力を使うと空白で割れますが、ファイル名展開(*)ならファイル名が壊れません。

例外はほぼありません。「囲むと動かない」場面(* を展開させたいときなど)以外は、機械的に囲んでしまって構いません。迷ったら囲む。それだけで、この種のバグはほぼ消えます。

$@ を使うときも同じで、"$@" と囲むのが正解です。囲まないと、スペースを含む引数が分割されてしまいます。

やってみよう

スクリプトを実行して、囲んだほうだけが成功することを確かめましょう。次にファイル名を file="report.txt"(スペースなし)に変えて実行すると、どちらも成功してしまいます。だからこそ、テストでは気づけず本番で壊れるのです。

演習

スペースを含むファイル名でも正しく読めるように、cat の引数を二重引用符で囲んで 内容 を表示してください。

ヒント1を見る

cat $filecat "$file" に直します。

ヒント2を見る

変数を使う場所はすべて "$file" の形にします。

実際に動かしてみよう

本文のサンプルや演習のコードは、コードブロック右上の「コピー」ボタンでコピーして、下のエディタに貼り付ければそのまま実行できます。

シェルスクリプト(シミュレート)

スクリプトを書くエディタと、それを動かす仮想シェルを読み込みます(本物のbashではなく、動きを再現した学習用の環境です)。
スクロールして表示された時点でも自動で読み込まれます。