web-dev-qa-db-ja.com

スペースとワイルドカードを含むパスを含む文字列でのBashスクリプトエラー

Bashスクリプトの基本を理解できません。ここに私がこれまで持っているものがあります:

#!/bin/bash
FILES="/home/john/my directory/*.txt"

for f in "${FILES}"
do
  echo "${f}"
done

私がやりたいのは、forループ内のすべての.txtファイルを一覧表示することだけです。ただし、my directory内のスペースと*.txt内のアスタリスクがうまく機能していません。変数名に中括弧を付けても付けなくても、二重引用符を付けても付けなくても使用しようとしましたが、すべての.txtファイルを印刷できません。

これは非常に基本的なことですが、疲れていて直感が持てないので、まだ苦労しています。

何が悪いのですか?

ファイルにスペースまたはアスタリスクがない場合、上記のスクリプトを正常に適用できました...機能させるには、二重引用符と中括弧を使用するか使用しないかを実験する必要がありました。しかし、スペースとアスタリスクの両方があると、すべてが混乱します。

25
John

引用符の中では、*はファイルのリストに展開されません。このようなワイルドカードを正常に使用するには、引用符の外にある必要があります。

ワイルドカードが展開されたとしても、式"${FILES}"は、ファイルのリストではなく、単一の文字列になります。

機能する1つのアプローチは次のとおりです。

#!/bin/bash
DIR="/home/john/my directory/"
for f in "$DIR"/*.txt
do
  echo "${f}"
done

上記では、スペースやその他の難しい文字を含むファイル名は正しく処理されます。

より高度なアプローチでは、bash配列を使用できます。

#!/bin/bash
FILES=("/home/john/my directory/"*.txt)
for f in "${FILES[@]}"
do
  echo "${f}"
done

この場合、FILESはファイル名の配列です。定義を囲む括弧は、それを配列にします。 *は引用符の外にあります。コンストラクト"${FILES[@]}"は特殊なケースです。各文字列がファイル名の1つである文字列のリストに展開されます。スペースやその他の難しい文字を含むファイル名は正しく処理されます。

33
John1024

John1024で示されているように配列を使用することはより理にかなっていますが、ここでは、split + glob演算子を使用することもできます(スカラー変数は引用符で囲まずに残します)。

その演算子のglob部分だけが必要なため、split部分を無効にする必要があります。

#! /bin/sh -
# that also works in any sh, so you don't even need to have or use bash

file_pattern="/home/john/my directory/*.txt"
# all uppercase variables should be reserved for environment variables

IFS='' # disable splitting

for f in $file_pattern # here we're not quoting the variable so
                       # we're invoking the split+glob operator.
do
  printf '%s\n' "$f" # avoid the non-reliable, non-portable "echo"
done
7

できることは、ワイルドカード文字のみを引用符の外に残すことです。
何かのようなもの:
in "スペースのあるファイル" * "。txt"
行う
処理
完了
ワイルドカード自体がスペースに展開される場合、ls -lを使用してファイルのリストを生成し、bash読み取りを使用して各ファイルを取得するなど、ファイルごとのアプローチを使用する必要はありません。

1
Marcelo Pacheco